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

import * as React from 'react'
import {
    CvState,
    CvProps,
    CvBaseMixin,
    CvForm,
    CvContext,
    CvEvent,
    CvNavigationResult,
    CvActionFiredResult,
    CvValueAdapter,
    CvStateChangeResult,
    CvActionHandlerParams,
    CvStateChangeType,
    CvEventRegistry,
    CvEventType
} from 'catreact'
import {
    ActionSource,
    ContextAction,
    PaneContext,
    ListContext,
    DetailsContext,
    FormContext,
    FormDef,
    MapContext,
    GraphContext,
    CalendarContext,
    ImagePickerContext,
    GeoFixContext,
    GeoLocationContext,
    BarcodeScanContext,
    Log, PrintMarkupContext, ObjUtil
} from 'catavolt-sdk'
import {
    CvDetailsPanel,
    CvGridPanel,
    CvDropdownMenu,
    CvDropdownViewMenu,
    CvMapPanel,
    CvGraphPanel,
    CvSearchPanel,
    CvPrintPanel,
    CvDisplayProperties,
    CvGridEvent,
    CvGridEventType
} from './catreact-html'
import ReactElement = React.ReactElement;
import {CvToolbarMenu} from "./CvMenu";

/*
    Display property definitions
    These are the values that may be expected to be in the CvDisplayProperties prop
 */
const DISPLAY_PROP_ACTIVE_FORM_TAB =  'active_form_tab';
const DISPLAY_PROP_VIEW_ID = 'viewId';
const DISPLAY_PROP_LIST_SELECTION = 'list_sel';
const DISPLAY_PROP_LIST_TOTAL_ROWS = 'list_total_rows';
const DISPLAY_PROP_LIST_FIRST_VISIBLE_ROW = 'list_first_row';
const DISPLAY_PROP_FORM_LAYOUT = 'form_layout';

export type CvFormLayoutType = 'tab' | 'rsplit' | 'lsplit' | 'bsplit' | 'tsplit' | '4box' | 'opt' | 'vert';

export interface CvFormPanelState extends CvState {
}

export interface CvFormPanelProps extends CvProps {
    /**
     * The sdk {FormContext} to use for this form panel. If not provided, the 
     * {@link CvScopeContext} will be upwardly traversed to attempt to find a matching instance.
     */
    formContext?:FormContext;
    /**
     * Register to receive {@link CvEvent}s of type {@link CvNavigationResult}
     */
    navigationListeners?:Array<(event:CvEvent<CvNavigationResult>)=>void>;
    /**
     * Register to receive {@link CvEvent}s of type {@link CvActionFiredResult}
     */
    actionListeners?:Array<(event:CvEvent<CvActionFiredResult>)=>void>;

    /**
     * Provide a target for any navigations that originate from this component.  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;
   
    /**
     * Register to receive {@link CvEvent}s of type {@link CvStateChangeResult}
     */
    stateChangeListeners?:Array<(event:CvEvent<CvStateChangeResult>)=>void>;
    /**
     * A FormLayout component to use instead of the one supplied by the server meta data
     */
    layoutOverrideElem?:any;
    /**
     * Provide a custom renderer for the entire form.  See {@link CvFormComponentProvider}
     */
    formComponentProvider?:CvFormComponentProvider;

    /**
     * Mechanism to provide display 'settings' that are not maintained by the catavolt server
     */
    displayProperties?:CvDisplayProperties;

    /**
     * Register to receive notification of changes to display property values
     * This is primarily to allow urls to react to these changes and update accordingly
     */
    displayPropChangeListeners?:Array<(displayProperties:CvDisplayProperties)=>void>;
    
}

/**
 * Allows for overriding some or all of the construction of a {@link CvFormPanel}.
 * An object may be provided to the {@link CvFormPanel} that implements any or all of the methods on this interface.
 * The form rendering process will use any of the implemented methods on the provided object, and fallback on the defaults for others.
 */
export interface CvFormComponentProvider {

