/**
 * 字符串模板替换 hello {name} -> hello Lily
 * @param {String} str 模板字符串 hello {name}
 * @param {Object} o 数据 { name: Lily }
 * @returns {String} 替换后的字符串 hello Lily
 */
function substitute(str,o,regexp){
  return str.replace(regexp || /\\?\{([^{}]+)\}/g, function (match, name) {
    return (o[name] === undefined) ? match : o[name];
  });
}

/**
  * 辅助函数, 将一个input元素包装下
  *
  * @param formItem
  * @param field
  * @returns {XML}
  */
const colWrapper = (formItem, field) => {
    field.formItem = formItem;
    if (field.showType == 'textarea') {
        return substitute(
            '<Col key={{key}} sm={16}>\n\
                <FormItem key={{key}} label={{title}} labelCol={{ span: 5 }} wrapperCol={{ span: 17 }}>\n\
                    {formItem}\n\
                </FormItem>\n\
            </Col>', field
        );
    }
    return substitute(
        '<Col key={{key}} sm={8}>\n\
            <FormItem key={{key}} label={{title}} labelCol={{ span: 10 }} wrapperCol={{ span: 14 }}>\n\
                {formItem}\n\
            </FormItem>\n\
        </Col>', field
    );
}

/**
 * 将schema中的一列转换为下拉框
 *
 * @param field
 */
const transformSelect = (field,getFieldProps,canEdit) => {
    // TODO: 这里是否要做schema校验
    const options = [];
     
    field.options.forEach((option) => {
        options.push(substitute('<Option key={{key}} value={{key}}>{value}</Option>', option));
    });
    field.options = options;
    field.canEdit = canEdit;
    var type= field.dataType =='int' ?'number':'string';
    return colWrapper(substitute(
        '<Select placeholder={{placeholder}} size="default" disabled={!{canEdit}} {...getFieldProps({key}, { rules: [{ required : {required}, message: "必填" ,type:{type}}] }) }>\n\
            {options}\n\
        </Select>'
    ), field);
}

/**
 * 将schema中的一列转换为单选框
 *
 * @param field
 */
const transformRadio = (field,getFieldProps,canEdit) => {
    const options = [];
     
    field.options.forEach((option) => {
        options.push(substitute('<Radio key={{key}} value={{key}}>{value}</Radio>'), option);
    });
    field.options = options;
    field.canEdit = canEdit;
    return colWrapper(
        '<RadioGroup disabled={!{canEdit}} {...getFieldProps({key}, { \n\
            initialValue: {initialValue} ,\n\
            rules: [{ required : {required}, message: "必填" }] }) }>\n\
            {options}\n\
        </RadioGroup>'
    , field);
}

/**
 * 将schema中的一列转换为checkbox
 *
 * @param field
 */
const transformCheckbox = (field,getFieldProps,canEdit) => {
    const options = [];
     
    field.options.forEach((option) => {
        options.push({ label: option.value, value: option.key });
    });

    return colWrapper((
        <CheckboxGroup options={options} disabled={!canEdit} {...getFieldProps(field.key, { rules: [{ required : field.required, message: '必填' ,type:'array'}] }) } />
    ), field);
}

/**
 * 转换为下拉多选框
 *
 * @param field
 * @returns {XML}
 */
const transformMultiSelect = (field,getFieldProps,canEdit) => {
    const options = [];
     
    field.options.forEach((option) => {
        options.push(<Option key={option.key} value={option.key}>{option.value}</Option>);
    });

    return colWrapper((
        <Select multiple placeholder={field.placeholder} size="default" disabled={!canEdit} {...getFieldProps(field.key, { rules: [{ required : field.required, message: '必填' ,type:'array' }] }) }>
            {options}
        </Select>
    ), field);
}
const transformTextarea = (field, getFieldProps, canEdit) => { 
        return colWrapper((<Input  type="textarea" rows={4} 
            disabled={!canEdit} {...getFieldProps(field.key, 
                { 
                    rules: [
                        { required : field.required, message: '必填'},
                        {
                            validator(rule, value, callback) {
                                if (field.validType&&validator[field.validType]) {
                                    validator[field.validType](rule, value, callback)
                                }
                                else callback();
                            }
                        }
                    ]
                }) 
            }
             />), field);
}

