/**
 * Created by rburson on 12/23/15.
 */

import * as React from 'react'
import {
    CvState,
    CvProps,
    CvBaseMixin,
    CvEvent,
    CvEventRegistry,
    CvEventType,
    CvLoginResult,
    CvLogout,
    CvLogoutResult,
    CvSessionManager,
    CvContext
} from './catreact-core'
import {AppContext, AppWinDef, Try, SessionContext} from 'catavolt-sdk'

export interface CvAppWindowState extends CvState {
    appWinDef:AppWinDef;
}

export interface CvAppWindowProps extends CvProps {
    /**
     * {@link CvLoginResult} containing the sdk {AppWinDef}.
     *  Should not be used with windowId
     */
    loginResult?:CvLoginResult;

    /**
     * A resource to allow for the retrieval of the {@link CvLoginResult} from the cache.  
     * Should not be used with loginResult
     */
    windowId?:string;

    /**
     * A listener that will notified of an invalid session
     */
    logoutListeners?:Array<(event:CvEvent<CvLogoutResult>)=>void>
   
    /**
        Custom renderer for CvAppWindow. Exposes the AppWinDef as the scopObj and the relevant callback object.
     */
    renderer?:(cvContext:CvContext, callback?:CvAppWindowCallback)=>{};
}

export interface CvAppWindowCallback {
  
    getAppContext():AppContext;
}

const SESSION_CHECK_INTERVAL_MILLIS:number = 30000;

/**
 * A component analogous to Catavolt AppWinDef
 */
export var CvAppWindow = React.createClass<CvAppWindowProps, CvAppWindowState>({

    propTypes: {
        /**
         * loginResult?:CvLoginResult;
         */
        loginResult: React.PropTypes.shape({appWinDef: React.PropTypes.instanceOf(AppWinDef)}),

        logoutListeners: React.PropTypes.arrayOf(React.PropTypes.func),

        windowId: React.PropTypes.string,

        catavolt: React.PropTypes.instanceOf(AppContext),

        eventRegistry: React.PropTypes.instanceOf(CvEventRegistry),
        /**
         * renderer signature: (cvContext:CvContext, callbackObj?:any)=>{}
         */
        renderer:React.PropTypes.func
    },

    mixins: [CvBaseMixin],

    componentDidMount: function () {

        if(this.props.loginResult || this.props.windowId) {
            this.updateAppWinDef(this.props.loginResult, this.props.windowId);
        } else {
            (this.eventRegistry() as CvEventRegistry).subscribe<CvLoginResult>(this._loginListener, CvEventType.LOGIN);
        }

    },

    componentWillUnmount: function() {
        this._stopSessionTimer();
        (this.eventRegistry() as CvEventRegistry).unsubscribe(this._loginListener);
    },

    componentWillReceiveProps: function(nextProps) {
        if(nextProps.loginResult || nextProps.windowId) {
            this.updateAppWinDef(nextProps.loginResult, nextProps.windowId);
        } else {
           this.setAppWinDef(null);
        }
    },

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

    getDefaultProps: function() {
        return {loginResult: null, windowId: null, logoutListeners: []}
    },

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

    render: function () {

        if(this.state.appWinDef) {
            if (this.props.renderer) {
                return this.props.renderer(this.getChildContext().cvContext, this._getCallbackObject());
            } else if (React.Children.count(this.props.children) > 0) {
                return this.props.children
            } else {
                return null;
            }
        } else {
            return null;
        }
    },
    
    setAppWinDef: function(appWinDef:AppWinDef) {
        this.setState({appWinDef: appWinDef});
        this._resetSessionTimer();
    },
    
    updateAppWinDef: function(loginResult:CvLoginResult, windowId:string) {
        if(loginResult) {
            this.setAppWinDef(loginResult.appWinDef);
        } else if(windowId) {
            this._handleRetrieveAppWinDef(windowId);
        }
    },

    _getCallbackObject: function ():CvAppWindowCallback {
        return {
            getAppContext: ():AppContext => {
                return this.catavolt();
            }
        }
    },
    
    _handleRetrieveAppWinDef: function(windowId:string) {
        const event:CvEvent<CvLoginResult> = this.eventRegistry().getEventByKey(windowId);
        if(!event) {
            CvSessionManager.updateSession(this.catavolt(), this.eventRegistry()).onComplete((appWinDefTry:Try<AppWinDef>)=>{
                if(appWinDefTry.isSuccess) {
                    this.setAppWinDef(appWinDefTry.success);
                } else {
                    this._processLogout();
                }
            });
        } else {
            this.setAppWinDef(event.eventObj.appWinDef);
        }
    },

    _loginListener: function(loginEvent:CvEvent<CvLoginResult>) {
        this.updateAppWinDef(loginEvent.eventObj);
    },
    
    _processLogout: function() {
        let tenantId = null;
        if(this.catavolt().sessionContextTry.isSuccess){
            tenantId = this.catavolt().sessionContextTry.success.tenantId;
        } else {
            const sessionContext:SessionContext = CvSessionManager.getSession();
            if(sessionContext) {
                tenantId = sessionContext.tenantId;
            }
        }
        (CvLogout as any).performLogout(this.catavolt(), this.eventRegistry(), tenantId, this.props.logoutListeners)
    },
    
    _resetSessionTimer: function() {
        this._stopSessionTimer();
        this.sessionInterval = setInterval(()=>{
            if(this.catavolt().remainingSessionTime < SESSION_CHECK_INTERVAL_MILLIS) {
                clearInterval(this.sessionInterval);
                this._processLogout();
            }
        }, SESSION_CHECK_INTERVAL_MILLIS);
    },
    
    _stopSessionTimer: function() {
        if(this.sessionInterval) {
            clearInterval(this.sessionInterval);
        }
    }


});