    getFormRenderer?():(cvContext:CvContext, childComponents:Array<ReactElement<any>>)=>ReactElement<any>;
    getChildFormComponentRenderer?():(formContext:FormContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getDetailsComponentRenderer?():(detailsContext:DetailsContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getGraphComponentRenderer?():(graphContext:GraphContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getListComponentRenderer?():(listContext:ListContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getMapComponentRenderer?():(mapContext:MapContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getPrintMarkupComponentRenderer?():(printMarkupContext:PrintMarkupContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getCalendarComponentRenderer?():(calendarContext:CalendarContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getImagePickerComponentRenderer?():(imagePickerContext:ImagePickerContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getGeoFixComponentRenderer?():(geoFixContext:GeoFixContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getGeoLocationComponentRenderer?():(geoLocationContext:GeoLocationContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
    getBarcodeScanComponentRenderer?():(barcodeScanContext:BarcodeScanContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=>{}
}

/**
 * Renders a FormContext, utilizing a series of composite, component renderers
 * These may be overriden via {@link CvFormComponentProvider}
 */

export var CvFormPanel = React.createClass<CvFormPanelProps, CvFormPanelState>({

    mixins: [CvBaseMixin],

    componentDidMount: function () {
    },

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

    getDefaultProps: function () {
        return {
            formContext: null,
            navigationListeners: [],
            actionListeners: [],
            navTarget: null,
            layoutOverrideElem: null,
            stateChangeListeners: [],
            formComponentProvider: null,
            displayProperties: new CvDisplayProperties(),
            displayPropChangeListeners: []
        }
    },

    render: function () {

        const formContext = this.formContext();
        if (formContext) {
            if (this.props.renderer) {
                return this.props.renderer(this.getChildContext().cvContext);
            } else {
               
                const defaultProvider = this._getDefaultFormComponentProvider();
                
                return <CvForm formContext={this.formContext()}
                               stateChangeListeners={this.props.stateChangeListeners}
                               childFormComponentRenderer={defaultProvider.getChildFormComponentRenderer()}
                               detailsComponentRenderer={defaultProvider.getDetailsComponentRenderer()}
                               listComponentRenderer={defaultProvider.getListComponentRenderer()}
                               mapComponentRenderer={defaultProvider.getMapComponentRenderer()}
                               printMarkupComponentRenderer={defaultProvider.getPrintMarkupComponentRenderer()}
                               graphComponentRenderer={defaultProvider.getGraphComponentRenderer()}
                               calendarComponentRenderer={defaultProvider.getCalendarComponentRenderer()}
                               barcodeScanComponentRenderer={defaultProvider.getBarcodeScanComponentRenderer()}
                               imagePickerComponentRenderer={defaultProvider.getImagePickerComponentRenderer()}
                               geoFixComponentRenderer={defaultProvider.getGeoFixComponentRenderer()}
                               geoLocationComponentRenderer={defaultProvider.getGeoLocationComponentRenderer()}
                               formRenderer={defaultProvider.getFormRenderer()}
                />
            }
        } else {
            return null;
        }

    },
    
    customFormComponentProvider: function() {
        return this.props.formComponentProvider;   
    },
    
    formContext: function () {
        return this.props.formContext || this.firstInScope(FormContext);
    },

    /*
     This (default) component provider checks to see if there is a supplied customFormComponentProvider and
     whether or not it has provided a custom component of a particular type.  If it has provided a component of
     a particular type, the custom component will be returned instead of the default component
     */
    _getDefaultFormComponentProvider: function():CvFormComponentProvider {
       
        //override this provider with a custom provider
        const customProvider = this.customFormComponentProvider();

        return {
           
            getFormRenderer: () => {
                return (customProvider && customProvider.getFormRenderer) ? customProvider.getFormRenderer() :
                    (cvContext:CvContext, childComponents:Array<ReactElement<any>>)=> {
                        return this._getFormLayoutComponent(childComponents);
                    }
            },
            
            getChildFormComponentRenderer: () => {
                return (customProvider && customProvider.getChildFormComponentRenderer) ? customProvider.getChildFormComponentRenderer() :
                    
                    ((formContext:FormContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        return <CvFormPanel formContext={formContext}
                                            navigationListeners={this.props.navigationListeners}
                                            stateChangeListeners={[stateChangeListener]}
                                            actionListeners={this.props.actionListeners}
                                            layoutOverrideElem={this.props.layoutOverrideElem}/>

                });
            },


            getDetailsComponentRenderer: () => {
                return (customProvider && customProvider.getDetailsComponentRenderer) ? customProvider.getDetailsComponentRenderer() :
                    
                    ((detailsContext:DetailsContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        const topMenuDef = (detailsContext.menuDefs && detailsContext.menuDefs.length > 0) ? detailsContext.menuDefs[0] : null;
                        const contextMenuDef = topMenuDef ? topMenuDef.findContextMenuDef() : null;
                        const actionHandlerAdapter:CvValueAdapter<CvActionHandlerParams> = new CvValueAdapter<CvActionHandlerParams>();
                        const VIEW_ID_PROP_NAME = detailsContext.paneRef + '_' + DISPLAY_PROP_VIEW_ID;
                        const viewId = (this.props.displayProperties as CvDisplayProperties).getProperty(VIEW_ID_PROP_NAME);

                        const menu =  <CvDropdownMenu paneContext={detailsContext}
                                                      menuDef={contextMenuDef}
                                                      openLeft={true}
                                                      navigationListeners={this.props.navigationListeners}
                                                      actionListeners={this.props.actionListeners}
                                                      stateChangeListeners={[stateChangeListener]}
                                                      actionHandler={actionHandlerAdapter.createValueListener()}
                                                      navTarget={this.props.navTarget}/>

                        const toolbarMenu =  <CvToolbarMenu paneContext={detailsContext}
                                                      menuDef={topMenuDef}
                                                      openLeft={true}
                                                      navigationListeners={this.props.navigationListeners}
                                                      actionListeners={this.props.actionListeners}
                                                      stateChangeListeners={[stateChangeListener]}
                                                      actionHandler={actionHandlerAdapter.createValueListener()}
                                                      navTarget={this.props.navTarget}/>

                        const viewMenu = <CvDropdownViewMenu paneContext={detailsContext}
                                                             selectedViewId={viewId}
                                                             actionListeners={this.props.actionListeners}
                                                             navigationListeners={this.props.navigationListeners}
                                                             viewDescs={detailsContext.viewDescs}
                                                             navTarget={this.props.navTarget}
                                                             viewSelectionListener={(viewId:string)=>{
                                                                 this.props.displayProperties.getAndUpdate(VIEW_ID_PROP_NAME, viewId)
                                                                     .publishToListeners(this.props.displayPropChangeListeners);
                                                             }}
                                                             openLeft={true}/>

                        let panel = null;
                        const actionSource:ActionSource = detailsContext.actionSource;

                        if(actionSource instanceof ContextAction &&
                            '#search' === (actionSource as ContextAction).actionId) {
                                panel = <CvSearchPanel detailsContext={detailsContext}
                                                          navigationListeners={this.props.navigationListeners}
                                                          stateChangeListeners={[stateChangeListener]}
                                                          actionListeners={this.props.actionListeners}
                                                          actionProvider={actionHandlerAdapter}/>
                        } else {
                            panel = <CvDetailsPanel detailsContext={detailsContext}
                                                    navigationListeners={this.props.navigationListeners}
                                                    stateChangeListeners={[stateChangeListener]}
                                                    actionListeners={this.props.actionListeners}
                                                    actionProvider={actionHandlerAdapter}/>
                        }

                        //using unique key here based on the viewId, hints to React that this this be a new instance of this component
                        const key = viewId ? detailsContext.paneRef + '_' + viewId : detailsContext.paneRef;
                        return <CvFormItemPanel paneContext={detailsContext} displayElement={panel} menuElement={menu} menuElementClassName="cv-panel-menu-item-details"
                                                toolbarElement={toolbarMenu} toolbarElementClassName="cv-panel-heading-toolbar-details" viewMenuElement={viewMenu} key={key} index={index}/>
                });
            },

            getGraphComponentRenderer: () => {
                return (customProvider && customProvider.getGraphComponentRenderer) ? customProvider.getGraphComponentRenderer() :
                    
                    ((graphContext:GraphContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {

                    const topMenuDef = (graphContext.menuDefs && graphContext.menuDefs.length > 0) ? graphContext.menuDefs[0] : null;
                    const contextMenuDef = topMenuDef ? topMenuDef.findContextMenuDef() : null;
                    const selectionAdapter:CvValueAdapter<Array<string>> = new CvValueAdapter<Array<string>>();
                    const actionHandlerAdapter:CvValueAdapter<CvActionHandlerParams> = new CvValueAdapter<CvActionHandlerParams>();

                    const menu = <CvDropdownMenu paneContext={graphContext}
                                                 menuDef={contextMenuDef}
                                                 openLeft={true}
                                                 navigationListeners={this.props.navigationListeners}
                                                 actionListeners={this.props.actionListeners}
                                                 stateChangeListeners={[stateChangeListener]}
                                                 navTarget={this.props.navTarget}
                                                 actionHandler={actionHandlerAdapter.createValueListener()}
                                                 selectionProvider={selectionAdapter}/>

                    const toolbarMenu =  <CvToolbarMenu paneContext={graphContext}
                                                         menuDef={topMenuDef}
                                                         openLeft={true}
                                                         navigationListeners={this.props.navigationListeners}
                                                         actionListeners={this.props.actionListeners}
                                                         stateChangeListeners={[stateChangeListener]}
                                                         actionHandler={actionHandlerAdapter.createValueListener()}
                                                         selectionProvider={selectionAdapter}
                                                         navTarget={this.props.navTarget}/>

                    const panel = <CvGraphPanel graphContext={graphContext}
                                            navigationListeners={this.props.navigationListeners}
                                            selectionListener={selectionAdapter.createValueListener()}
                                            actionProvider={actionHandlerAdapter}
                                            actionListeners={this.props.actionListeners}
                                            stateChangeListeners={[stateChangeListener]}/>

                    return <CvFormItemPanel paneContext={graphContext} displayElement={panel} menuElement={menu} toolbarElement={toolbarMenu} index={index}/>
                });

            },

            getListComponentRenderer: () => {
                return (customProvider && customProvider.getListComponentRenderer) ? customProvider.getListComponentRenderer() :
                    
                    ((listContext:ListContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {

                    const displayProps:CvDisplayProperties = this.props.displayProperties;
                    const topMenuDef = (listContext.menuDefs && listContext.menuDefs.length > 0) ? listContext.menuDefs[0] : null;
                    const contextMenuDef = topMenuDef ? topMenuDef.findContextMenuDef() : null;
                    const selectionAdapter:CvValueAdapter<Array<string>> = new CvValueAdapter<Array<string>>();
                    const actionHandlerAdapter:CvValueAdapter<CvActionHandlerParams> = new CvValueAdapter<CvActionHandlerParams>();
                    const VIEW_ID_PROP_NAME = listContext.paneRef + '_' + DISPLAY_PROP_VIEW_ID;
                    const viewId = displayProps.getProperty(VIEW_ID_PROP_NAME);
                    const LIST_SELECTION_PROP_NAME = listContext.paneRef + '_' + DISPLAY_PROP_LIST_SELECTION;
                    const selectedItems = displayProps.getProperty(LIST_SELECTION_PROP_NAME);
                    const NUM_ROWS_PROP_NAME = listContext.paneRef + '_' + DISPLAY_PROP_LIST_TOTAL_ROWS;
                    const initialRows = displayProps.getProperty(NUM_ROWS_PROP_NAME) ? Number(displayProps.getProperty(NUM_ROWS_PROP_NAME)) : null;
                    const FIRST_ROW_PROP_NAME = listContext.paneRef + '_' + DISPLAY_PROP_LIST_FIRST_VISIBLE_ROW;
                    const firstVisibleRow = displayProps.getProperty(FIRST_ROW_PROP_NAME) ? Number(displayProps.getProperty(FIRST_ROW_PROP_NAME)) : null;

                    const menu = <CvDropdownMenu paneContext={listContext}
                                                 menuDef={contextMenuDef}
                                                 openLeft={true}
                                                 navigationListeners={this.props.navigationListeners}
                                                 actionListeners={this.props.actionListeners}
                                                 stateChangeListeners={[stateChangeListener]}
                                                 navTarget={this.props.navTarget}
                                                 actionHandler={actionHandlerAdapter.createValueListener()}
                                                 selectionProvider={selectionAdapter}/>

                    const toolbarMenu =  <CvToolbarMenu paneContext={listContext}
                                                         menuDef={topMenuDef}
                                                         openLeft={true}
                                                         navigationListeners={this.props.navigationListeners}
                                                         actionListeners={this.props.actionListeners}
                                                         stateChangeListeners={[stateChangeListener]}
                                                         actionHandler={actionHandlerAdapter.createValueListener()}
                                                         selectionProvider={selectionAdapter}
                                                         navTarget={this.props.navTarget}/>

                    const viewMenu = <CvDropdownViewMenu paneContext={listContext}
                                                     selectedViewId={viewId}
                                                     actionListeners={this.props.actionListeners}
                                                     navigationListeners={this.props.navigationListeners}
                                                     viewDescs={listContext.viewDescs}
                                                     navTarget={this.props.navTarget}
                                                     viewSelectionListener={(viewId:string)=>{
                                                         //new view, reset all display props
                                                         this.props.displayProperties.getAndRemove(NUM_ROWS_PROP_NAME)
                                                             .getAndRemove(LIST_SELECTION_PROP_NAME)
                                                             .getAndRemove(FIRST_ROW_PROP_NAME)
                                                             .getAndUpdate(VIEW_ID_PROP_NAME, viewId)
                                                             .publishToListeners(this.props.displayPropChangeListeners);
                                                     }}
                                                     openLeft={true}/>

                    const selectionAdapterDelegate = selectionAdapter.createValueListener(); //use this to notify the adapter, which in turn notifies it's subscribers (ie the menu)
                    const gridListener = (event:CvGridEvent<any>)  => {
                        const displayProps = this.props.displayProperties;
                        if(event.type === CvGridEventType.SELECTION_EVENT) {
                            const selections:Array<string> = event.eventObj;
                            selectionAdapterDelegate(selections);  //notify the menu
                            displayProps.getAndUpdate(LIST_SELECTION_PROP_NAME, selections).publishToListeners(this.props.displayPropChangeListeners);
                        } else if(event.type === CvGridEventType.TOTAL_ROWS_LOADED_EVENT) {
                            displayProps.getAndUpdate(NUM_ROWS_PROP_NAME, String(event.eventObj)).publishToListeners(this.props.displayPropChangeListeners);
                        } else if(event.type === CvGridEventType.FIRST_VISIBLE_ROW_CHANGE_EVENT) {
                            displayProps.getAndUpdate(FIRST_ROW_PROP_NAME, String(event.eventObj)).publishToListeners(this.props.displayPropChangeListeners);
                        }
                    }

                    //const panel = <CvListPanel listContext={listContext}
                    const panel = <CvGridPanel listContext={listContext}
                                               initialSelectedItems={selectedItems}
                                               numInitialRows={initialRows}
                                               initialFirstVisibleRow={firstVisibleRow}
                                               navigationListeners={this.props.navigationListeners}
                                               actionListeners={this.props.actionListeners}
                                               gridListener={gridListener}
                                               actionProvider={actionHandlerAdapter}
                                               stateChangeListeners={[stateChangeListener]}
                                               searchable={contextMenuDef && !!(contextMenuDef.findAtId('#search'))}/>

                    //using unique key here based on the viewId, hints to React that this this be a new instance of this component
                    const key = viewId ? listContext.paneRef + '_' + viewId : listContext.paneRef;
                    return <CvFormItemPanel paneContext={listContext} displayElement={panel} menuElement={menu} toolbarElement={toolbarMenu} viewMenuElement={viewMenu} key={key} index={index}/>
                });
            },

            getMapComponentRenderer: () => {
                return (customProvider && customProvider.getMapComponentRenderer) ? customProvider.getMapComponentRenderer() :
                    
                    ((mapContext:MapContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {

                        const topMenuDef = (mapContext.menuDefs && mapContext.menuDefs.length > 0) ? mapContext.menuDefs[0] : null;
                        const contextMenuDef = topMenuDef ? topMenuDef.findContextMenuDef() : null;
                    const selectionAdapter:CvValueAdapter<Array<string>> = new CvValueAdapter<Array<string>>();
                    const actionHandlerAdapter:CvValueAdapter<CvActionHandlerParams> = new CvValueAdapter<CvActionHandlerParams>();

                    const menu = <CvDropdownMenu paneContext={mapContext}
                                                 menuDef={contextMenuDef}
                                                 openLeft={true}
                                                 navigationListeners={this.props.navigationListeners}
                                                 actionListeners={this.props.actionListeners}
                                                 stateChangeListeners={[stateChangeListener]}
                                                 navTarget={this.props.navTarget}
                                                 actionHandler={actionHandlerAdapter.createValueListener()}
                                                 selectionProvider={selectionAdapter}/>

                    const toolbarMenu =  <CvToolbarMenu paneContext={mapContext}
                                                         menuDef={topMenuDef}
                                                         openLeft={true}
                                                         navigationListeners={this.props.navigationListeners}
                                                         actionListeners={this.props.actionListeners}
                                                         stateChangeListeners={[stateChangeListener]}
                                                         actionHandler={actionHandlerAdapter.createValueListener()}
                                                         selectionProvider={selectionAdapter}
                                                         navTarget={this.props.navTarget}/>

                    const panel = <CvMapPanel mapContext={mapContext}
                                          navigationListeners={this.props.navigationListeners}
                                          selectionListener={selectionAdapter.createValueListener()}
                                          actionProvider={actionHandlerAdapter}
                                          actionListeners={this.props.actionListeners}
                                          stateChangeListeners={[stateChangeListener]}/>

                    return <CvFormItemPanel paneContext={mapContext} displayElement={panel} menuElement={menu} toolbarElement={toolbarMenu} index={index}/>
                });
            },


            getPrintMarkupComponentRenderer: () => {
                return (customProvider && customProvider.getPrintMarkupComponentRenderer) ? customProvider.getPrintMarkupComponentRenderer() :

                    ((printMarkupContext:PrintMarkupContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        const topMenuDef = (printMarkupContext.menuDefs && printMarkupContext.menuDefs.length > 0) ? printMarkupContext.menuDefs[0] : null;
                        const contextMenuDef = topMenuDef ? topMenuDef.findContextMenuDef() : null;
                        const actionHandlerAdapter:CvValueAdapter<CvActionHandlerParams> = new CvValueAdapter<CvActionHandlerParams>();

                        const menu =  <CvDropdownMenu paneContext={printMarkupContext}
                                                      menuDef={contextMenuDef}
                                                      openLeft={true}
                                                      navigationListeners={this.props.navigationListeners}
                                                      actionListeners={this.props.actionListeners}
                                                      stateChangeListeners={[stateChangeListener]}
                                                      actionHandler={actionHandlerAdapter.createValueListener()}
                                                      navTarget={this.props.navTarget}/>

                        const toolbarMenu =  <CvToolbarMenu paneContext={printMarkupContext}
                                                             menuDef={topMenuDef}
                                                             openLeft={true}
                                                             navigationListeners={this.props.navigationListeners}
                                                             actionListeners={this.props.actionListeners}
                                                             stateChangeListeners={[stateChangeListener]}
                                                             actionHandler={actionHandlerAdapter.createValueListener()}
                                                             navTarget={this.props.navTarget}/>

                        let panel = <CvPrintPanel printMarkupContext={printMarkupContext}
                                                    navigationListeners={this.props.navigationListeners}
                                                    stateChangeListeners={[stateChangeListener]}
                                                    actionListeners={this.props.actionListeners}
                                                    actionProvider={actionHandlerAdapter}/>

                        return <CvFormItemPanel paneContext={printMarkupContext} displayElement={panel} menuElement={menu} toolbarElement={toolbarMenu} index={index}/>
                    });
            },

            /**
             * Pending implementation
             * @private
             */
            getCalendarComponentRenderer: () => {
                return (customProvider && customProvider.getCalendarComponentRenderer) ? customProvider.getCalendarComponentRenderer() :
                    ((calendarContext:CalendarContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        return <h2>There is not yet a default Calendar Implementation. You can provide one, if you like.</h2>;
                    });
            },

            /**
             * Pending implementation
             * @private
             */
            getImagePickerComponentRenderer: () => {
                return (customProvider && customProvider.getImagePickerComponentRenderer) ? customProvider.getImagePickerComponentRenderer() :
                    ((imagePickerContext:ImagePickerContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        return <h2>There is not yet a default ImagePicker Implementation. You can provide one, if you like.</h2>;
                    });
            },

            /**
             * Pending implementation
             * @private
             */
            getBarcodeScanComponentRenderer: () => {
                return (customProvider && customProvider.getBarcodeScanComponentRenderer) ? customProvider.getBarcodeScanComponentRenderer() :
                    ((barcodeScanContext:BarcodeScanContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        return <h2>There is not yet a default BarcodeScan Implementation. You can provide one, if you like.</h2>;
                    });
            },

            /**
             * Pending implementation
             * @private
             */
            getGeoFixComponentRenderer: () => {
                return (customProvider && customProvider.getGeoFixComponentRenderer) ? customProvider.getGeoFixComponentRenderer() :
                    ((geoFixContext:GeoFixContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        return <h2>There is not yet a default GeoFix Implementation. You can provide one, if you like.</h2>;
                    });
            },

            /**
             * Pending implementation
             * @private
             */
            getGeoLocationComponentRenderer: () => {
                return (customProvider && customProvider.getGeoLocationComponentRenderer) ? customProvider.getGeoLocationComponentRenderer() :
                    ((geoLocationContext:GeoLocationContext, stateChangeListener:(event:CvEvent<CvStateChangeResult>)=>void, index:number)=> {
                        return <h2>There is not yet a default GeoLocation Implementation. You can provide one, if you like.</h2>;
                    });
            }
        }
    },

    /**
     *
     * @param childComponents These are the components which have been rendered by the various child component functions
     * @returns {any}
     * @private
     */
    _getFormLayoutComponent: function (childComponents:Array<ReactElement<any>>):ReactElement<any> {

        const formContext:FormContext = this.formContext();
        const formDef:FormDef = this.formContext().formDef;
        const displayProperties:CvDisplayProperties = this.props.displayProperties;
        let formLayoutType = displayProperties.getProperty(DISPLAY_PROP_FORM_LAYOUT);
        if(!formLayoutType) {
            if (formDef.isTabsLayout) {
                formLayoutType = 'tab';
            } else if (formDef.isThreeBoxOneLeftLayout) {
                formLayoutType = 'rsplit';
            } else if (formDef.isThreeBoxOneRightLayout) {
                formLayoutType = 'lsplit';
            } else if (formDef.isThreeBoxOneOverLayout) {
                formLayoutType = 'bsplit';
            } else if (formDef.isThreeBoxOneUnderLayout || formDef.isTwoVerticalLayout) {
                formLayoutType = 'tsplit';
            } else if (formDef.isFourBoxSquareLayout) {
                formLayoutType = '4box';
            } else if (formDef.isOptionsFormLayout) {
                formLayoutType = 'opt';
            } else {
                formLayoutType = 'vert';
            }
        }

        let component = null;

        if (this.props.layoutOverrideElem) {

            const props = {
                formContext: formContext,
                formComponents: childComponents,
                navigationListeners: this.props.navigationListeners,
                actionListeners: this.props.actionListeners,
                navTarget: this.props.navTarget
            };

            component = React.createElement(this.props.layoutOverrideElem, props);

        } else if (formLayoutType === 'tab') {
            component = <CvTabbedFormPanel formContext={formContext}
                                      formComponents={childComponents}
                                      navigationListeners={this.props.navigationListeners}
                                      actionListeners={this.props.actionListeners}
                                      navTarget={this.props.navTarget}
                                      displayProperties={displayProperties}
                                      displayPropChangeListeners={this.props.displayPropChangeListeners}
            />
        } else if (formLayoutType === 'rsplit') {
            component = <CvFormRightSplitPane formContext={formContext}
                                         formComponents={childComponents}
                                         navigationListeners={this.props.navigationListeners}
                                         actionListeners={this.props.actionListeners}
                                         navTarget={this.props.navTarget}
            />
        } else if (formLayoutType == 'lsplit') {
            component = <CvFormLeftSplitPane formContext={formContext}
                                        formComponents={childComponents}
                                        navigationListeners={this.props.navigationListeners}
                                        actionListeners={this.props.actionListeners}
                                        navTarget={this.props.navTarget}
            />
        } else if (formLayoutType == 'bsplit') {
            component = <CvFormBottomSplitPane formContext={formContext}
                                          formComponents={childComponents}
                                          navigationListeners={this.props.navigationListeners}
                                          actionListeners={this.props.actionListeners}
                                          navTarget={this.props.navTarget}
            />
        } else if (formLayoutType == 'tsplit') {
            component = <CvFormTopSplitPane formContext={formContext}
                                       formComponents={childComponents}
                                       navigationListeners={this.props.navigationListeners}
                                       actionListeners={this.props.actionListeners}
                                       navTarget={this.props.navTarget}
            />
        } else if (formLayoutType == '4box') {
            component = <CvFormFourBoxSplitPane formContext={formContext}
                                           formComponents={childComponents}
                                           navigationListeners={this.props.navigationListeners}
                                           actionListeners={this.props.actionListeners}
                                           navTarget={this.props.navTarget}
            />
        } else if (formLayoutType == 'opt') {
            component = <CvFormOptionsLayoutPane formContext={formContext}
                                            formComponents={childComponents}
                                            navigationListeners={this.props.navigationListeners}
                                            actionListeners={this.props.actionListeners}
                                            navTarget={this.props.navTarget}
            />
        } else {
            component = <CvVerticalLayoutFormPane formContext={formContext}
                                             formComponents={childComponents}
                                             navigationListeners={this.props.navigationListeners}
                                             actionListeners={this.props.actionListeners}
                                             navTarget={this.props.navTarget}
            />
        }
        const topMenuDef = (formContext.menuDefs && formContext.menuDefs.length > 0) ? formContext.menuDefs[0] : null;
        const menuDef = (formContext.menuDefs && formContext.menuDefs.length > 0) ? formContext.menuDefs[0].findContextMenuDef() : null;
        const actionHandlerAdapter:CvValueAdapter<CvActionHandlerParams> = new CvValueAdapter<CvActionHandlerParams>();

        const layoutMenu = <CvFormLayoutMenu selectedItem={displayProperties.getProperty(DISPLAY_PROP_FORM_LAYOUT)}
                                             displayProperties={displayProperties}
                                             displayPropChangeListeners={this.props.displayPropChangeListeners}
                                             openLeft={true}/>;
        
        const viewMenu = <CvDropdownViewMenu paneContext={formContext}
                                             actionListeners={this.props.actionListeners}
                                             navigationListeners={this.props.navigationListeners}
                                             navTarget={this.props.navTarget}
                                             viewDescs={formContext.viewDescs}
                                             openLeft={true}/>
        const formMenu =  <CvDropdownMenu paneContext={formContext}
                                      menuDef={menuDef}
                                      openLeft={false}
                                      navigationListeners={this.props.navigationListeners}
                                      actionListeners={this.props.actionListeners}
                                      stateChangeListeners={[]}
                                      actionHandler={actionHandlerAdapter.createValueListener()}
                                      navTarget={this.props.navTarget}/>

        const toolbarMenu =  <CvToolbarMenu paneContext={formContext}
                                            menuDef={topMenuDef}
                                            openLeft={true}
                                            navigationListeners={this.props.navigationListeners}
                                            actionListeners={this.props.actionListeners}
                                            stateChangeListeners={[]}
                                            actionHandler={actionHandlerAdapter.createValueListener()}
                                            navTarget={this.props.navTarget}/>


        const paneTitle = this.findPaneTitle(formContext);
        
        return  (
            <div className="cv-form">
                <div className="row cv-form-header">
                   <div style={{display:"inline"}}>
                       <span className="pull-left cv-form-title">{paneTitle && paneTitle !== 'Default' ? paneTitle : ''}</span>
                       <div className="pull-left cv-form-header-toolbar">
                            {toolbarMenu}
                       </div>
                       {(()=>{
                           return menuDef == null ? null :
                               <button className="pull-left btn btn-xs cv-toolbar-button" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
                                   <div className="cv-form-menu pull-left">{formMenu}</div>
                               </button>
                       })()}
                   </div>
                   <div className="cv-form-menu pull-right">{viewMenu}</div>
                   <div className="cv-form-menu pull-right">{layoutMenu}</div>
                </div>
               <div className="cv-form-wrapper">{component}</div>
            </div>
        );
    }
});

export var CvFormLayoutMenu = React.createClass<{
        selectedItem:string,
        displayProperties:CvDisplayProperties,
        openLeft:boolean,
        displayPropChangeListeners?:Array<(displayProperties:CvDisplayProperties)=>void>
    },{}>({

    getDefaultProps: function() {
        return {
            selectedItem:null,
            displayProperties:null,
            displayPropChangeListeners:[],
            openLeft:false
        };
    },

    render: function () {

        const layouts = ['tab','rsplit','lsplit','bsplit','tsplit','4box','vert'];

        let layoutItemClassName = "dropdown-menu cv-dropdown-menu";
        if (this.props.openLeft) layoutItemClassName += ' dropdown-menu-right';

        return <span className="dropdown cv-layout-dropdown">
                    <button className="btn btn-xs" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
                        <span className="glyphicon glyphicon-th" aria-hidden="true"/><span className="caret"/>
                    </button>
                    <ul className={layoutItemClassName}>
                        {layouts.map(layout=>
                            <li onClick={()=>{
                                this.props.displayProperties.getAndUpdate(DISPLAY_PROP_FORM_LAYOUT, layout)
                                    .publishToListeners(this.props.displayPropChangeListeners);
                            }}><a className="cv-target"><div className={'cv-layout-item cv-layout-' + layout}/></a></li>
                        )}
                    </ul>
                </span>
    }
});


/* Base Form Item Panel */

export interface CvFormItemPanelProps extends CvProps {
    displayElement:ReactElement<any>;
    menuElement:ReactElement<any>;
    menuElementClassName?:string;
    viewMenuElement?:ReactElement<any>;
    toolbarElement:ReactElement<any>;
    toolbarElementClassName?:string;
    paneContext:PaneContext;
    initOpen?:boolean;
    index?:number;
}

export interface CvFormItemPanelState extends CvProps {
    // ensure that the requested view is loaded before displaying the element
    viewLoaded:string;
}

export var CvFormItemPanel = React.createClass<CvFormItemPanelProps, CvState>({

    mixins: [CvBaseMixin],

    componentWillMount: function() {
      if(!this.props.viewMenuElement) {
          this.setState({viewLoaded:'default_view'});
      }
    },

    getDefaultProps: function () {
        return {displayElement: null, paneContext: null, menuElement: null, viewMenuElement: null, toolbarElement:null, initOpen:true, index:1}
    },

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

    render: function () {

        if (this.props.paneContext && this.props.displayElement) {
            const paneId = (this.props.paneContext as PaneContext).paneDef.paneId;
            const zindex = 100 - this.props.index;
            const menuClassName = "pull-right cv-panel-menu-item " + (this.props.menuElementClassName ? this.props.menuElementClassName : "");
            const toolClassName = "pull-right cv-panel-heading-toolbar " + (this.props.toolbarElementClassName ? this.props.toolbarElementClassName : "");
            return (
                <div className="cv-panel panel panel-primary animated fadeIn" style={{position: 'relative', zIndex: zindex}}>
                    <div className="cv-panel-heading panel-heading">
                        <div className="cv-panel-heading-title">
                            <span className="cv-panel-title">{this.findPaneTitle(this.props.paneContext)}</span>
                            {/*
                                //collapsible panels removed due to layout redesign
                                //this no longer works due to the requirement that all containers must use 100% height
                                <a role="button" className="cv-panel-toggle" data-toggle="collapse" href={'#'+paneId}
                                   aria-expanded="true" aria-controls={paneId}>
                                </a>
                             */}
                        </div>
                        <div className="cv-panel-heading-menu">
                            <div className={menuClassName}>{this.props.menuElement}</div>
                            <div className="pull-right cv-panel-menu-view">
                                {this.props.viewMenuElement ? React.cloneElement(this.props.viewMenuElement, {viewLoadedListener: this._viewLoaded}) : null}
                            </div>
                            <div className={toolClassName}>
                                {this.props.toolbarElement}
                            </div>
                        </div>
                    </div>
                    <div className={'cv-panel-collapse ' + (this.props.initOpen ? 'collapse in' : 'collapse')} id={paneId}>
                        <div className="cv-panel-body">
                            {this.state.viewLoaded ? this.props.displayElement : null}
                        </div>
                    </div>
                </div>
            );
        } else {
            return null;
        }
    },

    _viewLoaded: function(viewId:string) {
        if(!this.state.viewLoaded || this.state.viewLoaded != viewId) {
            this.setState({viewLoaded: viewId});
        }
    }
});


/* Base Mixin for Form layouts */

export interface CvFormLayoutState extends CvState {
}

export interface CvFormLayoutProps extends CvProps {
    formComponents:Array<ReactElement<any>>;
    formContext:FormContext;
    navigationListeners?:Array<(event:CvEvent<CvNavigationResult>)=>void>;
    actionListeners?:Array<(event:CvEvent<CvActionFiredResult>)=>void>;
    navTarget?:string;
    displayProperties?:CvDisplayProperties;
    displayPropChangeListeners?:Array<(displayProperties:CvDisplayProperties)=>void>;
}

export var CvFormLayout = {
    
    displayProperties: function():CvDisplayProperties {
        return this.props.displayProperties;
    },

    formComponents: function ():Array<ReactElement<any>> {
        return this.props.formComponents;
    },

    formContext: function ():FormContext {
        return this.props.formContext;
    }

}

/**
 *  Tabbed Form Layout
 */

/* Tabbed Menu */

export interface CvTabbedFormMenuProps extends CvFormLayoutProps {
    tabSelectionListener:(selectedItem:number)=>void;
    selectedItem:number;
}

export var CvTabbedFormMenu = React.createClass<CvTabbedFormMenuProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],
    
    getDefaultProps: function () {
        return {formComponents: [], tabSelectionListener: null, formContext: null, selectedItem:0};
    },
    
    render: function () {
        if (this.formComponents() && this.formComponents().length > 0) {
            return <ul className="nav nav-pills">
                {this.formContext().childrenContexts.map((childContext:PaneContext, i:number)=> {
                    let title = this.findPaneTitle(childContext);
                    if (!title || title.length < 1) {
                        title = 'View ' + (i + 1);
                    }
                    return <li onClick={this._itemSelected.bind(this, i)} role="presentation" key={i}
                               className={i == this.props.selectedItem ? 'active' : ''}>
                        <a className="cv-target">{title}</a>
                    </li>
                })}
            </ul>
        } else {
            return null;
        }
    },

    _itemSelected(item:number) {
        this.props.tabSelectionListener(item);
    }

});

export interface CvTabbedFormPanelProps extends CvFormLayoutProps {
}

/* Tabbed Panel */

export var CvTabbedFormPanel = React.createClass<CvTabbedFormPanelProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null,
            displayProperties:new CvDisplayProperties(), displayPropChangeListeners:[]};
    },

    render: function () {
        if (this.formComponents() && this.formComponents().length > 0) {
            if (this.formComponents().length > 1) {
                return (
                    <div className="cv-form-container">
                        <div className="cv-tab-form-menu">
                            <CvTabbedFormMenu formContext={this.formContext()}
                                              formComponents={this.formComponents()}
                                              selectedItem={this.selectedItem()}
                                              tabSelectionListener={(selectedItem:number)=>{
                                                this.displayProperties().getAndUpdate(DISPLAY_PROP_ACTIVE_FORM_TAB, selectedItem)
                                                    .publishToListeners(this.props.displayPropChangeListeners);
                                              }
                            }/>
                        </div>    
                        <div className="cv-tab-form-body">{this.selectedElement()}</div>
                    </div>
                );
            } else {
                return <div className="cv-form-container">
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {this.formComponents()[0]}
                            </div>
                        </div>
                    </div>
            }
        } else {
            return null;
        }
    },

    selectedElement: function () {
        return this.formComponents()[this.selectedItem()];
    },

    selectedContext: function () {
        return this.formContext().childrenContexts[this.selectedItem()];
    },
    
    selectedItem: function():number {
        const activeFormTab = this.displayProperties().getProperty(DISPLAY_PROP_ACTIVE_FORM_TAB);
        return activeFormTab ? Number(activeFormTab) : 0;
    }

});


/* Options Form Layout */

export var CvFormOptionsLayoutPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {

        const formComponents = this.formComponents();
        if (formComponents && formComponents.length > 0) {
            if (formComponents.length < 2) {
                return (<div className="cv-form-container">
                    <div className="row cv-form-row">
                        <div className="col-sm-12 cv-form-col">
                            {formComponents[0]}
                        </div>
                    </div>
                </div>);
            } else {
                return (
                    <div className="cv-form-container">
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-option-pane">
                                {formComponents[0]}
                            </div>
                        </div>
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {(()=>{
                                    const elems = [];
                                    for(let i = 1; i < formComponents.length; i++){
                                        elems.push(
                                        <div className="row cv-form-row">
                                            <div className="col-sm-12 cv-form-col">
                                                {formComponents[i]}
                                            </div>
                                        </div>
                                            );
                                        }
                                    return elems;
                                    })()}
                            </div>
                        </div>
                    </div>);
            }
        } else {
            return null;
        }
    },

});

/* 3, 1 left Layout */

export var CvFormRightSplitPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {

        const formComponents = this.formComponents();
        if (formComponents && formComponents.length > 0) {
            if (formComponents.length < 2) {
                return (<div className="cv-form-container">
                    <div className="row cv-form-row">
                        <div className="col-sm-12 cv-form-col">
                            {formComponents[0]}
                        </div>
                    </div>
                </div>);
            } else {
                return (
                    <div className="cv-form-container">
                        <div className="row cv-form-row">
                            <div className="col-sm-6 cv-form-col">
                                {formComponents[0]}
                            </div>
                            <div className="col-sm-6 cv-form-col">
                                <div className="row cv-form-row-split">
                                    <div className="col-sm-12 cv-form-col">
                                        {formComponents[1]}
                                    </div>
                                </div>
                                <div className="row cv-form-row-split">
                                    <div className="col-sm-12 cv-form-col">
                                        {formComponents.length > 2 ? formComponents[2] : null}
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {(()=>{
                                    const elems = [];
                                    for(let i = 3; i < formComponents.length; i++){
                                        elems.push(
                                        <div className="row cv-form-row">
                                            <div className="col-sm-12 cv-form-col">
                                                {formComponents[i]}
                                            </div>
                                        </div>
                                            );
                                        }
                                    return elems;
                                    })()}
                            </div>
                        </div>
                    </div>);
            }
        } else {
            return null;
        }
    },

});

/* 3, 1 right Layout */

export var CvFormLeftSplitPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {

        const formComponents = this.formComponents();
        if (formComponents && formComponents.length > 0) {
            if (formComponents.length < 2) {
                return (<div className="cv-form-container">
                    <div className="row cv-form-row">
                        <div className="col-sm-12 cv-form-col">
                            {formComponents[0]}
                        </div>
                    </div>
                </div>);
            } else {
                return (
                    <div className="cv-form-container">
                        <div className="row cv-form-row">
                            <div className="col-sm-6 cv-form-col">
                                <div className="row cv-form-row-split">
                                    <div className="col-sm-12 cv-form-col">
                                        {formComponents[0]}
                                    </div>
                                </div>
                                <div className="row cv-form-row-split">
                                    <div className="col-sm-12 cv-form-col">
                                        {formComponents[1]}
                                    </div>
                                </div>
                            </div>
                            <div className="col-sm-6 cv-form-col">
                                {formComponents.length > 2 ? formComponents[2] : null}
                            </div>
                        </div>
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {(()=>{
                                    const elems = [];
                                    for(let i = 3; i < formComponents.length; i++){
                                        elems.push(
                                        <div className="row cv-form-row">
                                            <div className="col-sm-12 cv-form-col">
                                                {formComponents[i]}
                                            </div>
                                        </div>
                                            );
                                        }
                                    return elems;
                                    })()}
                            </div>
                        </div>
                    </div>);
            }
        } else {
            return null;
        }
    },

});

/* 3, 1 over Layout */

export var CvFormBottomSplitPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {

        const formComponents = this.formComponents();
        if (formComponents && formComponents.length > 0) {
            if (formComponents.length < 2) {
                return (<div className="cv-form-container">
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {formComponents[0]}
                            </div>
                        </div>
                </div>);
            } else {
                return (
                    <div className="cv-form-container">
                        <div className="row cv-form-row-split">
                            <div className="col-sm-12 cv-form-col">
                                {formComponents[0]}
                            </div>
                        </div>
                        <div className="row cv-form-row-split">
                            <div className="col-sm-6 cv-form-col">
                                {formComponents[1]}
                            </div>
                            <div className="col-sm-6 cv-form-col">
                                {formComponents.length > 2 ? formComponents[2] : null}
                            </div>
                        </div>
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {(()=>{
                                    const elems = [];
                                    for(let i = 3; i < formComponents.length; i++){
                                        elems.push(
                                        <div className="row cv-form-row">
                                            <div className="col-sm-12 cv-form-col">
                                                {formComponents[i]}
                                            </div>
                                        </div>
                                            );
                                        }
                                    return elems;
                                    })()}
                            </div>
                        </div>
                    </div>);
            }
        } else {
            return null;
        }
    },

});

