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

import * as React from 'react'
import {
    CvState,
    CvProps,
    CvBaseMixin,
    CvDetailsPaneCallback,
    CvEvent,
    CvEventType,
    CvMessage,
    CvMessageType, CvValueAdapter, CvValueListener, CvImageExistenceState, CvImageAction, CvImagePackage,
    CvImagePackageUrl, CvImageProducer
} from 'catreact'
import {
    CvDataAnno,
    CvDataAnnoStyle,
    CvHtmlProp
} from './catreact-html'
import {
    ArrayUtil,
    Try,
    Prop,
    PropDef,
    DetailsContext,
    CellValueDef,
    AttributeCellValueDef,
    LabelCellValueDef,
    ForcedLineCellValueDef,
    SubstitutionCellValueDef,
    TabCellValueDef,
    Log,
    PropFormatter,
    CodeRef,
    ObjectRef,
    ObjUtil,
    TimeValue,
    EditorContext,
    Attachment, Binary
} from 'catavolt-sdk'

import SyntheticEvent = React.SyntheticEvent;


/*
 *  -------- Start react-widgets setup -----------
 *  The following are all the requirements for using the react-widgets dateTime components
 *  globalize is required and a configured 'instance' must be provided to react-widgets
 *  Note: These all use the older, commonjs-style object export
 *  On the bright side, globalize is very comprehensive, so supporting different locales and formats
 *  of numbers, currency, and dates in the future should be easier
 */
import {DateTimePicker, DropdownList, Multiselect} from 'react-widgets';

import Globalize = require('globalize')

//import the localization data

import cldrDataSubtags  = require( "cldr-data/supplemental/likelySubtags" );
import cldrDataTime = require( "cldr-data/supplemental/timeData" );
import cldrDataWeek = require( "cldr-data/supplemental/weekData" );
//English
import cldrDataNumSystems = require( "cldr-data/supplemental/numberingSystems" );
import cldrDataGreg = require( "cldr-data/main/en/ca-gregorian" );
import cldrDataTimeZoneNames = require( "cldr-data/main/en/timeZoneNames" );
import cldrDataDate = require( "cldr-data/main/en/dateFields" );
import cldrDataNumber = require( "cldr-data/main/en/numbers" );
//German
import cldrDataGregDE = require( "cldr-data/main/de/ca-gregorian" );
import cldrDataTimeZoneNamesDE = require( "cldr-data/main/de/timeZoneNames" );
import cldrDataDateDE = require( "cldr-data/main/de/dateFields" );
import cldrDataNumberDE = require( "cldr-data/main/de/numbers" );

Globalize.load(cldrDataDate, cldrDataGreg, cldrDataNumber,
    cldrDataGregDE, cldrDataTimeZoneNamesDE, cldrDataDateDE, cldrDataNumberDE,
    cldrDataTime, cldrDataWeek, cldrDataSubtags,
    cldrDataTimeZoneNames, cldrDataNumSystems);

//Set the locale here
Globalize.locale('en')

import globalizeLocalizer = require('react-widgets-globalize')
globalizeLocalizer(Globalize);

/*
 *  -------- End react-widgets setup ----------
 */

import * as  moment from 'moment';

export interface CvCellValueDefState extends CvState {
}

export interface CvCellValueDefProps extends CvProps {
    cellValueDef:CellValueDef;
    detailsContext?:EditorContext;
    detailsCallback?:CvDetailsPaneCallback;
    imageProducers?:Array<CvImageProducer>;
    imageReadOnly?:boolean,
    wrapperElem:any;
    wrapperElemProps?:any;
    overrideDefaultValue?:string;
    valueChangeListener?:(propName:string, propDef:PropDef, newValue:any)=>void;
}

