declare function saveAs(param1: any, param2: any): void;

namespace uniflash.views {
    'use strict';


    function createMainViewStateProvider($stateProvider: angular.ui.IStateProvider): angular.ui.IStateProvider {

        var stateProvider = $stateProvider;
        var mainViewStateProvider = stateProvider.state('main', {
            url: '/main/:deviceXMLFile/:connectionXMLFile',
            templateUrl: 'main/main.html',
            controller: MainStateController,
            controllerAs: 'vm',
            resolve: {
                sessionInit: sessionInit
            },
            params: {forceSessionInit: false, lp: false},
            abstract: true // never come here directly
        });
        
        // This is the resolve function it will make sure that main view will not render
        // until the session has been loaded
        sessionInit.$inject = ['ufSession', '$stateParams'];
        function sessionInit(ufSession: uniflash.session.SessionService, $stateParams: any, $location : angular.ILocationService) {
            return ufSession.startIfNeeded($stateParams.deviceXMLFile, $stateParams.connectionXMLFile , $stateParams.forceSessionInit, $stateParams.lp);
        }

        MainStateController.$inject = ['$state', 'ufTargetConfig', 'ufPropertiesDB', 'ufContext', 'ufConsoleInternal', 'ufDsProgressDialog', 'ufPropertiesValue', 'ufSession'];

        function MainStateController($state, ufTargetConfig, ufPropertiesDB, ufContext, ufConsoleInternal, ufDsProgressDialog, ufPropertiesValue, ufSession) {
            var vm = this;
            vm.ufPropertiesDB = ufPropertiesDB;
            vm.ufContext = ufContext;
            vm.ufConsoleInternal = ufConsoleInternal;
            vm.ufPropertiesValue = ufPropertiesValue;

            ufTargetConfig.getDevice($state.params.deviceXMLFile)
                .then(function(device) {
                    vm.deviceName = device.id;
                    return ufTargetConfig.getConnection(device.connectionIndices, $state.params.connectionXMLFile);
                }).then(function(connection) {
                    vm.connectionName = connection.id;
                });
                
            /*
            vm.serial;
            ufTargetConfig.getConfigurables($state.params.deviceXMLFile, $state.params.connectionXMLFile).then( function() {
                 if ( ufTargetConfig.getConfigurableObj('SEPK.POD_PORT') && (ufTargetConfig.getConfigurableObj('SEPK.POD_PORT').value === 'Select by serial number') ) {
                     vm.serial = ufTargetConfig.getConfigurableObj('SEPK.POD_PORT_SEPK.POD_SERIAL').value;
                 }
            });
            */
                
            vm.getDeviceInfo = function() {
                let dsCore = ufDsProgressDialog.getDSCore(ufContext.getCurrentCore().name);
                return dsCore.run('getTargetInfo');
            };
            
            vm.downloadCCXML = function() {
                let filename = vm.deviceName+'.ccxml';
                let blob = new Blob([ufSession.getCCXML()]);
                saveAs(blob, filename);
            };
        }

        return mainViewStateProvider;
    }
    
    // We need to implement a provider, so views can register at angular application init time
    // at this time various views can register themselves with us
    export class MainViewProvider implements angular.IServiceProvider {

        static $inject = ['$stateProvider'];
        private viewDescriptors: ViewDiscriptor[] = [];
        public mainViewStateProvider: ViewStateProvider;

        constructor($stateProvider: angular.ui.IStateProvider) {
            // create the main ( top level ) angular ui router view 
            this.mainViewStateProvider = new ViewStateProvider(createMainViewStateProvider($stateProvider), null);
        }

        $get = ['$state', 'ufSession', ($state: angular.ui.IStateService, ufSession: uniflash.session.SessionService, _: any) => {
            return new MainViewService($state, ufSession, this.mainViewStateProvider);
        }];
    }
    
    class ViewNodeLookup{
       private static viewNodesLookup : {[s:string]:ViewNode} = {}; 
       static add(viewType: uniflash.RegisteredViews , viewNode : ViewNode){
           var key = uniflash.RegisteredViews[viewType];
           ViewNodeLookup.viewNodesLookup[key] = viewNode;
       }
       static get(viewType: uniflash.RegisteredViews){
           var key = uniflash.RegisteredViews[viewType];
           return ViewNodeLookup.viewNodesLookup[key]; 
       }
    }
    
    // object to encapsulate angular ui router view state
    class ViewStateProvider {

        private childProviders: ViewStateProvider[] = [];
        constructor(private $stateProvider: angular.ui.IStateProvider, private viewDescriptor: ViewDiscriptor) {
        }
       
