/**
 * Created by rburson on 3/13/17.
 */
import * as React from 'react'
import {
    CvState,
    CvProps,
    CvContext,
    CvEvent,
    CvEventRegistry,
    CvEventType,
    CvActionFiredResult,
    CvScopeContext,
    CvAction,
    CvActionBase,
    CvMessageType,
    CvBaseMixin,
    CvResultCallback, CvStateChangeType, CvStateChangeResult, CvNavigationResult,
    CvNavigationResultUtil, CvMessage
} from './catreact-core'
import {
    PaneContext,
    ViewDesc, ObjUtil, Try, FormContext, NavRequest, Either, AppContext
} from 'catavolt-sdk'

export interface CvViewDescProps extends CvProps {
    /**
     * Array of {@link CvListener}s that will be notified if a view is 'opened'
     */
    actionListeners?:Array<(event:CvEvent<CvActionFiredResult>)=>void>
    /**
     * Array of {@link CvListener> that will be notified of a state change for which they may need to refresh
     */
    stateChangeListeners?:Array<(event:CvEvent<CvStateChangeResult>)=>void>;
    /**
     * The sdk {ViewDesc}
     */
    viewDesc?:ViewDesc;
    /**
     * Register to receive {@link CvEvent}s of type {@link CvNavigationResult}
     */
    navigationListeners?:Array<(event:CvEvent<CvNavigationResult>)=>void>
    /**
     * Provide a target for any navigations that originate from this action.  The navTarget should
     * correspond to the 'targetId' value of the coresident {@link CvNavigation} or {@link CvNavigator}.
     * This is useful for single page apps where components may be coresident.
     */
    navTarget?:string;
    /**
     * The sdk {PaneContext} associated with this action.  If not specified, the context chain will be upwardly
     * searched to find a component with a containing sdk {PaneContext} scopeObj
     */
    paneContext?:PaneContext;
    /**
     * Render override function.  It should accept the following params:
     * @param cvContext The current cvContext.  The cvContext.cvScope.scopeObj will be the sdk {ViewDesc} object
     */
    renderer?:(cvContext:CvContext, callback:CvViewDescCallback)=>{}
    /**
     * Name of the wrapping element (should be component name)
     * Defaults to 'span' from backward compatibility's sake
     */
    wrapperElemName?:string;
    /**
     * Props for the wrapping element
     */
    wrapperElemProps?:any;
    /**
     * Name of the wrapperElem property that fires the 'open viewDesc' action (i.e. onClick or onPress)
     * Defaults to 'onClick'
     */
    wrapperEventHandlerName?:string;
}

interface CvViewDescState extends CvState{
}

export interface CvViewDescCallback {
    openView(resultCallback?:CvResultCallback<Either<PaneContext, NavRequest>>):void;
}

