/**
 * Created by rburson on 3/21/16.
 */

import * as React from 'react'
import {
    CvState,
    CvProps,
    CvBaseMixin,
    CvProp,
    CvDetailsPaneCallback,
    CvDataAnno,
    CvDataAnnoStyle,
    CvEvent,
    CvEventType,
    CvMessage,
    CvMessageType
} from './../catreact'
import {
    Try,
    Prop,
    PropDef,
    DetailsContext,
    CellValueDef,
    AttributeCellValueDef,
    LabelCellValueDef,
    ForcedLineCellValueDef,
    SubstitutionCellValueDef,
    TabCellValueDef,
    Log,
    PropFormatter,
    CodeRef,
    ObjectRef,
    StringUtil,
    ObjUtil,
    TimeValue
} from 'catavolt-sdk'

import DateTimeField from 'react-bootstrap-datetimepicker'

export interface CvCellValueDefState extends CvState {
}

export interface CvCellValueDefProps extends CvProps {
    cellValueDef:CellValueDef;
    detailsContext?:DetailsContext;
    detailsCallback?:CvDetailsPaneCallback;
    wrapperElem:any;
    wrapperElemProps?:any;
}

export var CvCellValueDefMixin = {

    cellValueDef: function (nextProps) {
        return (nextProps && nextProps.cellValueDef) || this.props.cellValueDef;
    },

    detailsCallback: function (nextProps) {
        return (nextProps && nextProps.detailsCallback) || this.props.detailsCallback;
    },

    detailsContext: function (nextProps) {
        return (nextProps && nextProps.detailsContext) || this.props.detailsContext || this.firstInScope(DetailsContext);
    },

    getChildContext: function () {
        const ctx = this.getDefaultChildContext();
        ctx.cvContext.scopeCtx.scopeObj = this.cellValueDef();
        return ctx;
    }

};


/*
 ***************************************************
 * Render an CellValueDef
 ***************************************************
 */
export var CvCellValueDef = React.createClass<CvCellValueDefProps, CvCellValueDefState>({

    mixins: [CvBaseMixin, CvCellValueDefMixin],

    render: function () {

        if (this.props.renderer) {
            return this.props.renderer(this.getChildContext().cvContext);
        } else {

            const detailsContext:DetailsContext = this.detailsContext();
            const cellValueDef:CellValueDef = this.cellValueDef();

            let component = null;

            if (cellValueDef) {

                if (cellValueDef instanceof AttributeCellValueDef) {

                    const prop = detailsContext.entityRec.propAtName((cellValueDef as AttributeCellValueDef).propertyName);
                    const styleInfo:CvDataAnnoStyle = (CvDataAnno as any).generateStyleInfo(prop);

                    component = <CvDataAnno dataAnnoStyle={styleInfo} paneContext={detailsContext}
                                            wrapperElem={this.props.wrapperElem}
                                            wrapperElemProps={this.props.wrapperElemProps}>
                        <CvAttributeCellValueDef detailsContext={detailsContext}
                                                 detailsCallback={this.detailsCallback()}
                                                 cellValueDef={this.cellValueDef()}/>
                    </CvDataAnno>

                    return component;


                } else if (cellValueDef instanceof LabelCellValueDef) {
                    component = <span className="cv-label-cell-def">{(cellValueDef as LabelCellValueDef).value}</span>
                } else if (cellValueDef instanceof ForcedLineCellValueDef) {
                    component = <span className="cv-forced-line-cell-def"/>
                } else if (cellValueDef instanceof TabCellValueDef) {
                    component = <span className="cv-tab-cell-def">{' '}</span>
                } else if (cellValueDef instanceof SubstitutionCellValueDef) {
                    component =
                        <span className="cv-sub-cell-def">{(cellValueDef as SubstitutionCellValueDef).value}</span>
                } else {
                    component = <span/>
                }
            }

            const props = ObjUtil.addAllProps(this.props.wrapperElemProps, {});
            return React.createElement(this.props.wrapperElem, props, component);

        }
    }

});

/*
 ***************************************************
 * Render an AttributeCellValueDef
 ***************************************************
 */
export interface CvAttributeCellValueDefState extends CvCellValueDefState {
    availableValues?:Array<any>;
}

