/*global define */
define([
    'lib/stapes', 'views/MainView', 'models/Settings', 'views/SettingsView', 'views/EffectsView', 'views/TransformView', 'data/ServerControl', 'api/Socket', 'api/Network'
], function (Stapes, MainView, Settings, SettingsView, EffectsView, TransformView, ServerControl, Socket, Network) {
    'use strict';
    var network = new Network();

    return Stapes.subclass(/** @lends AppController.prototype */{
        /**
         * @type MainView
         */
        mainView: null,
        /**
         * @type SettingsView
         */
        settingsView: null,
        /**
         * @type EffectsView
         */
        effectsView: null,
        /**
         * @type TransformView
         */
        transformView: null,
        /**
         * @type Settings
         */
        settings: null,
        /**
         * @type ServerControl
         */
        serverControl: null,
        color: {
            r: 25,
            g: 25,
            b: 25
        },
        effects: [],
        transform: {},
        selectedServer: null,

        /**
         * @class AppController
         * @constructs
         */
        constructor: function () {
            this.mainView = new MainView();
            this.settingsView = new SettingsView();
            this.effectsView = new EffectsView();
            this.transformView = new TransformView();

            this.settings = new Settings();

            this.bindEventHandlers();
            this.mainView.setColor(this.color);

            if (!network.canDetectLocalAddress()) {
                this.settingsView.enableDetectButton(false);
            }
        },

        /**
         * Do initialization
         */
        init: function () {
            this.settings.load();
        },

        /**
         * @private
         */
        bindEventHandlers: function () {
            this.settings.on({
                'loaded': function () {
                    var i;

                    for (i = 0; i < this.settings.servers.length; i++) {
                        if (this.settings.servers[i].selected) {
                            this.selectedServer = this.settings.servers[i];
                            break;
                        }
                    }

                    this.settingsView.fillServerList(this.settings.servers);

                    if (!this.selectedServer) {
                        this.gotoArea('settings');
                    } else {
                        this.connectToServer(this.selectedServer);
                    }
                },
                'error': function (message) {
                    this.showError(message);
                },
                'serverAdded': function (server) {
                    var i;
                    for (i = 0; i < this.settings.servers.length; i++) {
                        if (this.settings.servers[i].selected) {
                            this.selectedServer = this.settings.servers[i];
                            this.connectToServer(server);
                            break;
                        }
                    }

                    this.settingsView.fillServerList(this.settings.servers);
                },
                'serverChanged': function (server) {
                    var i;
                    for (i = 0; i < this.settings.servers.length; i++) {
                        if (this.settings.servers[i].selected) {
                            this.selectedServer = this.settings.servers[i];
                            this.connectToServer(server);
                            break;
                        }
                    }

                    this.settingsView.fillServerList(this.settings.servers);
                    this.connectToServer(server);
                },
                'serverRemoved': function () {
                    var i, removedSelected = true;
                    this.settingsView.fillServerList(this.settings.servers);

                    for (i = 0; i < this.settings.servers.length; i++) {
                        if (this.settings.servers[i].selected) {
                            removedSelected = false;
                            break;
                        }
                    }

                    if (removedSelected) {
                        this.selectedServer = null;
                        if (this.serverControl) {
                            this.serverControl.disconnect();
                        }
                        this.effectsView.clear();
                        this.transformView.clear();
                    }
                }
            }, this);

            this.mainView.on({
                'barClick': function (id) {
                    if (id !== 'settings') {
                        if (!this.selectedServer) {
                            this.showError('No server selected');
                        } else if (!this.serverControl) {
                            this.connectToServer(this.selectedServer);
                        }
                    }
                    this.gotoArea(id);
                },
                'colorChange': function (color) {
                    this.color = color;

                    if (!this.selectedServer) {
                        this.showError('No server selected');
                    } else if (!this.serverControl) {
                        this.connectToServer(this.selectedServer, function () {
                            this.serverControl.setColor(color, this.selectedServer.duration);
                        }.bind(this));
                    } else {
                        this.serverControl.setColor(color, this.selectedServer.duration);
                    }
                },
                'clear': function () {
                    if (!this.selectedServer) {
                        this.showError('No server selected');
                    } else if (!this.serverControl) {
                        this.connectToServer(this.selectedServer, function () {
                            this.serverControl.clear();
                        }.bind(this));
                    } else {
                        this.serverControl.clear();
                    }
                },
                'clearall': function () {
                    if (!this.selectedServer) {
                        this.showError('No server selected');
                    } else if (!this.serverControl) {
                        this.connectToServer(this.selectedServer, function () {
                            this.serverControl.clearall();
                            this.mainView.setColor({r: 0, g: 0, b: 0});
                        }.bind(this));
                    } else {
                        this.serverControl.clearall();
                        this.mainView.setColor({r: 0, g: 0, b: 0});
                    }
                }
            }, this);

            this.settingsView.on({
                'serverAdded': function (server) {
                    if (server.address && server.port) {
                        server.priority = server.priority || 50;
                        this.settings.addServer(server);
                        this.lockSettingsView(false);
                    } else {
                        this.showError('Invalid server data');
                    }
                },
                'serverAddCanceled': function () {
                    this.lockSettingsView(false);
                    this.settingsView.fillServerList(this.settings.servers);
                },
                'serverEditCanceled': function () {
                    this.lockSettingsView(false);
                    this.settingsView.fillServerList(this.settings.servers);
                },
                'serverSelected': function (index) {
                    this.lockSettingsView(false);
                    this.settings.setSelectedServer(index);
                },
                'serverRemoved': function (index) {
                    this.settings.removeServer(index);
                },
                'serverChanged': function (data) {
                    if (data.server.address && data.server.port) {
                        data.server.priority = data.server.priority || 50;
                        this.settings.updateServer(data.index, data.server);
                        this.lockSettingsView(false);
                    } else {
                        this.showError('Invalid server data');
                    }
                },
                'editServer': function (index) {
                    var server = this.settings.servers[index];
                    this.settingsView.editServer({index: index, server: server});
                },
                'durationChanged': function (value) {
                    this.settings.duration = value;
                    this.settings.save();
                },
                'detect': function () {
                    this.lockSettingsView(true);
                    this.settingsView.showWaiting(true);
                    this.searchForServer(function (server) {
                        this.settings.addServer(server);
                    }.bind(this), function () {
                        this.lockSettingsView(false);
                        this.settingsView.showWaiting(false);
                    }.bind(this));
                }
            }, this);

            this.effectsView.on({
                'effectSelected': function (effectId) {
                    if (!this.serverControl) {
                        this.connectToServer(this.selectedServer, function () {
                            this.serverControl.runEffect(this.effects[parseInt(effectId)]);
                        }.bind(this));
                    } else {
                        this.serverControl.runEffect(this.effects[parseInt(effectId)]);
                    }
                }
            }, this);

            this.transformView.on({
                'gamma': function (data) {
                    if (data.r) {
                        this.transform.gamma[0] = data.r;
                    } else if (data.g) {
                        this.transform.gamma[1] = data.g;
                    } else if (data.b) {
                        this.transform.gamma[2] = data.b;
                    }

                    if (this.serverControl) {
                        this.serverControl.setTransform(this.transform);
                    }
                },
                'whitelevel': function (data) {
                    if (data.r) {
                        this.transform.whitelevel[0] = data.r;
                    } else if (data.g) {
                        this.transform.whitelevel[1] = data.g;
                    } else if (data.b) {
                        this.transform.whitelevel[2] = data.b;
                    }

                    if (this.serverControl) {
                        this.serverControl.setTransform(this.transform);
                    }
                },
                'blacklevel': function (data) {
                    if (data.r) {
                        this.transform.blacklevel[0] = data.r;
                    } else if (data.g) {
                        this.transform.blacklevel[1] = data.g;
                    } else if (data.b) {
                        this.transform.blacklevel[2] = data.b;
                    }

                    if (this.serverControl) {
                        this.serverControl.setTransform(this.transform);
                    }
                },
                'threshold': function (data) {
                    if (data.r) {
                        this.transform.threshold[0] = data.r;
                    } else if (data.g) {
                        this.transform.threshold[1] = data.g;
                    } else if (data.b) {
                        this.transform.threshold[2] = data.b;
                    }

                    if (this.serverControl) {
                        this.serverControl.setTransform(this.transform);
                    }
                },
                'hsv': function (data) {
                    if (data.valueGain) {
                        this.transform.valueGain = data.valueGain;
                    } else if (data.saturationGain) {
                        this.transform.saturationGain = data.saturationGain;
                    }

                    if (this.serverControl) {
                        this.serverControl.setTransform(this.transform);
                    }
                }
            }, this);
        },

        /**
         * @private
         * @param id
         */
        gotoArea: function (id) {
            this.mainView.scrollToArea(id);
        },

        /**
         * @private
         * @param server
         */
        connectToServer: function (server, onConnected) {
            if (this.serverControl) {
                if (this.serverControl.isConnecting()) {
                    return;
                }
                this.serverControl.off();
                this.serverControl.disconnect();
                this.transformView.clear();
                this.effectsView.clear();
            }

            this.serverControl = new ServerControl(server, Socket);
            this.serverControl.on({
                connected: function () {
                    this.serverControl.getServerInfo();
                },
                serverInfo: function (info) {
                    var index;
                    if (!this.selectedServer.name || this.selectedServer.name.length === 0) {
                        this.selectedServer.name = info.hostname;
                        index = this.settings.indexOfServer(this.selectedServer);
                        this.settings.updateServer(index, this.selectedServer);
                        this.settingsView.fillServerList(this.settings.servers);
                    }
                    this.effects = info.effects;
                    this.transform = info.transform[0];
                    this.updateView();
                    this.showStatus('Connected to ' + this.selectedServer.name);
                    if (onConnected) {
                        onConnected();
                    }
                },
                error: function (message) {
                    this.serverControl = null;
                    this.showError(message);
                }
            }, this);
            this.serverControl.connect();
        },

        /**
         * @private
         */
        updateView: function () {
            var i, effects = [];
            if (this.effects) {
                for (i = 0; i < this.effects.length; i++) {
                    effects.push({id: i, name: this.effects[i].name});
                }
            }

            this.effectsView.clear();
            this.effectsView.fillList(effects);

            this.transformView.clear();
            this.transformView.fillList(this.transform);
        },

        /**
         * Shows the error text
         * @param {string} error - Error message
         */
        showError: function (error) {
            this.mainView.showError(error);
        },

        /**
         * Shows a message
         * @param {string} message - Text to show
         */
        showStatus: function (message) {
            this.mainView.showStatus(message);
        },

        /**
         * @private
         * @param lock
         */
        lockSettingsView: function (lock) {
            if (network.canDetectLocalAddress()) {
                this.settingsView.enableDetectButton(!lock);
            }
            this.settingsView.lockList(lock);
        },

        /**
         * @private
         * @param onFound
         * @param onEnd
         */
        searchForServer: function (onFound, onEnd) {
            network.getLocalInterfaces(function (ips) {
                if (ips.length === 0) {
                    onEnd();
                    return;
                }

                function checkInterface (localInterfaceAddress, ciOnFinished) {
                    var index, ipParts, addr;

                    index = 1;
                    ipParts = localInterfaceAddress.split('.');
                    ipParts[3] = index;
                    addr = ipParts.join('.');

                    function checkAddressRange (startAddress, count, carOnFinished) {
                        var ipParts, i, addr, cbCounter = 0, last;

                        function checkAddress (address, port, caOnFinished) {
                            var server = new ServerControl({'address': address, 'port': port}, Socket);
                            server.on({
                                'error': function () {
                                    server.disconnect();
                                    caOnFinished();
                                },
                                'connected': function () {
                                    server.getServerInfo();
                                },
                                'serverInfo': function (result) {
                                    var serverInfo = {
                                        'address': address,
                                        'port': port,
                                        'priority': 50
                                    };
                                    server.disconnect();

                                    if (result.hostname) {
                                        serverInfo.name = result.hostname;
                                    }

                                    caOnFinished(serverInfo);
                                }
                            });
                            server.connect();
                        }

                        function checkAddressDoneCb (serverInfo) {
                            var ipParts, nextAddr;

                            if (serverInfo && onFound) {
                                onFound(serverInfo);
                            }

                            cbCounter++;
                            if (cbCounter === count) {
                                ipParts = startAddress.split('.');
                                ipParts[3] = parseInt(ipParts[3]) + count;
                                nextAddr = ipParts.join('.');
                                carOnFinished(nextAddr);
                            }
                        }

                        ipParts = startAddress.split('.');
                        last = parseInt(ipParts[3]);

                        for (i = 0; i < count; i++) {
                            ipParts[3] = last + i;
                            addr = ipParts.join('.');

                            checkAddress(addr, 19444, checkAddressDoneCb);
                        }
                    }

                    function checkAddressRangeCb (nextAddr) {
                        var ipParts, count = 64, lastPart;

                        ipParts = nextAddr.split('.');
                        lastPart = parseInt(ipParts[3]);
                        if (lastPart === 255) {
                            ciOnFinished();
                            return;
                        } else if (lastPart + 64 > 254) {
                            count = 255 - lastPart;
                        }

                        checkAddressRange(nextAddr, count, checkAddressRangeCb);
                    }

                    // do search in chunks because the dispatcher used in the ios socket plugin can handle only 64 threads
                    checkAddressRange(addr, 64, checkAddressRangeCb);
                }

                function checkInterfaceCb () {
                    if (ips.length === 0) {
                        onEnd();
                    } else {
                        checkInterface(ips.pop(), checkInterfaceCb);
                    }
                }

                checkInterface(ips.pop(), checkInterfaceCb);

            }.bind(this), function (error) {
                this.showError(error);
            }.bind(this));
        }
    });
});
