import { Grid, GridItem, GridItemWrapper, GridSourceItem } from "./grid";
import { Dropdown, DropdownOptions } from "../dropdown";

/** 列类型枚举 */
declare enum GridColumnType {
    /** 通用列 */
    Common = 0,
    /** 单行文本框列 */
    Input = 1,
    /** 下拉选择列 */
    Dropdown = 2,
    /** 复选框列 */
    Checkbox = 3,
    /** 图标列 */
    Icon = 4,
    /** 多行文本列 */
    Text = 5,
    /** 日期选择列 */
    Date = 6
}

/** 列定义接口 */
export interface GridColumnDefinition {
    /** 列关键字，默认以该关键字从行数据中提取单元格值，行数据的关键字属性值里包含 DisplayValue 则优先显示此值 */
    key?: string;
    /** 列的类型，可以为 {@linkcode GridColumn} 的子类，或者内置类型 {@linkcode GridColumnType} */
    type?: GridColumnType | typeof GridColumn;
    /** 列标题文本 */
    caption?: string;
    /** 列标题的元素样式 */
    captionStyle?: { [key: string]: string };
    /** 大于 0 则设置为该宽度，否则根据列内容自动调整列宽 */
    width?: number;
    /** 列对齐方式 */
    align?: "left" | "center" | "right";
    /**
     * 列是否可用（可编辑），允许以下类型<br/><br/>
     * `boolean` 则直接使用该值<br/><br/>
     * `string` 则以该值为关键字从行数据中取值作为判断条件<br/><br/>
     * `(item: GridItem) => boolean` 则调用该函数（上下文为列定义对象），以返回值作为判断条件<br/><br/>
     */
    enabled?: boolean | string | ((item: GridItem) => boolean);
    /**
     * 单元格取值采用该方法返回的值
     * @param item 行数据对象
     * @param editing 是否处于编辑状态
     * @param body Grid 控件的 `&lt;tbody&gt;` 部分
     */
    filter?: (item: GridItem, editing: boolean, body?: HTMLElement) => any;
    /** 单元格以该值填充内容，忽略filter与关键字属性 */
    text?: string;
    /** 列是否可见 */
    visible?: boolean;
    /** 列是否允许调整宽度 */
    resizable?: boolean;
    /** 列是否允许排序 */
    sortable?: boolean;
    /** 列是否允许重排顺序 */
    orderable?: boolean;
    /** 列为复选框类型时是否在列头增加全选复选框 */
    allcheck?: boolean;
    /** 单元格css样式对象（仅在重建行元素时读取） */
    css?: { [key: string]: string };
    /** 根据返回值填充单元格样式（填充行列数据时读取） */
    styleFilter?: (item: GridItem) => { [key: string]: string };
    /** 根据返回值设置单元格背景色 */
    bgFilter?: (item: GridItem) => string;
    /** 给单元格元素附加事件（事件函数上下文为数据行对象） */
    events?: { [event: string]: Function };
    /** 根据返回值设置单元格元素的附加属性，允许直接设置对象也支持函数返回对象 */
    attrs?: { [key: string]: string } | ((item: GridItem) => { [key: string]: string });
    /** 是否允许进行列头过滤 */
    allowFilter?: boolean;
    /** 自定义列过滤器的数据源（函数上下文为Grid） */
    filterSource?: Array<GridItem> | ((col: GridColumnDefinition) => Array<GridItem>);
    /** 自定义列排序函数 */
    sortFilter?: (a: GridItem, b: GridItem) => -1 | 0 | 1;
    /** 列为下拉列表类型时以该值设置下拉框的参数 */
    dropOptions?: DropdownOptions;
    /** 列为下拉列表类型时以该值设置下拉列表数据源，支持函数返回，也支持返回异步对象 */
    source?: Array<GridSourceItem> | ((item: GridItem) => Array<GridSourceItem> | Promise<Array<GridSourceItem>>);
    /** 下拉列表数据源是否缓存结果（即行数据未发生变化时仅从source属性获取一次值） */
    sourceCache?: boolean;
    /** 列为图标类型时以该值设置图标样式（函数上下文为列定义对象），默认值 `fa-light` */
    iconType?: "fa-light" | "fa-regular" | "fa-solid";
    /** 列为图标类型时以该值作为单元格元素的额外样式类型（函数上下文为列定义对象） */
    iconClassName?: string | ((item: GridItem) => string);
    /** 列为日期类型时以该值作为最小可选日期值 */
    dateMin?: string;
    /** 列为日期类型时以该值作为最大可选日期值 */
    dateMax?: string;
    /** 列为日期类型时自定义日期转字符串函数 */
    dateValueFormatter?: string | ((date: Date) => string);
    /** 以返回值额外设置单元格的tooltip（函数上下文为列定义对象） */
    tooltip?: string | ((item: GridItem) => string);