export interface CvAttributeCellValueDefProps extends CvProps {
    cellValueDef:CellValueDef;
    detailsContext?:DetailsContext;
    detailsCallback?:CvDetailsPaneCallback;
    overrideText?:string;
}

const cv_no_selection_value:string = 'cv_no_selection_value';

export var CvAttributeCellValueDef = React.createClass<CvAttributeCellValueDefProps, CvCellValueDefState>({

    mixins: [CvBaseMixin, CvCellValueDefMixin],

    componentWillMount: function () {
        this.refresh();
    },

    componentWillReceiveProps: function (nextProps) {
        this.refresh(nextProps);
    },

    getDefaultProps: function () {
        return {cellValueDef: null, detailsContext: null, detailsCallback: null, overrideText: null}
    },

    getInitialState: function () {
        return {availableValues: null}
    },


    render: function () {

        if (this.props.renderer) {
            return this.props.renderer(this.getChildContext().cvContext);
        } else {
            const detailsContext = this.detailsContext();
            const cellValueDef:AttributeCellValueDef = this.cellValueDef();

            let component = null;

            if (cellValueDef && cellValueDef.propertyName) {
                if (!detailsContext.isReadModeFor(cellValueDef.propertyName) && !this.props.overrideText) {
                    const prop:Prop = detailsContext.buffer.propAtName(cellValueDef.propertyName);
                    const propDef:PropDef = detailsContext.entityRecDef.propDefAtName(cellValueDef.propertyName);
                    if (prop) {

                        /*** Combobox ***/
                        if (cellValueDef.isComboBoxEntryMethod) {
                            if (this.state.availableValues && this.state.availableValues.length > 0) {
                                component =
                                    (<select className="form-control cv-cell-def-select-box"
                                             id={cellValueDef.propertyName}
                                             onChange={this._propChange.bind(this, prop.name)}
                                             defaultValue={PropFormatter.toString(prop.value, propDef)}>
                                        <option value={cv_no_selection_value}>No Selection</option>
                                        {this.state.availableValues.map((value)=> {
                                            return <option value={PropFormatter.toString(value, propDef)}>
                                                {this._displayAsOption(value, propDef)}
                                            </option>
                                        })}
                                    </select>);
                            }
                            /*** Dropdown ***/
                        } else if (cellValueDef.isDropDownEntryMethod) {
                            if (this.state.availableValues && this.state.availableValues.length > 0) {
                                component =
                                    (<select className="form-control cv-cell-def-select-box"
                                             id={cellValueDef.propertyName}
                                             onChange={this._propChange.bind(this, prop.name)}
                                             defaultValue={PropFormatter.toString(prop.value, propDef)}>
                                        <option value={cv_no_selection_value}>No Selection</option>
                                        {this.state.availableValues.map((value)=> {
                                            return <option value={PropFormatter.toString(value, propDef)}>
                                                {this._displayAsOption(value, propDef)}
                                            </option>
                                        })}
                                    </select>);
                            }
                        } else {
                            /*** Binary ***/
                            if (detailsContext.isBinary(cellValueDef)) {
                                component =
                                    <CvProp propName={cellValueDef.propertyName} entityRec={detailsContext.entityRec}/>
                            } else {
                                if (prop) {
                                    /*** Boolean ***/
                                    if (propDef.isBooleanType) {
                                        component =<input type="checkbox" defaultChecked={prop.value}
                                                          onChange={this._checkboxChange.bind(this, prop.name)}/>
                                        /*** Datetime ***/
                                    } else if (propDef.isDateTimeType) {
                                        component =<DateTimeField
                                            dateTime={prop.value ? prop.value.getTime() : (new Date()).getTime()}
                                            onChange={this._dateChange.bind(this, prop.name, propDef)}
                                            inputFormat="MM/DD/YYYY h:mm A"/>
                                        /*** Date ***/
                                    } else if (propDef.isDateType) {
                                        component =<DateTimeField
                                            dateTime={prop.value ? prop.value.getTime() : (new Date().getTime())}
                                            onChange={this._dateChange.bind(this, prop.name, propDef)}
                                            inputFormat="MM/DD/YYYY" mode="date"/>
                                        /*** Time ***/
                                    } else if (propDef.isTimeType) {
                                        if (prop.value) {
                                            component =<DateTimeField
                                                dateTime={prop.value ? (prop.value as TimeValue).toDateValue().getTime() : (new Date().getTime())}
                                                onChange={this._dateChange.bind(this, prop.name, propDef)}
                                                inputFormat="h:mm A" mode="time"/>
                                        } else {
                                            component =
                                                <DateTimeField
                                                    onChange={this._dateChange.bind(this, prop.name, propDef)}
                                                    inputFormat="h:mm A" mode="time"
                                                    defaultText="Choose a time value..."/>
                                        }
                                        /*** Text ***/
                                    } else {
                                        component = <input type="text"
                                                           className="form-control"
                                                           onBlur={this._propChange.bind(this, prop.name)}
                                                           id={cellValueDef.propertyName}
                                                           defaultValue={this._displayForEdit(prop, propDef)}/>
                                    }
                                }
                            }
                        }
                    }
                } else {
                    component =<CvProp propName={cellValueDef.propertyName} entityRec={detailsContext.entityRec}
                                       overrideText={this.props.overrideText}/>
                }
            }

            return component;
        }
    },

    _displayForEdit: function (prop:Prop, propDef:PropDef) {

        if (propDef.isMoneyType) {
            return PropFormatter.formatForWrite(prop.value, propDef);
        } else if (propDef.isPercentType) {
            return PropFormatter.formatForWrite(prop.value, propDef);
        } else if (propDef.isCodeRefType) {
            return (prop.value as CodeRef).description;
        } else if (propDef.isObjRefType) {
            return (prop.value as ObjectRef).description;
        } else {
            return PropFormatter.formatForWrite(prop.value, propDef);
        }

    },

    _displayAsOption: function (optionValue:string, propDef:PropDef) {
        const stringVal = PropFormatter.toString(optionValue, propDef);
        if (propDef.isCodeRefType) {
            return StringUtil.splitSimpleKeyValuePair(stringVal)[1];
        } else if (propDef.isObjRefType) {
            return StringUtil.splitSimpleKeyValuePair(stringVal)[1];
        } else {
            return stringVal;
        }
    },

    refresh: function (nextProps) {

        const cellValueDef = this.cellValueDef(nextProps);
        const detailsContext = this.detailsContext(nextProps);

        if (!detailsContext.isReadModeFor(cellValueDef.propertyName)) {
            if (cellValueDef.propertyName && (cellValueDef.isComboBoxEntryMethod || cellValueDef.isDropDownEntryMethod)) {
                return detailsContext.getAvailableValues(cellValueDef.propertyName).onComplete((valueTry:Try<Array<any>>) => {
                    if (valueTry.isSuccess) {
                        this.setState({availableValues: valueTry.success});
                    } else {
                        const event:CvEvent<CvMessage> = {
                            type: CvEventType.MESSAGE,
                            eventObj: {
                                message: 'Could not get available values for property: ' + cellValueDef.propertyName,
                                messageObj: valueTry.failure,
                                type: CvMessageType.ERROR
                            }
                        }
                        this.eventRegistry().publish(event, false);
                    }
                });
            }
        }
    },

    _propChange: function (propName:string, e:any) {
        let value = e.currentTarget.value === cv_no_selection_value ? null : e.currentTarget.value;
        if(value == '' || value === undefined) {
            value = null;
        }
        this.detailsCallback().setPropValue(propName, value);
    },

    _dateChange: function (propName:string, propDef:PropDef, timeValue:string) {
        if (timeValue) {
            const d:Date = new Date(Number(timeValue));
            if (propDef.isDateTimeType || propDef.isDateType) {
                this.detailsCallback().setPropValue(propName, d);
            } else if (propDef.isTimeType) {
                this.detailsCallback().setPropValue(propName, TimeValue.fromDateValue(d));
            }
        }
    },

    _checkboxChange: function (propName:string, e:any) {
        if (e.currentTarget.checked) {
            this.detailsCallback().setPropValue(propName, true);
        } else {
            this.detailsCallback().setPropValue(propName, false);
        }
    }


});