/*global TICloudAgent*/
namespace uniflash.target_config {
    'use strict';

    export interface DeviceSetup {
        id: string;
        xmlFile: string;
        connectionIndices: number[];
        defaultConnectionIndex?: number;
        showPreview: boolean;
    }

    export interface ConnectionSetup {
        id: string;
        xmlFile: string;
        connectionType: string;
    }

    export interface Core {
        name: string;
        pathName: string;
        isa: string;
    }

    export interface QueryConfigResult {
        deviceProperties?: any;
        connectionProperties?: any;
		cores: Core[]; // for GUI
		debuggableCores: Core[];
		nondebuggableCores: Core[];
        deviceXMLFile: string;
        connectionXMLFile: string;
    }

    export interface SetupData {
        devices: DeviceSetup[];
        connections: ConnectionSetup[];
    }

    export class TargetConfig {
        static $inject = ['ticAgentAPI','$q'];
        private cachedPromise;
        private configurables;
        private parsedConfigurables = [];

        constructor(private ticAgentAPI, private $q) {
        }

        getLaunchPadInfo() {
            return this.ticAgentAPI.getLpListJson()
                .then((response) => {
                    return JSON.parse(response.data);
                });
        }

        getLaunchPads() {
            return this.getLaunchPadInfo().then((data) => {
                return data.launchpads;
            });
        }

        getConfigInfo(): angular.IPromise<SetupData> {
            if (!this.cachedPromise) {
                this.cachedPromise = this.ticAgentAPI.getConfigInfo(TICloudAgent.getOS())
                    .then((response) => {
                        return JSON.parse(response.data);
                    });
            }
            return this.cachedPromise;
        }

        getDevices(): angular.IPromise<DeviceSetup[]> {
            return this.getConfigInfo()
                .then((data) => {
                    return data.devices;
                });
        }

        getDevice(deviceXMLFile: string): angular.IPromise<DeviceSetup> {
            return this.getDevices()
                .then((devices) => {
                    return _.find(devices, {
                        xmlFile: deviceXMLFile
                    });
                });
        }

        getConnection(connectionIndices, connectionXMLFile): angular.IPromise<ConnectionSetup> {
            return this.getConnections(connectionIndices)
                .then((connections) => {
                    return _.find(connections, {
                        xmlFile: connectionXMLFile
                    });
                });
        }

        getConnectionWithXML(connectionXMLFile): angular.IPromise<ConnectionSetup> {
            return this.getConnectionList()
                .then((connections) => {
                    return _.find(connections, {
                        xmlFile: connectionXMLFile
                    });
                });
        }

        getConnections(connectionIndices): angular.IPromise<ConnectionSetup[]> {
            return this.getConfigInfo()
                .then((data) => {
                    let connections = _.map(connectionIndices, (connectionIndex: any) => {
                        return data.connections[connectionIndex];
                    });
                    return connections;
                });
        }

        getConnectionList(): angular.IPromise<ConnectionSetup[]> {
            return this.getConfigInfo().then((data) => {
                return data.connections;
            });
        }

        getConnectionIndex(curConnection): angular.IPromise<number> {
            return this.getConnectionList().then((connections) => {
                return _.findIndex(connections, function (connection) {
                    return (connection.xmlFile === curConnection.xmlFile);
                });
            });
        }

        queryConfig(ccxmlFileContents): angular.IPromise<QueryConfigResult[]> {
            return this.ticAgentAPI.queryConfig(ccxmlFileContents, TICloudAgent.getOS())
                .then((response) => {
                    let configResult = JSON.parse(response.data);
                    // filter cores that should not be shown in uniflash (see UNIFLASH-421)
                    configResult.debuggableCores = _.filter(configResult.cores, (core: Core) => {
                        return core.isa.indexOf("cs_child") === -1 && core.isa.indexOf("AJSM") === -1;
					});
					
					configResult.nondebuggableCores = _.filter(configResult.cores, (core: Core) => {
						return core.isa.match(/TMS192C.*/) || core.isa.indexOf("cs_child") === 1 || core.isa.indexOf("AJSM") === 1;
					});

					configResult.cores = _.filter(configResult.cores, (core: Core) => {
						return !core.isa.match(/TMS192C.*/) && core.isa.indexOf("cs_child") === -1 && core.isa.indexOf("AJSM") === -1;
					});
                    return configResult;
                });
        }

        resetConfigurables() {
            this.configurables = undefined;
        }

        getConfigurables(deviceXMLFile, connectionXMLFile) {
            if ( !this.configurables ) {
                return this.ticAgentAPI.getConfigurables(TICloudAgent.getOS(), connectionXMLFile, deviceXMLFile)
                    .then((configurablesRes) => {
                        this.configurables = JSON.parse(configurablesRes.data);
                        this.parsedConfigurables = [];
                        this.parseConfigurables();
                });
            } else {
                return this.$q.resolve();
            }
        }
        
        parseConfigurables() {
            // we will only look at the first connection configurables for now
            _.each(this.configurables.connections[0].configurables, ( curConfigurable ) => {
                if ( curConfigurable.property ) {
                    this.parseCurConfigurable(curConfigurable.property.$,curConfigurable.property.choice,false);
                }
            });           
        }
        