/**
 * 将schema中的一列转换为普通输入框
 *
 * @param field
 * @returns {XML}
 */
const transformNormal = (field,getFieldProps,canEdit) => {
    
    switch (field.dataType) {
        case 'int': 
            return colWrapper((
                <InputNumber size="default" disabled={!canEdit} {...getFieldProps(field.key, 
                    { rules: [
                        { 
                            required : field.required, 
                            message: '必填',
                            type:'number',
                        }, 
                    ] }) } min={field.min} max={field.max}/>
            ), field);
        case 'float': 
            return colWrapper((
                <InputNumber step={0.01} size="default" disabled={!canEdit} {...getFieldProps(field.key,
                    { rules: [
                        { 
                            required : field.required, 
                            message: '必填',
                            type:'number',
                        },                      

                    ] }) } min={field.min} max={field.max}/>
            ), field);
        case 'date': ;
        case 'datetime':
            var format = field.dataType == 'datetime' ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";

            return colWrapper((
                <DatePicker showTime={field.dataType == 'datetime'}  format={format}
                    placeholder={field.placeholder } disabled={!canEdit} {...getFieldProps(field.key, { rules: [{ required : field.required, message: '必填',type:'date' }] }) } />
            ), field);
        default:  // 默认就是普通的输入框
             
            return colWrapper((
                <Input placeholder={field.placeholder} size="default" disabled={!canEdit} {...getFieldProps(field.key, 
                    { 
                        rules: [
                        { 
                            required : field.required, 
                            message: '必填', 
                        },
                        {
                            pattern: field.pattern||null,
                            message: field.message||null,
                        }, 
                        {
                            validator(rule, value, callback) {
                                if (field.validType&&validator[field.validType]) {
                                    validator[field.validType](rule, value, callback)
                                }
                                else callback();
                            }
                        }
                    ] 
                }) } />
            
            ), field);
    }
}

/**
 * 也是个辅助函数, 由于是范围查询, 输入的formItem是两个, 一个用于begin一个用于end
 *
 * @param beginFormItem
 * @param endFormItem
 * @param field
 * @returns {XML}
 */
const betweenColWrapper = (beginFormItem, endFormItem, field,getFieldProps) => {
    // 布局真是个麻烦事
    // col内部又用了一个row做布局
    // 
    return (
        <Col key={`${field.key}Begin`} sm={8}>
            <Row>
                <Col span={16}>
                    <FormItem key={`${field.key}Begin`} label={field.title} labelCol={{ span: 15 }} wrapperCol={{ span: 9 }} >
                        {beginFormItem}
                    </FormItem>
                </Col>
                <Col span={7} offset={1}>
                    <FormItem key={`${field.key}End`} labelCol={{ span: 10 }} wrapperCol={{ span: 14 }}>
                        {endFormItem}
                    </FormItem>
                </Col>
            </Row>
        </Col>
    );
}

/**
 * between类型比较特殊, 普通元素每个宽度是8, int和float的between元素宽度也是8, 但datetime类型的between元素宽度是16
 * 否则排版出来不能对齐, 太丑了, 逼死强迫症
 * 而且普通的transform函数返回是一个object, 而这个函数返回是一个array, 就是因为datetime的between要占用两列
 *
 * @param field
 */
