/**
 * Created by rburson on 7/25/16.
 */
import * as React from 'react'
import {
    CvEditorBaseProps,
    CvEditorBaseState,
    CvEditorPaneCallback,
    CvBaseMixin,
    CvEditorBase,
    CvEvent,
    CvResultCallback,
    CvNavigationResult,
} from './catreact-core'
import {
    DetailsContext,
    EditorContext,
    EntityRecDef, StringUtil
} from 'catavolt-sdk'

export type CvSearchValueOperator = 'Equal' | 'Not Equal' | 'Greater Than'
    | 'Less Than' | 'Contains' | 'Starts With' | 'Ends With';

export type CvSortDirection = 'ASC' | 'DSC';

export const FILTER_VALUE_SUFFIX:string = '_FILTER_VALUE';
export const FILTER_OPERATOR_SUFFIX:string = '_FILTER_OPER';
export const SORT_DIRECTION_SUFFIX:string = '_SORT_DIRECTION';
export const SORT_SEQUENCE_SUFFIX:string = '_SORT_SEQUENCE';

export interface CvSearchPaneState extends CvEditorBaseState {
}
export interface CvSearchPaneProps extends CvEditorBaseProps {
}

/**
 * Allows for setting search values on underlying editor model
 */
export interface CvSearchPaneCallback extends CvEditorPaneCallback {

    /**
     * Reset all search values to 'null'.
     */
    clear():void;
    
    /**
     * Reset all filter related values to 'null', leaving 'sort' values intact.
     */
    clearSearchValues():void;

    /**
     * Reset all sort related values to 'null', leaving 'search/filter' values intact.
     */
    clearSortValues():void;
    /**
     * Get the current search value (if previously set) for this property name.
     * @param propName
     */
    getSearchValue(propName):string;

    /**
     * Get the current search value's operation (if previously set) for this property name.
     * @param propName
     */
    getSearchValueOperation(propName):string;

    /**
     * Get the current search value's sort direction (if previously set) for this property name.
     * @param propName
     */
    getSortValueDirection(propName):string;

    /**
     * Get the current search value's sort priority (if previously set) for this property name.
     * @param propName
     */
    getSortValuePriority(propName):string;

    /**
     * Get whether or not the current search value's sort direction is ascending for this property name.
     */
    isAscending(propName):boolean;
    
    /**
     * Get whether or not the current search value's sort direction is descending for this property name.
     */
    isDescending(propName):boolean;

    /**
     * After a submission, the search model is returned in 'read' mode.  To search again, this should be called
     * prior to making any param changes.
     * @param resultCallback
     */
    reopenSearch(resultCallback?:CvResultCallback<EntityRecDef>):void;

    /**
     * Sort the search by the specified property in the ascending direction with priority (defaults to 0, meaning primary sort field).
     * @param propName
     * @param sortFieldPriority
     */
    setAscending(propName:string, sortFieldPriority:number):void;

    /**
     * Sort the search by the specified property in the descending direction with priority (defaults to 0, meaning primary sort field).
     * @param propName
     * @param sortFieldPriority
     */
    setDescending(propName:string, sortFieldPriority:number):void;

    /**
     Set the 'value' of the search operation for a property(i.e. The 'thing' to be searched on this property). This is the value to run against the accompanying operator
     * @param propName
     * @param searchValue
     */
    setSearchValue(propName:string, searchValue:string);

    /**
     * Set the value of the 'operation' to perform on this property.  The valid values are defined by {@link CvSearchValueOperator} 
     * @param propName
     * @param operator
     */
    setSearchValueOperation(propName:string, operator:CvSearchValueOperator);
    
    /**
     * Sort the search by the specified property.  Set the sort direction {@link CvSortDirection} and priority (defaults to 0, meaning primary sort field).
     * @param propName
     * @param sortDirection
     * @param sortFieldPriority
     */
    setSortValue(propName:string, sortDirection:CvSortDirection, sortFieldPriority:number);

    /**
     * Submit the search. If successful, returns search editor model in 'read' mode. The search will be applied when the backing list is refreshed.
     * @param resultCallback
     */
    submitSearch(resultCallback?:CvResultCallback<CvNavigationResult>);
}

/**
 * Render a Search Model
 */