/* 3, 1 under Layout */

export var CvFormTopSplitPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {

        const formComponents = this.formComponents();
        if (formComponents && formComponents.length > 0) {
            if (formComponents.length < 2) {
                return (<div className="cv-form-container">
                            <div className="row cv-form-row">
                                <div className="col-sm-12 cv-form-col">
                                    {formComponents[0]}
                                </div>
                            </div>
                </div>);
            } else {
                return (
                    <div className="cv-form-container">
                        <div className="row cv-form-row-split">
                            <div className="col-sm-6 cv-form-col">
                                {formComponents[0]}
                            </div>
                            <div className="col-sm-6 cv-form-col">
                                {formComponents[1]}
                            </div>
                        </div>
                        <div className="row cv-form-row-split">
                            <div className="col-sm-12 cv-form-col">
                                {formComponents.length > 2 ? formComponents[2] : null}
                            </div>
                        </div>
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {(()=>{
                                    const elems = [];
                                    for(let i = 3; i < formComponents.length; i++){
                                        elems.push(
                                        <div className="row cv-form-row">
                                            <div className="col-sm-12 cv-form-col">
                                                {formComponents[i]}
                                            </div>
                                        </div>
                                            );
                                        }
                                    return elems;
                                    })()}
                            </div>
                        </div>
                    </div>);
            }
        } else {
            return null;
        }
    },

});