export let CvCellValueDefMixin = {

    _applyHeaderStyle(component:any, cellValueDef:CellValueDef): any {
        let answer = component;
        if (cellValueDef.isHeading1Style) {
            answer = <h1>{component}</h1>;
        } else if (cellValueDef.isHeading2Style) {
            answer = <h2>{component}</h2>;
        } else if (cellValueDef.isHeading3Style) {
            answer = <h3>{component}</h3>;
        } else if (cellValueDef.isHeading4Style) {
            answer = <h4>{component}</h4>;
        }
        return answer;
    },

    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 let 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 hasFocus = detailsContext.detailsDef.focusPropName ?
                        (cellValueDef as AttributeCellValueDef).propertyName == detailsContext.detailsDef.focusPropName : false;
                    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()}
                                                 imageProducers={this.props.imageProducers}
                                                 imageReadOnly={this.props.imageReadOnly}
                                                 overrideDefaultValue={this.props.overrideDefaultValue}
                                                 overrideText={styleInfo.overrideText}
                                                 valueChangeListener={this.props.valueChangeListener}
                                                 hasFocus={hasFocus}/>
                    </CvDataAnno>

                    return component;


                } else if (cellValueDef instanceof LabelCellValueDef) {
                    component = <span className="cv-label-cell-def">{(cellValueDef as LabelCellValueDef).value}</span>
                    // Apply the heading style
                    component = this._applyHeaderStyle(component, cellValueDef);

                } else if (cellValueDef instanceof ForcedLineCellValueDef) {
                    // Display a hidden M to make sure this line takes up some vertical space.
                    component = <span className="cv-forced-line-cell-def" style={{visibility:"hidden"}}>{'M'}</span>
                } else if (cellValueDef instanceof TabCellValueDef) {
                    component = <span className="cv-tab-cell-def"/>
                } 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>;
    selectedImageSrc?:string;
    imageExistenceState:CvImageExistenceState;
    imageUndoData: any;
}

export interface CvAttributeCellValueDefProps extends CvProps {
    cellValueDef:CellValueDef;
    detailsContext?:EditorContext;
    detailsCallback?:CvDetailsPaneCallback;
    imageProducers?:Array<CvImageProducer>;
    imageReadOnly?:boolean,
    inlineStyle?:{};
    overrideText?:string;
    overrideDefaultValue?:string;
    valueChangeListener?:(propName:string, propDef:PropDef, newValue:any)=>void;
    classSelector?:(propDef:PropDef, defaultClass?:string)=>string;
    hasFocus?:boolean;
}

const cv_no_selection_value:string = 'cv_no_selection_value';