    /**
     * 列头复选框改变时触发
     * @param this 上下文为 Grid 对象
     * @param col 列定义对象
     * @param flag 是否选中
     * @eventProperty
     */
    onAllChecked?: (this: Grid, col: GridColumnDefinition, flag: boolean) => void;
    /**
     * 单元格发生变化时触发
     * @param this 上下文为 Grid 对象
     * @param item 数据行对象
     * @param value 修改后的值
     * @param oldValue 修改前的值
     * @param e 列修改事件传递过来的任意对象
     * @eventProperty
     */
    onChanged?: (this: Grid, item: GridItem, value: boolean | string | number, oldValue: boolean | string | number, e?: any) => void;
    /**
     * 文本单元格在输入完成时触发的事件
     * @param this 上下文为 Grid 对象
     * @param item 数据行对象
     * @param value 修改后的文本框值
     * @eventProperty
     */
    onInputEnded?: (this: Grid, item: GridItem, value: string) => void;
    /**
     * 列过滤点击OK时触发的事件
     * @param this 上下文为 Grid 对象
     * @param col 列定义对象
     * @param selected 选中的过滤项
     * @eventProperty
     */
    onFilterOk?: (this: Grid, col: GridColumnDefinition, selected: Array<GridItem>) => void;
    /**
     * 列过滤后触发的事件
     * @param this 上下文为 Grid 对象
     * @param col 列定义对象
     * @eventProperty
     */
    onFiltered?: (this: Grid, col: GridColumnDefinition) => void;
    /**
     * 列为下拉框类型时在下拉列表展开时触发的事件
     * @param this 上下文为列定义对象
     * @param item 数据行对象
     * @param drop 下拉框对象
     * @eventProperty
     */
    onDropExpanded?: (this: GridColumnDefinition, item: GridItem, drop: Dropdown) => void;
    /**
     * 列为下拉框类型时在下拉列表关闭时触发的事件
     * @param this 上下文为列定义对象
     * @param item 数据行对象
     * @param drop 下拉框对象
     * @eventProperty
     */
    onDropCollapsed?: (this: GridColumnDefinition, item: GridItem, drop: Dropdown) => void;
}

/** 列定义基类 */
export class GridColumn {
    /** @ignore */
    constructor();
    /**
     * 标记该类型是否支持列头批量操作
     */
    static get headerEditing(): boolean;
    /**
     * 创建显示单元格时调用的方法
     * @param col 列定义对象
     * @returns 返回创建的单元格元素
     * @virtual
     */
    static create(col: GridColumnDefinition): HTMLElement;
    /**
     * 创建编辑单元格时调用的方法<br/><br/>
     * 元素修改后设置行包装对象的 `__editing` 后，支持在离开编辑状态时及时触发 {@linkcode leaveEdit} 方法<br/>
     * 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。
     * @param trigger 编辑事件回调函数，e 参数会传递给 {@linkcode getValue} 方法
     * @param col 列定义对象
     * @param container 父容器元素
     * @param vals 行包装对象，其 `values` 属性为行数据对象
     * @returns 返回创建的编辑状态的单元格元素
     * @virtual
     */
    static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
    /**
     * 创建列头时调用的方法
     * @param col 列定义对象
     * @returns 返回创建的列头元素
     * @virtual
     */
    static createCaption?(col: GridColumnDefinition): HTMLElement;
    /**
     * 设置单元格值时调用的方法
     * @param element 单元格元素
     * @param val 待设置的单元格值
     * @param vals 行包装对象
     * @param col 列定义对象
     * @param grid {@linkcode Grid} 对象
     * @virtual
     */
    static setValue(element: HTMLElement, val: string | boolean | number, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
    /**
     * 获取编辑状态单元格值时调用的方法
     * @param e 由 {@linkcode createEdit} 方法中 `trigger` 函数传递来的对象
     * @param col 列定义对象
     * @returns 返回单元格的值
     * @virtual
     */
    static getValue(e: any, col: GridColumnDefinition): string | boolean | number;
    /**
     * 设置单元格样式时调用的方法
     * @param element 单元格元素
     * @param style 样式对象
     * @virtual
     */
    static setStyle(element: HTMLElement, style: { [key: string]: string }): void;
    /**
     * 设置单元格可用性时调用的方法
     * @param element 单元格元素
     * @param enabled 启用值，为false时代表禁用
     * @virtual
     */
    static setEnabled(element: HTMLElement, enabled?: boolean): void;
    /**
     * 单元格离开编辑元素时触发，需要由行包装对象的 `__editing` 来确定是否触发。
     * @param element 单元格元素
     * @param container 父容器元素
     * @virtual
     */
    static leaveEdit?(element: HTMLElement, container: HTMLElement): void;
}

/** 单行文本列 */
export class GridInputColumn extends GridColumn {
    /**
     * 设置该类型是否支持触发 {@linkcode GridColumnDefinition.onInputEnded} 方法<br/>
     * 该属性返回 `true` 后，在任意事件中修改行包装对象的 `__editing` 值，则会在行列元素变动时及时触发 {@linkcode GridColumnDefinition.onInputEnded} 方法，避免例如文本框还未触发 `onchange` 事件就被移除元素而导致的问题<br/>
     * 更多例子参考代码中 {@linkcode GridInputColumn} 的实现
     */
    static get editing(): boolean;
    /**
     * @inheritdoc GridColumn.createEdit
     * @override
     */
    static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
    /**
     * @inheritdoc GridColumn.setValue
     * @override
     */
    static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
    /**
     * @inheritdoc GridColumn.getValue
     * @override
     */
    static getValue(e: any): string;
    /**
     * @inheritdoc GridColumn.setEnabled
     * @override
     */
    static setEnabled(element: HTMLElement, enabled?: boolean): void;
}

/** 多行文本列 */
export class GridTextColumn extends GridInputColumn {
    /**
     * @inheritdoc GridInputColumn.createEdit
     * @override
     */
    static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
    /**
     * @inheritdoc GridInputColumn.setValue
     * @override
     */
    static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
}

/** 下拉选择列 */
export class GridDropdownColumn extends GridColumn {
    /**
     * @inheritdoc GridColumn.createEdit
     * @override
     */
    static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
    /**
     * @inheritdoc GridColumn.setValue
     * @override
     */
    static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition): void;
    /**
     * @inheritdoc GridColumn.getValue
     * @override
     */
    static getValue(e: any, col: GridColumnDefinition): string;
    /**
     * @inheritdoc GridColumn.setEnabled
     * @override
     */
    static setEnabled(element: HTMLElement, enabled?: boolean): void;
    /**
     * @inheritdoc GridColumn.leaveEdit
     * @override
     */
    static leaveEdit?(element: HTMLElement, container: HTMLElement): void;
}