export var CvViewDesc = React.createClass<CvViewDescProps, CvViewDescState>({
    
    mixins: [CvBaseMixin],

    componentDidMount: function() {
    },

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

    getDefaultProps: function () {
        return {
            paneContext: null,
            actionListeners: [],
            stateChangeListeners: [],
            navigationListeners: [],
            navTarget: null,
            viewDesc: null,
            renderer: null,
            wrapperElemName: 'span',
            wrapperElemProps: {},
            wrapperEventHandlerName: 'onClick',

        }
    },
   
    openView: function(resultCallback?:CvResultCallback<Either<PaneContext, NavRequest>>) {

        (CvViewDesc as any).openView(
            this.viewDesc(),
            this.paneContext(),
            this.eventRegistry(),
            this.catavolt(),
            this.props.actionListeners,
            this.props.stateChangeListeners,
            this.props.navigationListeners,
            this.props.navTarget,
            resultCallback
        );
    },

    paneContext: function ():PaneContext {
        var paneContext:PaneContext = null;
        if (this.props.paneContext) {
            paneContext = this.props.paneContext;
        } else {
            paneContext = this.findPaneContext();
        }
        return paneContext;
    },

    render: function () {
        if(this.viewDesc()) {
            if (this.props.renderer) {
                return this.props.renderer(this.getChildContext().cvContext, this._getCallbackObj())
            } else if (this.props.children) {
                const props = ObjUtil.addAllProps(this.props.wrapperElemProps, {});
                props[this.props.wrapperEventHandlerName] = ()=>{this.openView()};
                return React.createElement(this.props.wrapperElemName, props, this.props.children);
            } else {
                return null;
            }
        } else {
            return null;
        }
    },
    
    viewDesc: function():ViewDesc {
        return this.props.viewDesc;
    },

    _getCallbackObj: function ():CvViewDescCallback {

        return {
            openView: (resultCallback?:CvResultCallback<Either<PaneContext, NavRequest>>):void => {
                this.openView(resultCallback);
            }
        }
    },

    statics : {

        publishStateChange: function (paneContext:PaneContext, eventRegistry:CvEventRegistry,
                                      stateChangeListeners?:Array<(event:CvEvent<CvStateChangeResult>)=>void>) {
            if (paneContext) {
                const event:CvEvent<CvStateChangeResult> =
                    {
                        type: CvEventType.STATE_CHANGE,
                        resourceId: null,
                        eventObj: {source: paneContext, type: CvStateChangeType.PANE_DEF_CHANGE}
                    };
                eventRegistry.publish(event, false)
                stateChangeListeners.forEach(listener=> {
                    listener(event)
                });
            }
        },

        publishNavigation: function(navRequest:NavRequest, actionId:string,
                                    paneContext:PaneContext, catavolt:AppContext, eventRegistry:CvEventRegistry,
                                    navigationListeners:Array<(event:CvEvent<CvNavigationResult>)=>void>, navTarget:string) {
            const e:CvEvent<CvNavigationResult> =
                CvNavigationResultUtil.publishNavigation(catavolt, eventRegistry, navRequest,
                    actionId, null, navTarget, navigationListeners,
                    paneContext.isDestroyed, false);
        },

        publishError: function(failure:any, eventRegistry:CvEventRegistry) {
            const event:CvEvent<CvMessage> = {type:CvEventType.MESSAGE, eventObj:{message:'Change View Failed',
                messageObj:failure, type: CvMessageType.ERROR}}
            eventRegistry.publish(event, false);
        },


        openView: function(viewDesc:ViewDesc, paneContext:PaneContext,
                           eventRegistry:CvEventRegistry,
                           catavolt:AppContext,
                           actionListeners:Array<(event:CvEvent<CvActionFiredResult>)=>void>,
                           stateChangeListeners:Array<(event:CvEvent<CvStateChangeResult>)=>void>,
                           navigationListeners:Array<(event:CvEvent<CvNavigationResult>)=>void>,
                           navTarget:string,
                           resultCallback?:CvResultCallback<Either<PaneContext, NavRequest>>) {

            const actionId = '#viewChange';

            CvActionBase._publishActionStarted(actionId, paneContext, false, actionListeners, eventRegistry);
            paneContext.openView(viewDesc).onComplete((openViewTry:Try<Either<PaneContext, NavRequest>>)=> {
                CvActionBase._publishActionFinished(actionId, paneContext, actionListeners, eventRegistry);
                if (openViewTry.isFailure) {
                    if (resultCallback) resultCallback(null, openViewTry.failure);
                    (CvViewDesc as any).publishError(openViewTry.failure, eventRegistry);
                } else {
                    if (resultCallback) resultCallback(openViewTry.success);
                    if(openViewTry.success.isLeft) {
                        (CvViewDesc as any).publishStateChange(openViewTry.success.left, eventRegistry, stateChangeListeners);
                    } else {
                        (CvViewDesc as any).publishNavigation(openViewTry.success.right, actionId, paneContext, catavolt,
                            eventRegistry, navigationListeners, navTarget);
                    }
                }
            });
        },

    }

});