export var CvSearchPane = React.createClass<CvSearchPaneProps, CvSearchPaneState>({

    mixins: [CvBaseMixin, CvEditorBase],

    componentDidMount: function () {
        this._componentDidMount();
    },

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

    componentWillUnmount: function () {
        this._componentWillUnmount();
    },

    detailsContext: function (nextProps, nextContext) {
        return this.editorContext(nextProps, nextContext) as DetailsContext;
    },

    getDefaultProps: function () {
        return CvEditorBase._getDefaultProps();
    },

    render: function () {

        const editorContext:EditorContext = this.editorContext();

        if (editorContext && !editorContext.isDestroyed) {
            if (this.props.renderer) {
                return this.props.renderer(this.getChildContext().cvContext);
            } else if (this.props.detailsRenderer) {
                if (editorContext.buffer) {
                    return this.props.detailsRenderer(this.getChildContext().cvContext, editorContext.entityRec, this._getSearchCallbackObj());
                } else {
                    return null;
                }
            } else {
                return null;
            }
        } else {
            return null;
        }
    },

    _getSearchCallbackObj: function ():CvSearchPaneCallback {
        const editorCallback:CvEditorPaneCallback = this._getCallbackObj();
        const me:CvSearchPaneCallback = {
            clearSearchValues: ():void => {
                this.detailsContext().entityRec.propNames.filter((propName:string)=>{
                    return StringUtil.endsWith(propName, FILTER_OPERATOR_SUFFIX)  ||
                        StringUtil.endsWith(propName, FILTER_VALUE_SUFFIX);
                }).forEach((propName:string)=>{
                    editorCallback.setPropValue(propName, null)});
            },
            clearSortValues: ():void => {
                this.detailsContext().entityRec.propNames.filter((propName:string)=>{
                    return StringUtil.endsWith(propName, SORT_DIRECTION_SUFFIX)  ||
                            StringUtil.endsWith(propName, SORT_SEQUENCE_SUFFIX);
                }).forEach((propName:string)=>{
                    editorCallback.setPropValue(propName, null)});
            },
            getSearchValue: (propName):string => {
                return editorCallback.getPropValueForEdit(propName + FILTER_VALUE_SUFFIX);
            },
            getSearchValueOperation: (propName):string => {
                return editorCallback.getPropValueForEdit(propName + FILTER_OPERATOR_SUFFIX);
            },
            getSortValueDirection: (propName):string => {
                return editorCallback.getPropValueForEdit(propName + SORT_DIRECTION_SUFFIX);
            },
            getSortValuePriority: (propName):string => {
                return editorCallback.getPropValueForEdit(propName + SORT_SEQUENCE_SUFFIX);
            },
            isAscending: (propName):boolean => {
                const dir = me.getSortValueDirection(propName)
                //server may return ASCENDING or ASC and DSC or DESCENDING
                return dir && dir.indexOf('A') === 0;
            },
            isDescending: (propName):boolean => {
                const dir = me.getSortValueDirection(propName)
                //server may return ASCENDING or ASC and DSC or DESCENDING
                return dir && dir.indexOf('D') === 0;
            },
            reopenSearch: (resultCallback?:CvResultCallback<EntityRecDef>):void => {
                editorCallback.openWriteMode(resultCallback);
            },
            setAscending: (propName:string, sortFieldPriority:number=0):void => {
                me.setSortValue(propName, 'ASC', sortFieldPriority);
            },
            setDescending: (propName:string, sortFieldPriority:number=0):void => {
                me.setSortValue(propName, 'DSC', sortFieldPriority);
            },
            setSearchValue: (propName:string, searchValue:string):void => {
                editorCallback.setPropValue(propName + FILTER_VALUE_SUFFIX, searchValue);
            },
            setSearchValueOperation: (propName:string, operator:CvSearchValueOperator):void => {
                editorCallback.setPropValue(propName + FILTER_OPERATOR_SUFFIX, operator);
            },
            setSortValue: (propName:string, sortDirection:CvSortDirection='ASC', sortFieldPriority:number=0):void => {
                editorCallback.setPropValue(propName + SORT_DIRECTION_SUFFIX, sortDirection);
                editorCallback.setPropValue(propName + SORT_SEQUENCE_SUFFIX, sortFieldPriority);
            },
            submitSearch: (resultCallback?:CvResultCallback<CvNavigationResult>):void => {
                editorCallback.saveChanges(resultCallback);
            },
            
            /* proxy through the 'base class' methods */
            clear: ():void => {
                editorCallback.clear();
            },
            getPropValueForDisplay: (propName:string):string => {
                return editorCallback.getPropValueForDisplay(propName);  
            },
            getPropValueForEdit: (propName:string):string => {
                return editorCallback.getPropValueForEdit(propName);
            },
            openReadMode: (resultCallback?:CvResultCallback<EntityRecDef>):void => {
                editorCallback.openReadMode(resultCallback);
            },
            openWriteMode: (resultCallback?:CvResultCallback<EntityRecDef>):void => {
                editorCallback.openWriteMode(resultCallback);
            },
            refresh: ()=> {
                editorCallback.refresh();
            },
            saveChanges: (resultCallback?:CvResultCallback<CvEvent<CvNavigationResult>>, navTarget?:string):void => {
                editorCallback.saveChanges(resultCallback, navTarget);
            },
            setBinaryPropWithDataUrl: (name:string, dataUrl:string) => {
                editorCallback.setBinaryPropWithDataUrl(name, dataUrl);
            },
            setBinaryPropWithEncodedData: (name:string, encodedData:string) => {
                editorCallback.setBinaryPropWithEncodedData(name, encodedData);
            },
            setPropValue: (name:string, value:any) => {
                editorCallback.setPropValue(name, value);
            },
            suppressEvents: (suppressEvents:boolean)=>{ editorCallback.suppressEvents(suppressEvents); }
        }
        return me;
    },

});