export let 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,
            overrideDefaultValue: null, valueChangeListener:null, classSelector:null, hasFocus:false }
    },

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

    render: function () {
        const csf = this.props.classSelector ? this.props.classSelector : (propDef:PropDef, defaultClass?:string):string => { return defaultClass }
        const inlineStyle = this.props.inlineStyle;
        if (this.props.renderer) {
            return this.props.renderer(this.getChildContext().cvContext);
        } else {
            const detailsContext = this.detailsContext();
            const cellValueDef:AttributeCellValueDef = this.cellValueDef();
            const overrideDefaultValue = this.props.overrideDefaultValue;
            const focusFn = (ref)=>{ if(ref && this.props.hasFocus) ref.focus(); }
            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 is not applied atm
                                const cn = csf(propDef,"form-control cv-cell-def-select-box");
                                const data:{selected:any, values:Array<any>} = this._assignData(overrideDefaultValue, prop.value, propDef, this.state.availableValues);
                                component = <DropdownList
                                    defaultValue={data.selected}
                                    data={data.values}
                                    textField={((item)=>this._getOptionDisplayValue(item, propDef)) as any}
                                    onChange={this._propChange.bind(this, prop.name, propDef)}
                                    ref={focusFn}/>
                            }
                            /*** Dropdown ***/
                        } else if (cellValueDef.isDropDownEntryMethod) {
                            if (this.state.availableValues) {
                                if(propDef.isListType) {
                                    component = <Multiselect defaultValue={overrideDefaultValue ? overrideDefaultValue : this._createDisplayValueList(prop.value, propDef)}
                                                             data={this._createDisplayValueList(this.state.availableValues, propDef)}
                                                             textField='display' placeholder="No Items Selected"
                                                             onChange={this._listChange.bind(this, prop.name, propDef)} ref={focusFn}/>
                                } else {
                                    //this is not applied atm
                                    const cn = csf(propDef,"form-control cv-cell-def-select-box");
                                    const data:{selected:any, values:Array<any>} = this._assignData(overrideDefaultValue, prop.value, propDef, this.state.availableValues);
                                    component = <DropdownList
                                        defaultValue={data.selected}
                                        data={data.values}
                                        textField={((item)=>this._getOptionDisplayValue(item, propDef)) as any}
                                        onChange={this._propChange.bind(this, prop.name, propDef)}
                                        ref={focusFn}/>
                                }
                            }
                            /*** Icon chooser ***/
                        } else if (cellValueDef.isIconEntryMethod) {
                            if (this.state.availableValues) {
                                const data:{selected:any, values:Array<any>} = this._assignData(null, prop.value, propDef, this.state.availableValues);
                                component = <DropdownList defaultValue={data.selected}
                                                          data={data.values}
                                                          placeholder="No Items Selected"
                                                          valueComponent={this._newIconRenderer("cv-icon-chooser-value")}
                                                          itemComponent={this._newIconRenderer("cv-icon-chooser-item")}
                                                          onChange={this._propChangeIcon.bind(this, prop.name, propDef)}
                                                          ref={focusFn}/>
                            }
                        } else {
                            /*** Binary ***/
                            if (detailsContext.isBinary(cellValueDef)) {
                                const imageActions:Array<CvImageAction> = this.props.imageProducers == null ? null :
                                    this.props.imageProducers.filter(ip=> {
                                        return (this.state.imageExistenceState == CvImageExistenceState.Present && ip.includeWhenImageisPresent)
                                            || (this.state.imageExistenceState == CvImageExistenceState.Missing && ip.includeWhenImageIsMissing)
                                            || (this.state.imageExistenceState == CvImageExistenceState.Deleted && ip.includeWhenImageIsDeleted);
                                    }).map(w=>{
                                        let ip:CvImageProducer = w as CvImageProducer;
                                        let launchPick = ()=> {
                                            let input:HTMLInputElement = this.refs["inputFileRef"] as HTMLInputElement;
                                            input.click();
                                        };
                                        let a:CvValueAdapter<CvImagePackage> = new CvValueAdapter<CvImagePackage>();
                                        let f = this.setImageFromDataUrl.bind(this, prop.name, propDef, launchPick);
                                        a.subscribe((v:Object)=>{ f(v) });
                                        return { callback:()=>{ ip.prodImageCB(a.createValueListener())},
                                            buttonClassName:ip.buttonClassName};
                                    });

                                const style = this.props.inlineStyle ? this.props.inlineStyle : (this.props.imageStyle ? this.props.imageStyle : this.props.style);
                                const readOnly = (this.props.imageReadOnly === undefined) || (this.props.imageReadOnly === null) ? false :  this.props.imageReadOnly;
                                component = <div>
                                    <input type="file" onChange={this._fileChange.bind(this, prop.name, propDef)} ref="inputFileRef" style={{display:"none"}}/>
                                        <CvHtmlProp style={style} propName={cellValueDef.propertyName} entityRec={detailsContext.entityRec}
                                                    imageActions={imageActions} imageReadOnly={readOnly} hasFocus={this.props.hasFocus}/>
                                </div>


                            } else {
                                /*** Boolean ***/
                                if (propDef.isBooleanType) {
                                    component =<input type="checkbox" defaultChecked={overrideDefaultValue != null ? overrideDefaultValue : prop.value}
                                                      onChange={this._checkboxChange.bind(this, prop.name, propDef)} ref={focusFn}/>
                                    /*** Datetime ***/
                                } else if (propDef.isDateTimeType) {
                                    const formatter = Globalize.dateFormatter({datetime:"medium"});
                                    const editFormatter = Globalize.dateFormatter({datetime:"short"});
                                    const timeFormatter = Globalize.dateFormatter({time:"medium"});
                                    component =<DateTimePicker
                                        defaultValue={overrideDefaultValue ? overrideDefaultValue : prop.value}
                                        onChange={this._dateChange.bind(this, prop.name, propDef)}
                                        calendar={true}
                                        time={true}
                                        format={formatter}
                                        editFormat={editFormatter}
                                        timeFormat={timeFormatter}
                                        parse= {(str)=> { return moment(str).toDate()}}
                                        ref={focusFn}/>
                                    /*** Date ***/
                                } else if (propDef.isDateType) {
                                    const formatter = Globalize.dateFormatter({date:"long"});
                                    const editFormatter = Globalize.dateFormatter({date:"short"});
                                    component =<DateTimePicker
                                        defaultValue={overrideDefaultValue ? overrideDefaultValue : prop.value}
                                        onChange={this._dateChange.bind(this, prop.name, propDef)}
                                        calendar={true}
                                        time={false}
                                        format={formatter}
                                        editFormat={editFormatter}
                                        parse= {(strDate)=> { return moment(strDate).toDate()}}
                                        ref={focusFn}/>
                                    /*** Time ***/
                                } else if (propDef.isTimeType) {
                                    const formatter = Globalize.dateFormatter({time:"medium"});
                                    const editFormatter = Globalize.dateFormatter({time:"medium"});
                                    component =<DateTimePicker
                                        defaultValue={overrideDefaultValue ? overrideDefaultValue : (prop.value ? (prop.value as TimeValue).toDateValue() : null)}
                                        onChange={this._dateChange.bind(this, prop.name, propDef)}
                                        calendar={false}
                                        time={true}
                                        format={formatter}
                                        timeFormat={editFormatter}
                                        parse= {(strTime)=> { return moment("2000-01-01 " + strTime, ["YYYY-MM-DD h:m:s:a", "YYYY-MM-DD H:m:s"]).toDate()}}
                                        ref={focusFn}/>
                                } else if(propDef.isFileAttachment) {
                                    component =<input style={inlineStyle} type="file"
                                                      onChange={this._fileChange.bind(this, prop.name, propDef)} ref={focusFn}/>
                                /*** Text ***/
                                } else if(propDef.isTextBlock) {
                                    const cn = csf(propDef, "form-control");
                                    component = <textarea id={cellValueDef.propertyName}
                                                          className={cn}
                                                          style={inlineStyle}
                                                          onBlur={(e:any)=>this._propChange(prop.name, propDef, e.currentTarget.value)}
                                                          defaultValue={prop ? prop.value : ""} ref={focusFn}/>
                                } else {
                                    const cn = csf(propDef, "form-control");
                                    component = <input type="text"
                                                       className={cn}
                                                       style={inlineStyle}
                                                       onBlur={(e:any)=>this._propChange(prop.name, propDef, e.currentTarget.value)}
                                                       id={cellValueDef.propertyName}
                                                       defaultValue={overrideDefaultValue ? overrideDefaultValue : PropFormatter.formatForWrite(prop, propDef)}
                                                       ref={focusFn}/>
                                }
                            }
                        }
                    }
                } else {
                    const propDef:PropDef = detailsContext.entityRecDef.propDefAtName(cellValueDef.propertyName);
                    let optionalProps = propDef && propDef.isTextBlock ? {className:"cv-text-area"} : {};
                    if (propDef.isURLType) {
                        const prop:Prop = detailsContext.buffer.propAtName(cellValueDef.propertyName);
                        let url = PropFormatter.formatForRead(prop, propDef);
                        // if (url && url.indexOf("http") !== 0 ) {       THIS WOULD PROHIBIT mailto:....
                        //     url = "https://" + url;
                        // }
                        component = <a href={url} target="_blank">{url}</a>
                    } else {
                        component = <CvHtmlProp {...optionalProps} propName={cellValueDef.propertyName} entityRec={detailsContext.entityRec}
                                                overrideValue={this.props.overrideText} style={inlineStyle} imageReadOnly={true}/>
                    }
                }
            }
            // Apply the heading style
            component = this._applyHeaderStyle(component, cellValueDef);
            return component;
        }
    },

    refresh: function (nextProps) {

        const cellValueDef = this.cellValueDef(nextProps);
        const detailsContext = this.detailsContext(nextProps);
        const prop:Prop = detailsContext.buffer.propAtName(cellValueDef.propertyName);
        const propDef:PropDef = detailsContext.entityRecDef.propDefAtName(cellValueDef.propertyName);

        if (!detailsContext.isReadModeFor(cellValueDef.propertyName)) {
            if (cellValueDef.propertyName && (cellValueDef.isComboBoxEntryMethod || cellValueDef.isDropDownEntryMethod || cellValueDef.isIconEntryMethod)) {
                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);
                    }
                });
                // If not already done, set the image-existence-state.
            } else if (propDef.isBinaryType && (!this.state.imageExistenceState) && detailsContext.isFirstReadComplete) {
                if (prop && prop.value) {
                    this.setState({imageExistenceState: CvImageExistenceState.Present});
                 } else {
                    this.setState({imageExistenceState: CvImageExistenceState.Missing});
                }
            }
        }
    },

    setImageFromDataUrl:function (propName:string, propDef:PropDef, launchPick:()=>void, imagePackage:CvImagePackage) {
        const imagePresent = this.state.imageExistenceState === CvImageExistenceState.Present;
        const futureUndoUrl = (imagePresent && this.state.selectedImageSrc) ? this.state.selectedImageSrc : null;
        let dataUrl:string = null;
        let needToSetImage = false;

        // If this is an undo request, and there is no imageUndoData in state, then we should undo with the original
        // value which requires an async read.
        if (imagePackage.isPick()) {
            launchPick();
        } else if (imagePackage.isUndo()) {
            if (this.state.imageUndoData) {
                // Set the new value in the model and state.
                dataUrl = this.state.imageUndoData;
                needToSetImage = true;
            } else {
                // If there is no undo data, read the binary value from the SDK.
                const detailsContext = this.detailsContext();
                const cellValueDef: AttributeCellValueDef = this.cellValueDef();
                const prop: Prop = detailsContext.buffer.propAtName(cellValueDef.propertyName);
                detailsContext.binaryAt(cellValueDef.propertyName, detailsContext.buffer.before).onComplete((binaryTry: Try<Binary>) => {
                    if (binaryTry.isSuccess) {
                        if (this.isMounted()) {
                            this.setState({imageUndoData: binaryTry.success.toUrl()});
                            this.setImageFromDataUrl(propName, propDef, launchPick, new CvImagePackageUrl(binaryTry.success.toUrl()));
                        }
                    }
                });
            }
        } else if (imagePackage.isUrl()) {
                // If the value is being set, take the current value and add to the undo state for a future undo.
            dataUrl = (imagePackage as CvImagePackageUrl).url;
            this.setState({imageUndoData: futureUndoUrl});
            needToSetImage = true;
        } else if (imagePackage.isDelete()) {
            dataUrl = null;
            this.setState({imageUndoData: futureUndoUrl});
            needToSetImage = true;
        }
        if (needToSetImage) {
            if (this.props.valueChangeListener) this.props.valueChangeListener(propName, propDef, dataUrl);
            this.detailsCallback().setBinaryPropWithDataUrl(propName, dataUrl);
            this.setState({selectedImageSrc: dataUrl,
                    imageExistenceState: (dataUrl ? CvImageExistenceState.Present : CvImageExistenceState.Deleted)});
        }
    },

    _createDisplayValueList: function(values:Array<any>, propDef):Array<any> {
          if(values && values.length > 0) {
             return values.map((value)=>{ return this._createDisplayValueObject(value, propDef);});
          } else {
              return null;
          }
    },
    
    _createDisplayValueObject: function(optionValue:any, propDef:PropDef) {
        return {display: this._getOptionDisplayValue(optionValue, propDef), value:optionValue};
    },

    _getOptionDisplayValue: function (optionValue:any, propDef:PropDef):string {
        if(optionValue === cv_no_selection_value) {
            return 'No Selection';
        } else if (optionValue instanceof CodeRef) {
            return (optionValue as CodeRef).description;
        } else if (optionValue instanceof ObjectRef) {
            return (optionValue as ObjectRef).description;
        } else {
            return PropFormatter.toString(optionValue, propDef);
        }
    },

    _assignData(override:any, value:any, propDef:PropDef, values:Array<any>):{selected:any, values:Array<any>} {
        let selected = override ? override :
            this._findPropValueInList(value, propDef, this.state.availableValues);
        let answer = null;
        if (selected == null && (value != null)) {
            values.splice(0, 0, value);
            answer = {selected: value, values: values};
        } else if (selected == null) {
            answer = {selected: cv_no_selection_value, values: values};
        } else {
            answer = {selected: selected, values: values};
        }
        return answer;
    },

    _findPropValueInList: function(optionValue:any, propDef:PropDef, optionValues:Array<any>) {
          const result = ArrayUtil.find(optionValues,
              item=>Prop.identity(optionValue, propDef) == Prop.identity(item, propDef));
          return result; //  ? result : cv_no_selection_value;
    },
    
    _listChange: function(propName:string, propDef:PropDef, list:Array<any>) {
        const value = list.map((v)=>{ return v.value; });
        if(this.props.valueChangeListener) this.props.valueChangeListener(propName, propDef, value);
        this.detailsCallback().setPropValue(propName, value);
    },

    _propChange: function (propName:string, propDef:PropDef, selectedValue:any) {
        let value = selectedValue === cv_no_selection_value ? null : selectedValue;
        if (value == '' || value === undefined) {
            value = null;
        }
        if(this.props.valueChangeListener) this.props.valueChangeListener(propName, propDef, value);
        this.detailsCallback().setPropValue(propName, value);
    },

    _propChangeIcon: function (propName:string, propDef:PropDef, cr:CodeRef) {
        if(this.props.valueChangeListener) this.props.valueChangeListener(propName, propDef, cr);
        this.detailsCallback().setPropValue(propName, cr);
    },

    _dateChange: function (propName:string, propDef:PropDef, dateValue:Date, stringValue:string) {
        if(this.props.valueChangeListener) this.props.valueChangeListener(propName, propDef, dateValue);
        if (dateValue) {
            if (propDef.isDateTimeType || propDef.isDateType) {
                this.detailsCallback().setPropValue(propName, dateValue);
            } else if (propDef.isTimeType) {
                this.detailsCallback().setPropValue(propName, TimeValue.fromDateValue(dateValue));
            }
        } else {
            this.detailsCallback().setPropValue(propName, null);
        }
    },

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

    _fileChange:function (propName:string, propDef:PropDef, e:any) {
        if(e.currentTarget.files) {
            const files:FileList = e.currentTarget.files;
            if(files.length > 0) {
                if(this.props.valueChangeListener) this.props.valueChangeListener(propName, propDef, files[0]);
                //Attachment
                if(propDef.isFileAttachment) {
                    this.detailsCallback().setPropValue(propName, new Attachment(files[0].name, files[0]));
                } else {
                    //Binary
                    let fr = new FileReader();
                    fr.onload = (e:any)=>{
                        const dataUrl = e.target.result;
                        this.detailsCallback().setBinaryPropWithDataUrl(propName, dataUrl);
                        this.setState({selectedImageSrc: dataUrl, imageExistenceState: CvImageExistenceState.Present});
                    }
                    fr.readAsDataURL( files[0] );
                }
            }
        }
    },

    _newIconRenderer:function(className:string) {
        return React.createClass({
            render() {
                const cr:CodeRef = this.props.item as CodeRef;
                return <div className={className}>
                    <img src={this._findPath(cr.description)} />
                    <span>{cr.code}</span>
                </div>
            },
            _findPath: function (desc:string) {
                if (desc) {
                    const i = desc.indexOf(":");
                    if (i > -1 && (i < desc.length-1)) {
                        var answer = desc.substr(i+1);
                    }
                }
                return answer;
            }
        });

    }
});