/* 4 box Layout */

export var CvFormFourBoxSplitPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {

        const formComponents = this.formComponents();
        if (formComponents && formComponents.length > 0) {
            if (formComponents.length < 2) {
                return (<div className="cv-form-container">
                            <div className="row cv-form-row">
                                <div className="col-sm-12 cv-form-col">
                                    {formComponents[0]}
                                </div>
                            </div>
                </div>);
            } else {
                return (
                    <div className="cv-form-container">
                        <div className="row cv-form-row-split">
                            <div className="col-sm-6 cv-form-col">
                                {formComponents[0]}
                            </div>
                            <div className="col-sm-6 cv-form-col">
                                {formComponents[1]}
                            </div>
                        </div>
                        <div className="row cv-form-row-split">
                            <div className="col-sm-6 cv-form-col">
                                {formComponents.length > 2 ? formComponents[2] : null}
                            </div>
                            <div className="col-sm-6 cv-form-col">
                                {formComponents.length > 3 ? formComponents[3] : null}
                            </div>
                        </div>
                        <div className="row cv-form-row">
                            <div className="col-sm-12 cv-form-col">
                                {(()=>{
                                    const elems = [];
                                    for(let i = 4; i < formComponents.length; i++){
                                        elems.push(
                                        <div className="row cv-form-row" key={i}>
                                            <div className="col-sm-12 cv-form-col">
                                                {formComponents[i]}
                                            </div>
                                        </div>
                                            );
                                        }
                                    return elems;
                                    })()}
                            </div>
                        </div>
                    </div>);
            }
        } else {
            return null;
        }
    },

});


/* Vertical Layout */

export var CvVerticalLayoutFormPane = React.createClass<CvFormLayoutProps, CvFormLayoutState>({

    mixins: [CvBaseMixin, CvFormLayout],

    getDefaultProps: function () {
        return {formComponents: [], formContext: null}
    },

    render: function () {
        if (this.formComponents() && this.formComponents().length > 0) {
            return <div className="cv-form-container">
                        {this.formComponents().map((formComponent, num)=> {
                            return <div className="row cv-form-row">
                                <div className="col-sm-12 cv-form-col">
                                    {(formComponent.key) ? formComponent : React.cloneElement(formComponent, {key: num})}
                                </div>
                            </div>
                        })}
            </div>
        } else {
            return null;
        }
    },

});
