define(['jquery', 'knockout', 'tools/tools', 'tools/urls', 'tools/messaging', 'tools/datetime', 'config'], function () {
    'use strict';
    var ko = require('knockout');
    var tools = require('tools/tools');
    var urls = require('tools/urls');
    var messaging = require('tools/messaging');
    var datetime = require('tools/datetime');
    var config = require('config');
    var log = tools.log;
    var handleMessageTnc = null;
    function handleMessage(message) {
        log('SUBTEMPLATE new message:', message);

        if (handleMessageTnc) {
            handleMessageTnc(message);
        }
    }
    messaging.registerHandler(handleMessage);

    require(['jqDragAndMove', 'sammy', 'tools/jq-tools', 'tools/tools-3d'], function () {
        var Sammy = require('sammy');
        var jqTools = require('tools/jq-tools');
        var $new = jqTools.$new;
        var $newCss = jqTools.$newCss;

        require(['tnc/tnc-border', 'tnc/tnc-layer', 'tnc/tnc-margin', 'tnc/tnc-position', 'tnc/tnc-transition', 'tnc/tnc-template', 'tnc/tnc-layer',
            'tnc/tnc-widget', 'tnc/tnc-widgetdata', 'tools/validate'
        ], function () {

            var TncBorder = require('tnc/tnc-border');
            var TncLayer = require('tnc/tnc-layer');
            var TncTransition = require('tnc/tnc-transition');
            var TncTemplate = require('tnc/tnc-template');
            var validate = require('tools/validate');
            var getQueryParams = urls.getQueryParams;
            var getNum = validate.getNum;
            var getColor = validate.getColor;

            function tncModule() {
                var module = {};

                window.TNC = module;
                var mainWindow = window;

                var $ = window.jQuery;
                module.params = getQueryParams();
                var debug = module.params['debug'];
                module.exampleItems = false;
                module.selectLayer = false;
                module.exampleItems = module.params['exampleitems'] ? true : false;
                module.templateId = null;

                module.previousTemplate = null;
                module.currentTemplate = null;
                module.nextTemplate = null;

                function sendMessage(message) {
                    message.templateId = module.templateId;
                    module.sendQueue = module.sendQueue || [];
                    module.sendQueue.push(message);
                    processSendQueue();
                }

                handleMessageTnc = function (message) {
                    if (message.templateId) {
                        module.templateId = message.templateId;
                        loadTemplate(message.page, message.folder);
                    }
                    if (message.cef) {
                        receiveMessage(message.cef.type, message.cef.object);
                    }
                };

                sendMessage({
                    templateListening: true
                });

                function globalMousedown() {
                    if (TncLayer && TncLayer.prototype && TncLayer.prototype.activeLayer) {
                        TncLayer.prototype.activeLayer.deactivate();
                    }
                    return false;
                }

                function globalClick() {
                    return false;
                }

                function globalKeydown(key) {
                    var leftArrow = 37;
                    var upArrow = 38;
                    var rightArrow = 39;
                    var downArrow = 40;
                    var spaceBar = 32;
                    var deleteKey = 46;
                    // var enterKey = 13;
                    var escapeKey = 27;

                    if (key == spaceBar) {
                        if (module.currentTemplate) {
                            if (!module.currentTemplate.paused) {
                                module.currentTemplate.pause();
                            } else {
                                module.currentTemplate.play(true);
                            }
                        } else {
                            log('Error: no template loaded');
                        }
                    }
                    if (key == downArrow) {
                        log('you pressed DOWN, saving template to store');

                        if (module.currentTemplate) {
                            tools.setLocal('templateSettings', module.currentTemplate.getSettings());
                        } else {
                            log('Error: no template loaded');
                        }
                        return true;
                    }
                    if (key == upArrow) {
                        log('you pressed UP, loading template from store');

                        var settings = tools.getLocal('templateSettings');
                        if (settings) {
                            loadTemplateFromSettings(settings);
                            settings = undefined;
                        } else {
                            log('Error: no template stored!');
                        }
                        return true;
                    }
                    if (key == leftArrow) {

                        if (module.currentTemplate) {
                            var template = module.currentTemplate;
                            log('calculating duration');
                            log('duration', template.getDuration());
                        }

                        return true;
                    }
                    if (key == rightArrow) {

                        if (module.currentTemplate) {
                            module.currentTemplate.play();
                        }
                    }
                    if (key == deleteKey) {
                        if (TncLayer && TncLayer.prototype && TncLayer.prototype.activeLayer) {
                            sendMessageCefhost('key', {
                                key: 'delete',
                                layer: TncLayer.prototype.activeLayer.id
                            });
                        }
                    }
                    if (key == escapeKey) {
                        if (TncLayer && TncLayer.prototype && TncLayer.prototype.activeLayer) {
                            sendMessageCefhost('key', {
                                key: 'escape',
                                layer: TncLayer.prototype.activeLayer.id
                            });
                        }
                    }

                    return false;
                }

                module.sendQueue = module.sendQueue || [];
                module.sending = false;

                function processSendQueue() {
                    if (!module.sending) {
                        var message = module.sendQueue.pop();
                        if (message) {
                            module.sending = true;

                            try {
                                messaging.sendToParent(message);
                                log('SUBTEMPLATE send message', message);

                                setTimeout(function () {
                                    module.sending = false;
                                    processSendQueue();
                                }, 100);
                            } catch (e) {
                                module.sending = false;
                                log(e);
                            }
                        }
                    }
                }

                function sendMessageCefhost(type, object, type2, object2) {
                    if (!config.isDesktop || debug)
                        return;
                    var url = 'http://cefhost.local/';
                    var data = {};

                    if (typeof (object) === 'string') {
                        data[type] = object;
                    } else {
                        data[type] = JSON.stringify(object);
                    }

                    if (type2 && object2) {
                        if (typeof (object2) === 'string') {
                            data[type2] = object2;
                        } else {
                            data[type2] = JSON.stringify(object2);
                        }
                    }

                    module.sendQueue.push({
                        url: url,
                        data: data
                    });
                    processSendQueue();
                }

                function getLayerById(id) {
                    var template = module.currentTemplate;
                    var layer = null;
                    if (template) {
                        var i;
                        for (i = 0; i < template.layers.length; i += 1) {
                            if (template.layers[i].id === id) {
                                layer = template.layers[i];
                            }
                        }
                    } else {
                        log('Error: no active template');
                    }
                    return layer;
                }

                function receiveMessage(type, object) {
                    var layer, settings;
                    log({
                        type: type,
                        object: object
                    });

                    if (type === 'template' && object === 'play') {
                        if (module.currentTemplate) {
                            module.currentTemplate.play();
                        }
                    }

                    if (type === 'template' && object === 'refresh') {
                        TncTemplate.prototype.isTemplateRefresh = true;

                        if (TncLayer.prototype.activeLayer) {
                            module.selectLayer = TncLayer.prototype.activeLayer.id;
                            log({
                                selectedLayer: module.selectLayer
                            });
                        }
                        TncTransition.prototype.transitionsDisabled = true;
                        // reenable transitions after timeout
                        setTimeout(function () {
                            TncTransition.prototype.transitionsDisabled = false;
                        }, 4000);

                        module.sammy.refresh(); // Rerun route (reloads template)
                    } else if (type === 'layer') {
                        settings = JSON.parse(object);
                        if (settings && settings.id) {
                            layer = getLayerById(settings.id);
                            if (layer) {
                                layer.opacity = getNum(settings.opacity, 1);
                                layer.backgroundColor = getColor(settings.backgroundColor, 'transparent');
                                layer.border = new TncBorder(settings.border);

                                layer.applySettings();
                            }
                        }
                    } else if (type === 'selection') {
                        var id = object;
                        module.selectLayer = id;

                        if (module.selectLayer != '') {
                            layer = getLayerById(id);
                            if (layer) {
                                layer.setActive();
                                module.selectLayer = false;
                            }
                        } else {
                            if (TncLayer.prototype.activeLayer) {
                                TncLayer.prototype.activeLayer.deactivate();
                            }
                        }
                    } else if (type === 'position') {
                        settings = JSON.parse(object);

                        if (TncLayer.prototype.activeLayer) {
                            layer = TncLayer.prototype.activeLayer;

                            layer.position.init(settings);
                            layer.position.applyPosition(layer.$element);
                        } else {
                            log('Error: no active layer');
                        }
                    } else if (type === 'addlayer') {
                        settings = JSON.parse(object);

                        if (module.currentTemplate) {
                            module.currentTemplate.addLayer(settings);
                        }
                    } else if (type === 'deletelayer') {
                        var layerId = object;
                        if (module.currentTemplate) {
                            module.currentTemplate.deleteLayer(layerId);
                        }
                    }
                }

                module.log = log;

                module.sendStillPlaying = function () {
                    var template = module.nextTemplate;
                    if (!template) template = module.currentTemplate;
                    if (template && template.stillPlaying) {
                        sendMessageCefhost('status', {
                            'still-playing': template.stillPlaying,
                            id: template.id
                        });
                    }
                };

                function documentReady() {
                    module.$templateParent = $new('div', $('body'));
                    module.$templateParent.addClass('tncTemplateParent');

                    $newCss('theme/tnc-template.css?_=' + Math.random());

                    if (config.isDesktop) {
                        initSammy();
                    }

                    setInterval(module.sendStillPlaying, 5000);

                    // Here's a custom Knockout binding that makes elements
                    // shown/hidden via jQuery's fadeIn()/fadeOut() methods
                    // Could be stored in a separate utility library
                    ko.bindingHandlers.fadeVisible = {
                        init: function (element, valueAccessor) {
                            // Initially set the element to be instantly
                            // visible/hidden depending on the value
                            var value = valueAccessor();
                            if (value && element) {
                                $(element).toggle(ko.unwrap(value)); // Use
                            }
                        },
                        update: function (element, valueAccessor) {
                            // Whenever the value subsequently changes, slowly
                            // fade the element in or out
                            var value = valueAccessor();
                            ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut();
                        }
                    };

                    ko.bindingHandlers.date = {
                        update: function (element, valueAccessor, allBindings) {
                            // First get the latest data that we're bound to
                            var value = valueAccessor();
                            // Next, whether or not the supplied model property
                            // is observable, get its current value
                            var valueUnwrapped = ko.unwrap(value);
                            // Grab some more data from another binding property
                            var dateFormat = allBindings.get('dateFormat') || 'yyyy-MM-dd HH-mm-ss'; // 400ms
                            var dateAsTimestamp = valueUnwrapped;
                            var dateAsString = datetime.formatDate(new Date(Date(dateAsTimestamp)), dateFormat);
                            // Now manipulate the DOM element
                            $(element).text(dateAsString);
                        }
                    };

                    ko.unapplyBindings = function ($node, remove) {
                        // unbind events
                        $node.find("*").each(function () {
                            $(this).unbind();
                        });
                        // Remove KO subscriptions and references
                        if (remove) {
                            ko.removeNode($node[0]);
                        } else {
                            ko.cleanNode($node[0]);
                        }
                    };
                }

                function initSammy() {
                    var sammy = new Sammy.Application();
                    module.sammy = sammy;

                    sammy.get('#/blank', function () {
                        module.path = '/theme';
                        loadTemplate(null, null, true);
                    });

                    sammy.get(/\#\/page\/(.*)/, function () {
                            var parts = this.params['splat'].pop().split('/');
                            var page = parts.pop();
                            var folder = parts.join('/');

                            log('Sammy new route', folder, page);
                            loadTemplate(page, folder);
                    });

                    sammy.notFound = function (context) {
                        var path = window.location.hash;
                        if (!path || path === '' || path === '/' || path === '#/') {
                            log('Sammy: Empty route, to default');

                            sammy.setLocation('#/page/index');
                        } else {
                            log('Sammy: Route not found\n' + path);
                        }
                    };
                    sammy.run();
                }

                $(mainWindow).keydown(function (event) {
                    var handled = globalKeydown(event.which);
                    if (handled)
                        event.preventDefault();
                });

                $(mainWindow).click(function (event) {
                    var handled = globalClick();
                    if (handled)
                        event.preventDefault();
                });

                $(mainWindow).mousedown(function (event) {
                    var handled = globalMousedown();
                    if (handled)
                        event.preventDefault();
                });

                var aTemplateIsAlreadyLoading = false;

                function loadTemplateFromSettings(settings) {

                    function templateReady() {
                        var template = module.currentTemplate;
                        log('setting old layer', module.selectLayer);

                        if (module.selectLayer) {
                            var layerId = module.selectLayer;
                            module.selectLayer = false;
                            var layer = null;
                            var i;
                            for (i = 0; i < template.layers.length; i += 1) {
                                if (template.layers[i].id === layerId) {
                                    layer = template.layers[i];
                                    break;
                                }
                            }

                            if (layer) {
                                layer.setActive();
                                // sendMessageCefhost('selection', layer.id);
                            } else {
                                sendMessageCefhost('selection', '');
                            }
                        }
                        template = undefined;
                    }

                    function templateLoaded() {
                        aTemplateIsAlreadyLoading = false;

                        var template = module.nextTemplate;
                        var preloadTime = template.getPreloadTime();

                        var wait = module.params.wait;


                        template.beforePlay(); //.then(function() { });

                        if (!wait) {
                            // afspelen gebeurt al voordat het template weergegeven word!
                            template.play();
                        }

                        setTimeout(function () {
                            // Send message template loaded
                            sendMessage({
                                templateLoaded: true,
                                transition: module.nextTemplate.transition
                            });

                            if (module.currentTemplate) {
                                module.currentTemplate.hideTemplate();
                                module.currentTemplate.unload();
                            }

                            module.nextTemplate.showTemplate();
                            module.currentTemplate = module.nextTemplate;
                            module.nextTemplate = null;
                            templateReady();
                        }, preloadTime);
                    }

                    settings.page = module.page;
                    settings.folder = module.folder;

                    var scope = {};
                    scope.sendMessage = sendMessage;
                    scope.sendMessageCefhost = sendMessageCefhost;
                    scope.globalMousedown = globalMousedown;
                    scope.globalClick = globalClick;
                    scope.globalKeydown = globalKeydown;

                    if (!aTemplateIsAlreadyLoading) {
                        aTemplateIsAlreadyLoading = true;

                        module.nextTemplate = new TncTemplate(scope, settings);
                        settings = undefined;

                        module.nextTemplate.load(module.$templateParent, templateLoaded);
                    } else {
                        log('abort: a template is already loading!');
                    }
                }

                function loadTemplate(page, folder, blank) {
                    var path = page;

                    if (blank) {
                        page = 'blank';
                        folder = 'theme';
                    }

                    if (folder) {
                        path = folder + '/' + page;
                    }
                    module.page = page;
                    module.folder = folder;

                    function loadTemplateFromResponse(response) {
                        try {
                            var settings = JSON.parse(response);
                            settings.css = path + '.css';
                            loadTemplateFromSettings(settings);
                        } catch (e) {
                            log(e);
                        }
                    }

                    var r = $.ajax({
                        url: path + '.json?_=' + Math.random(),
                        cache: false
                    });

                    r.always(function () {
                        if (r.responseText) {
                            loadTemplateFromResponse(r.responseText);
                        } else {
                            log('Page not found: ' + path);
                        }
                    });
                }

                $(document).ready(documentReady);
            }

            tncModule();
        });
    });
});