/** 复选框列 */
export class GridCheckboxColumn extends GridColumn {
    /**
     * @inheritdoc GridColumn.createEdit
     * @override
     */
    static createEdit(trigger: (e: any) => void): HTMLElement;
    /**
     * @inheritdoc GridColumn.setValue
     * @override
     */
    static setValue(element: HTMLElement, val: boolean): void;
    /**
     * @inheritdoc GridColumn.getValue
     * @override
     */
    static getValue(e: any): boolean;
    /**
     * @inheritdoc GridColumn.setEnabled
     * @override
     */
    static setEnabled(element: HTMLElement, enabled?: boolean): void;
}

/** 单选框列 */
export class GridRadioboxColumn extends GridCheckboxColumn {
    /**
     * @inheritdoc GridCheckboxColumn.createEdit
     * @override
     */
    static createEdit(trigger: (e: any) => void): HTMLElement;
}

/** 图标列 */
export class GridIconColumn extends GridColumn {
    /**
     * @inheritdoc GridColumn.create
     * @override
     */
    static create(): HTMLElement;
    /**
     * @inheritdoc GridColumn.setValue
     * @override
     */
    static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition): void;
    /**
     * @inheritdoc GridColumn.setEnabled
     * @override
     */
    static setEnabled(element: HTMLElement, enabled?: boolean): void;
}

/** 日期选择列 */
export class GridDateColumn extends GridColumn {
    /**
     * @inheritdoc GridColumn.createEdit
     * @override
     */
    static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
    /**
     * 设置单元格值时调用的方法<br/><br/>
     * 支持以下几种数据类型<br/><br/>
     * `"2024-01-26"`<br/>
     * `"1/26/2024"`<br/>
     * `"638418240000000000"`<br/>
     * `new Date('2024-01-26')`<br/>
     * @param element 单元格元素
     * @param val 待设置的单元格值
     * @override
     */
    static setValue(element: HTMLElement, val: string | number): void;
    /**
     * @inheritdoc GridColumn.getValue
     * @override
     */
    static getValue(e: any): string | number;
    /**
     * @inheritdoc GridColumn.setEnabled
     * @override
     */
    static setEnabled(element: HTMLElement, enabled?: boolean): void;
    /**
     * 格式化日期对象为 M/d/yyyy 格式的字符串
     * @param date 日期对象
     * @returns 返回格式化后的字符串
     */
    static formatDate(date: Date): string;
}