        parseCurConfigurable(curConfigurable,curChoices,hidden,parent?,choiceValue?) {
            if ( curConfigurable.Type === 'hiddenfield' ) { return; }
            
            let id, name;
            if ( curConfigurable.desc ) { name = curConfigurable.desc; } 
            else if ( curConfigurable.Name ) { name = curConfigurable.Name; }
            
            if ( curConfigurable.id ) { id = curConfigurable.id; }
            else if ( curConfigurable.ID ) { id = curConfigurable.ID; }
            else { id = name; }
            
            // hide filepathfields for now since we can't handle browsing for cloud version yet.
            if ( !hidden && (curConfigurable.Type === 'hiddenfield') ) { hidden = true; }
            
            let curParsedConfigurable = {
                'id': id,
                'name': name,
                'type': curConfigurable.Type,
                'value': curConfigurable.Value,
                'choices': [],
                'hidden': hidden,
                'extensions': curConfigurable.Extensions?curConfigurable.Extensions:'',
                'rules': [],
                'children': [],
                'configurableObj': curConfigurable
            }
            
            // if this is a child property, add some rule information into the parent
            if ( parent ) {
                curParsedConfigurable.id = parent.id+'_'+choiceValue+'_'+curParsedConfigurable.id;
                parent.rules.push( {'id':curParsedConfigurable.id,'value':choiceValue} );
                parent.children.push( curParsedConfigurable );
                curParsedConfigurable.hidden = parent.hidden || !( parent.value === choiceValue );
            }
            
            this.parsedConfigurables.push(curParsedConfigurable);
            if ( curParsedConfigurable.type === 'choicelist' ) {
                let curValueObj = curChoices[parseInt(curConfigurable.Value)].$;
                if ( curValueObj.Name ) {
                    curParsedConfigurable.value = curValueObj.Name;
                } else {
                    curParsedConfigurable.value = curValueObj.value;
                }
                
                let choices = [];
                _.each(curChoices, (curChoice) => {
                    let choiceValue;
                    if ( curChoice.$.Name ) { choiceValue = curChoice.$.Name; }
                    else { choiceValue = curChoice.$.value; }
                    
                    choices.push(choiceValue);
                    
                    // parse properties into the choice
                    if ( curChoice.property ) {
                        _.each(curChoice.property, (curProp) => {
                            this.parseCurConfigurable(curProp.$,curProp.choice,hidden,curParsedConfigurable,choiceValue);
                        })
                    }
                });
                curParsedConfigurable.choices = choices;
            }
        }
        
        getConfigurablesList() {
            return this.configurables;
        }
        
        getParsedConfigurablesList() {
            return this.parsedConfigurables;
        }
        
        setConfigurablesValue(id:string,value:string) {
            let matchedConfigurable = _.find(this.parsedConfigurables, (curConfigurables) => { 
                return curConfigurables.id === id; 
            });
            if ( matchedConfigurable ) {
                matchedConfigurable.value = value;
            }
        }
        
        getConfigurableObj(id:string) {
            // find the property with the given id, and get the current value
            let matchedConfigurable = _.find(this.parsedConfigurables, (curConfigurables) => { 
                return curConfigurables.id === id; 
            });
            if ( matchedConfigurable ) {
                return matchedConfigurable;
            }
            return;
        }

        applyConfigurablesValues() {
            _.each(this.parsedConfigurables, (curConfigurables) => {
                if ( curConfigurables.type === 'choicelist' ) {
                    let index = _.indexOf(curConfigurables.choices, curConfigurables.value);
                    curConfigurables.configurableObj.Value = index.toString();
                } else {
                    curConfigurables.configurableObj.Value = curConfigurables.value;
                }
            });
        }

        configurableValueChanged(configurable) {
            _.each(configurable.rules, (curRule) => {
                let hidden = ( (curRule.value !== configurable.value) || configurable.hidden );
                let childConfigurable = this.getConfigurableObj(curRule.id);
                this.setHidden(childConfigurable,hidden);
            });
        }

        setHidden(configurable,hidden) {
            if ( configurable.type === 'hiddenfield') {
                return;
            }
            configurable.hidden = hidden;
            this.configurableValueChanged(configurable);
        }

        createCCXML(deviceXMLFile, connectionXMLFile, lp) {
            this.applyConfigurablesValues();
            let promise = this.$q.when();
            
            // temporary workaround to set the 2pin JTAG setting for F280049C LaunchPads
			if (lp && (deviceXMLFile == "f280049c") && (connectionXMLFile == "TIXDS110_Connection")) {
				if (!this.configurables) {
					promise = this.getConfigurables(deviceXMLFile, connectionXMLFile);
				}
				promise = promise.then(() => {
					const configurables: { property: any }[] = this.configurables.connections[0].configurables;
					for (let i = 0; i < configurables.length; i++) {
						if (configurables[i].property && configurables[i].property.$ && configurables[i].property.$.id === 'SWD Mode Settings') {
							configurables[i].property.$.Value = 4;
							break;
						}
					}
				});
            }
            
			return promise.then(() => {
				let configurablesStr = JSON.stringify(this.configurables);
				return this.ticAgentAPI.getConfig(TICloudAgent.getOS(), connectionXMLFile, deviceXMLFile, configurablesStr)
					.then((response) => {
						this.resetConfigurables();
						return response.data;
					});
			});
        }

        getFileContent(path: string): angular.IPromise<string> {
            return this.ticAgentAPI.getFileContent(TICloudAgent.getOS(), path)
                .then((response) => {
                    return response.data;
                });
        }
    }

    angular.module('uniflash').service('ufTargetConfig', TargetConfig);
}