        //Add a view heirarchy , get back the added child node
        addViewState(viewToAdd: ViewDiscriptor): ViewStateProvider {
                  
            // register ui router views
            var internalStateProvider = this.$stateProvider.state(
                getState(viewToAdd.name), {
                    url: '/' + viewToAdd.name + (viewToAdd.isCoreSensitive ? '/:coreName' : ''),
                    templateUrl: viewToAdd.templateUrl,
                    controller: function() {
                        var vm = this;
                        vm.title = viewToAdd.title;
                    },
                    controllerAs: 'vm'
                }
            );

            var viewStateProvider = new ViewStateProvider(internalStateProvider, viewToAdd);
            this.childProviders.push(viewStateProvider);
            return viewStateProvider;
        }

        getViewNode(cores: uniflash.target_config.Core[], $state: angular.ui.IStateService): ViewNode {
            var viewNode: ViewNode;
            if (!this.viewDescriptor) {
                viewNode = new ViewNode(null, null, null);
            } else {
                viewNode = !this.viewDescriptor.isCoreSensitive ? createViewNode(this.viewDescriptor, $state)
                    : createCoreSensitiveViewNode(this.viewDescriptor, cores, $state);
            }

            _.each(this.childProviders, (childProvider) => {
                viewNode.childViewNodes.push(childProvider.getViewNode(cores, $state));
            });
            
            if(this.viewDescriptor) {
                ViewNodeLookup.add(this.viewDescriptor.viewType,viewNode);
            }
            
            return viewNode;
        }

    }
 
    interface ViewNodeClickedListener {
        (ViewNode) : void;
    }
    
    export class MainViewService {
        private viewNodeClickedListener : ViewNodeClickedListener;
        constructor(private $state: angular.ui.IStateService,
            private ufSession: uniflash.session.SessionService,
            private mainViewStateProvider: ViewStateProvider) {
        }
        
        // Based on the views, create navigational view nodes
        getMainViewNode(): ViewNode {
            return this.mainViewStateProvider.getViewNode(this.ufSession.queryConfig().cores, this.$state);
        }
        
        go(viewType : uniflash.RegisteredViews, coreName ){
            
            var viewNode = ViewNodeLookup.get(viewType);
           
           // assume only core sensetive views have children
            if( viewNode.childViewNodes.length > 0 ) {
               viewNode = coreName ?  _.findWhere(viewNode.childViewNodes,{displayName : coreName } )
                          : viewNode.childViewNodes[0];   
            }
            
           viewNode.clickHandler();
           this.viewNodeClickedListener(viewNode);
        }
        
        // register the navigation pane to be notified of view changes
        registerNavPaneListener(listener :ViewNodeClickedListener){
            this.viewNodeClickedListener = listener;
            return () => {
                //unregister
                this.viewNodeClickedListener = null;
            }
        }
    }

    function createViewNode(viewDescriptor: ViewDiscriptor, $state: angular.ui.IStateService): ViewNode {
        return new ViewNode(viewDescriptor.name
            , viewDescriptor.title
            , createClickHandler($state, getState(viewDescriptor.name)))
    }

    
    // A core sensetive view
    function createCoreSensitiveViewNode(viewDescriptor: ViewDiscriptor, cores: uniflash.target_config.Core[],
        $state: angular.ui.IStateService): ViewNode {

        var state = getState(viewDescriptor.name);

        var clickHandler = cores.length === 1 ?
            createClickHandler($state, state, cores[0].name) : null;


        var viewNode: ViewNode = new ViewNode(viewDescriptor.name, viewDescriptor.title, clickHandler);
        if (cores.length > 1) {
            _.each(cores, (core) => {
                var childViewNode = new ViewNode(core.name, core.name,
                        createClickHandler($state, state, core.name));
                childViewNode.level = 1;
                viewNode.childViewNodes.push(
                        childViewNode
                    );
            });
        }

        return viewNode;
    }

    export interface ViewDiscriptor {
        name: string;
        title: string;
        templateUrl: string;
        isCoreSensitive: boolean;
        viewType : uniflash.RegisteredViews;
    }

    interface ViewNodeClickHandler {
        (): void;
    }
            
    // This is the public interface used by the UI code
    // to render the views
    export class ViewNode {
        childViewNodes: ViewNode[] = [];
        active: boolean = false;
        level: number = 0;
        showView: boolean = true;
        constructor(public displayName: string, public title: string,
            public clickHandler: ViewNodeClickHandler = null) { // null for non clickable nodes  
        }

        hasChildren() {
            return this.childViewNodes.length > 0;
        }

    }

    function getState(name: string): string {
        return 'main.' + name;
    }

    function createClickHandler($state: angular.ui.IStateService, state: string, coreName: string = null): ViewNodeClickHandler {
        return () => {
            $state.go(state, coreName ? {
                coreName: coreName
            } : null);
        };
    }

    angular.module('uniflash').provider('ufMainView', MainViewProvider);
}