const transformBetween = (field,getFieldProps,canEdit) => {
    const cols = [];
    let beginFormItem;
    let endFormItem;
    

    switch (field.dataType) {
        case 'int':
        
            beginFormItem = (<InputNumber size="default" min={field.min} max={field.max}
                placeholder={field.placeholderBegin || '最小值'} disabled={!canEdit} {...getFieldProps(`${field.key}Begin`, { rules: [{ required : field.required, message: '必填',type:'number' }] }) } />);
            endFormItem = (<InputNumber size="default" min={field.min} max={field.max}
                placeholder={field.placeholderEnd || '最大值'} disabled={!canEdit} {...getFieldProps(`${field.key}End`, { rules: [{ required : field.required, message: '必填' ,type:'number'}] }) } />);
            cols.push(betweenColWrapper(beginFormItem, endFormItem, field));
            break;
        case 'float':
            
            beginFormItem = (<InputNumber step={0.01} size="default" min={field.min} max={field.max}
                placeholder={field.placeholderBegin || '最小值'} disabled={!canEdit} {...getFieldProps(`${field.key}Begin`, { rules: [{ required : field.required, message: '必填' ,type:'number'}] }) } />);
            endFormItem = (<InputNumber step={0.01} size="default"
                placeholder={field.placeholderEnd || '最大值'} disabled={!canEdit} {...getFieldProps(`${field.key}End`, { rules: [{ required : field.required, message: '必填',type:'number' }] }) } />);
            cols.push(betweenColWrapper(beginFormItem, endFormItem, field));
            break;
        case 'date': ; 
        case 'datetime':
            var format = field.dataType == 'datetime' ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";

            cols.push(
                <Col key={`${field.key}Begin`} sm={8}>
                    <FormItem key={`${field.key}Begin`} label={field.title} labelCol={{ span: 10 }} wrapperCol={{ span: 14 }}>
                        <DatePicker showTime={field.dataType == 'datetime'}  format={format}
                            placeholder={field.placeholderBegin || '开始日期'} disabled={!canEdit} {...getFieldProps(`${field.key}Begin`, { rules: [{ required : field.required, message: '必填' ,type:'date'}] }) } />
                    </FormItem>
                </Col>
            );
            cols.push(<Col key={`${field.key}End`} sm={8}>
                <FormItem key={`${field.key}End`} labelCol={{ span: 10 }} wrapperCol={{ span: 14 }}>
                    <DatePicker showTime={field.dataType == 'datetime'}  format={format}
                        placeholder={field.placeholderEnd || '结束日期'} disabled={!canEdit} {...getFieldProps(`${field.key}End`, { rules: [{ required : field.required, message: '必填' ,type:'date'}] }) } />
                </FormItem>
            </Col>);
            break;
        default:
            // 理论上来说不会出现这种情况
            console.error('unknown dataType: %s', field.dataType);
    }
    return cols;
}
 

const renderForm=(schema,getFieldProps,canEdit)=>{
    const rows = [];
    let cols = [];
    let hiddenCols = [];
    // 参见antd的布局, 每行被分为24个格子
    // 普通的字段每个占用8格, between类型的字段每个占用16格
    let spaceLeft = 24;
    schema.forEach((field) => {
        if(field.hidden){
            hiddenCols.push( <Input key={field.key} disabled={!canEdit} {...getFieldProps(field.key) } type='hidden' />);
        }
        else {
            // 当前列需要占用几个格子? 普通的都是8, 只有datetime between是16
            let spaceNeed = 8;
            if (field.showType === 'between' && (field.dataType === 'datetime'||field.dataType === 'date')) {
                spaceNeed = 16;
            }
            if (field.showType === 'textarea' ) {
                spaceNeed = 24;
            }
            // 如果当前行空间不足, 就换行
            if (spaceLeft < spaceNeed) {
                rows.push(<Row key={rows.length} gutter={16}>{cols}</Row>);
                cols = [];  // 不知array有没有clear之类的方法
                spaceLeft = 24;  // 剩余空间重置
            }

            // 开始push各种FormItem
            switch (field.showType) {
                case 'select':
                    cols.push(transformSelect(field,getFieldProps,canEdit));
                    break;
                case 'radio':
                    cols.push(transformRadio(field,getFieldProps,canEdit));
                    break;
                case 'checkbox':
                    cols.push(transformCheckbox(field,getFieldProps,canEdit));
                    break;
                case 'multiSelect':
                    cols.push(transformMultiSelect(field,getFieldProps,canEdit));
                    break;
                case 'textarea':
                    cols.push(transformTextarea(field, getFieldProps, canEdit));
                    break;
                case 'between':
                    for (const col of transformBetween(field,getFieldProps,canEdit)) {  // between类型比较特殊, 返回的是一个数组
                        cols.push(col)
                    }
                    break;
                default:
                    cols.push(transformNormal(field,getFieldProps,canEdit));
            }

            spaceLeft -= spaceNeed;
        }
        
    });

    // 别忘了最后一行
    if (cols.length > 0) {
        rows.push(<Row key={rows.length} gutter={16}>{cols}{hiddenCols}</Row>);
    }
    return rows
}
module.exports = {
    renderForm
}