define(['jquery', 'tools/all', 'knockout', 'tnc/tnc-tools',
        'tnc/tnc-transition', 'tnc/tnc-border', 'tnc/tnc-margin', 'bluebird', 'lib/moment-with-locales',
        'service/widgetDataService', 'service/widgetService'
    ],
    function () {
        'use strict';
        var $ = require('jquery');
        var allTools = require('tools/all');
        var tools = allTools.tools;
        // var tools3d = allTools.tools3d;
        var jqTools = allTools.jqTools;
        var images = allTools.images;
        var validate = allTools.validate;
        var arrays = allTools.arrays;
        var ko = require('knockout');
        var Promise = require('bluebird');

        var tncTools = require('tnc/tnc-tools');
        var TncTransition = require('tnc/tnc-transition');
        var TncBorder = require('tnc/tnc-border');
        var TncMargin = require('tnc/tnc-margin');

        var widgetDataService = require('service/widgetDataService');
        var widgetService = require('service/widgetService');

        var log = tools.log;
        // var format = tools.format;

        var getStr = validate.getStr;
        var getColor = validate.getColor;
        var getArray = validate.getArray;
        var getNum = validate.getNum;
        var $new = jqTools.$new;
        var isUrl = tncTools.isUrl;
        var limitOpacity = tools.limitOpacity;
        var del = tools.del;
        var urls = allTools.urls;
        var updateQueryString = urls.updateQueryString;

        // =======
        // WidgetData
        function TncWidgetData(scope, settings, parentSettings) {
            this.init(scope, settings, parentSettings);
        }

        TncWidgetData.prototype.init = function (scope, settings, parentSettings) {
            var self = this;
            self.scope = scope;
            self.settings = settings || {};
            self.parentSettings = parentSettings || {};

            self.dataType = self.settings.dataType || 'html';
            self.currentWidget = self.settings;

            // deferred promises;
            self._isLoaded = null;
            self._isPlayed = null;
        };

        TncWidgetData.prototype.instanceId = 0;

        TncWidgetData.prototype.getWidgetDataInstanceId = function () {
            var self = this;
            var t = self.scope.template;
            var l = self.scope.layer;
            var w = self.scope.widget;
            return 'WidgetData:' + t.folder + '/' + t.page + ':' + l.id + '[' + w.widgetIndex + '](' + self.dataType + ')[' + self.instanceId + ']';
        };

        TncWidgetData.prototype.getWidgetDataInstance = function () {
            var self = this;

            function WidgetDataInstance() {
                var instance = this;
                instance.instanceId = TncWidgetData.prototype.instanceId;
                instance._isLoaded = Promise.pending();
                instance._isPlayed = Promise.pending();
                instance.instanceIdString = instance.getWidgetDataInstanceId();
                TncWidgetData.prototype.instanceId++;
            }

            WidgetDataInstance.prototype = self;

            return new WidgetDataInstance();
        };

        TncWidgetData.prototype.load = function ($parent, widgetIndex) {
            var self = this;

            // create layer
            if (!self.$element) {
                self.$element = $new('div', $parent);
                self.$element.addClass('tncWidgetData');
            }

            ko.cleanNode(self.$element[0]);
            self.$element.empty();

            switch (self.dataType.toLowerCase()) {
                case 'image':
                    self.loadImage(widgetIndex);
                    break;
                case 'video':
                case 'knockout':
                case 'webpage':
                    self.loadWidgetComponent();
                    break;
                case 'maatwerk':
                    self.loadMaatwerk(widgetIndex);
                    break;
                case 'html':
                    self.loadHtml(widgetIndex);
                    break;
                default:
                    self.loadHtml(widgetIndex);
                    break;
            }

            return self._isLoaded.promise;
        };

        TncWidgetData.prototype.play = function (widgetIndex) {
            var self = this;

            var transition = new TncTransition(self.parentSettings.transition);
            var duration = getNum(self.parentSettings.duration, 0);

            if (self.widget) {
                self.widget.play().finally(function() {
                    self._isPlayed.resolve();
                });
                return self._isPlayed.promise;
            }

            var isVideo = self.dataType.toLowerCase() === 'video';
            if (duration === 0 && isVideo) {
                duration = 60000 * 5; // Filmje als geen duration is
                // opgegeven standaard max 5 minuten
            }

            if (isVideo) {
                var params = allTools.urls.getQueryParams(window.location.href);
                var novideo = getNum(params.novideo, 0);
                if (novideo) duration = 60000;
            }

            var isMaatwerk = self.dataType.toLowerCase() === 'maatwerk';
            if (isMaatwerk) {
                var src = self.settings.src || '';
                var srcUrl = self.resourceUrl(src);
                var isJsMaatwerk = srcUrl.indexOf('.js') > 0;

                if (isJsMaatwerk) {
                    self.playKnockout(widgetIndex);
                } else {
                    setTimeout(function () {
                        if (self.receivedMessage) {
                            log('Maatwerk + received message');
                            // Geen timeout, wacht op bericht
                            setTimeout(function () {
                                self._isPlayed.resolve();
                            }, 60000 * 60);
                        } else {
                            self._isPlayed.resolve();
                        }
                    }, duration + transition.duration);
                }
            } else {
                switch (self.dataType.toLowerCase()) {
                    case 'image':
                        self.playKnockout(widgetIndex);
                        break;
                    case 'knockout':
                        self.playKnockout(widgetIndex);
                        break;
                    default:
                        setTimeout(function () {
                            self._isPlayed.resolve();
                        }, transition.duration + duration);
                        break;
                }
            }

            return self._isPlayed.promise;
        };

        TncWidgetData.prototype.afterPlay = function ( /*widgetIndex, callback*/ ) {
            var self = this;
            // self.$element.html('');
            // self.$element.empty();

            if (self.$element && self.$element[0]) {
                ko.cleanNode(self.$element[0]);
            }

            log('DATA REMOVED');
        };

        TncWidgetData.prototype.getSettings = function () {
            var self = this;

            return self.settings; // return de originele settings, er mag
            // sowieso niets worden gewijzigd aan
            // dit object
        };

        // Widget data specific

        TncWidgetData.prototype.resourceUrl = function (src) {
            var self = this;
            return tncTools.resourceUrl(self.template.folder, src);
        };

        TncWidgetData.prototype.loadJavascriptMaatwerk = function () {
            var self = this;
            log(self.instanceIdString, 'loading maatwerk data');
            var src = self.settings.src || '';
            var srcUrl = self.resourceUrl(src);
            var instanceId = self.instanceIdString;

            // In dit geval zijn de items de pagina's
            self.settings.amountOnPage = 1;

            var folder = self.template.folder;
            var jsUrl = src.split('?')[0];

            self.items = [];
            jsUrl = '../' + folder + '/' + jsUrl + '#'; //.split('.js')[0];

            require([jsUrl], function (maatwerk) {

                    if (maatwerk && maatwerk.getData) {
                        var params = urls.getQueryParams(srcUrl);

                        var settings = {
                            settings: self.parentSettings,
                            folder: folder,
                            params: params
                        };

                        maatwerk.getData(settings, function (result) {
                            log(self.instanceIdString, 'maatwerk data result', instanceId);
                            self.templateData = result.data || {};

                            self.items = result.items || [];
                            if (!Array.isArray(self.items)) {
                                self.items = [self.items];
                            }

                            if (result.layout) {
                                self.knockoutLayout = result.layout;
                            }

                            self.knockoutItemsLoaded();
                        });
                    } else {
                        self.knockoutItemsLoaded();
                    }
                },
                function (error) {
                    log(error);
                    self.knockoutItemsLoaded();
                });
        };

        TncWidgetData.prototype.loadMaatwerk = function () {
            var self = this;

            var src = self.settings.src || '';
            var srcUrl = self.resourceUrl(src);

            if (srcUrl.indexOf('.js') > 0) {
                self.loadJavascriptMaatwerk();
                return;
            }

            if (src !== '') {
                // var doc = self.$element[0].ownerDocument;
                // $newJs(srcUrl, $('head', $(doc)));
                // log(srcUrl);


                self.$frame = $new('iframe', self.$element, {
                    src: srcUrl
                });

                self.$frame.css({
                    border: 0,
                    margin: 0,
                    'position': 'absolute',
                    'left': '0',
                    'top': '0',
                    'width': '100%',
                    'height': '100%'
                });

                self.window = tools.getWindowFromElement(self.$element);
                self.frameWindow = null;

                self.frameHandler = function (e) {
                    var data = JSON.parse(e.data);
                    if (e.source === self.frameWindow) {
                        log('received message', data);
                        // We have a message back
                        if (data.status && data.status === 'ready') {
                            self.receivedMessage = true;
                            log('play completed!!!!!');

                            self._isPlayed.resolve();
                        }
                    }
                };

                self.$frame.load(function () {
                    setTimeout(function () { // voor de zekerheid nog even een kleine timeout
                        self.frameWindow = self.$frame[0].contentWindow;
                        if (self.frameWindow) {
                            self.frameWindow.postMessage(JSON.stringify({
                                settings: self.parentSettings
                            }), '*');

                            self.window.addEventListener('message', self.frameHandler);
                        }
                    }, 200);
                });
            }

            setTimeout(function () {
                self._isLoaded.resolve();
            }, 1000); // reserve a second for html content loading
        };

        TncWidgetData.prototype.loadHtml = function (widgetIndex) {
            var self = this;

            var html = self.settings.html || '';
            self.$element[0].innerHTML = html; // .html(html);

            // fix relative font size
            self.$element.find('*').each(function () {
				var fontSizeStr = this.style.fontSize.replace('pt', 'px');
				if(fontSizeStr.indexOf('px') >= 0) {
					var fontSize = parseInt(fontSizeStr);
					if (fontSize > 0) {
						fontSize = Math.round((fontSize / 16) * 100) / 100;
						$(this).css('font-size', fontSize + 'rem');
					}
				}
            });

            setTimeout(function () {
                self._isLoaded.resolve();
            }, 1000); // reserve a second for html content loading
        };

        TncWidgetData.prototype.loadImage = function (widgetIndex) {
            var self = this;
            var i;

            var sources = getArray(self.settings.images, []);

            self.items = [];
            for (i = 0; i < sources.length; i += 1) {
                var item = sources[i];
                if (item && item !== '') {
                    self.items.push({
                        src: item
                    });
                }
            }

            var size = getStr(self.settings.size, 'cover');

            var getPreloadFn = function (src, item) {
                return function () {
                    return new Promise(function (resolve, reject) {
                        log('preloading:', src);
                        images.preloadImage(src).then(function (img) {
                                //console.log('preload image');
                                //var url = images.resizeImage(img);
                                //console.log('image resized');
                                item.src = 'url(\'' + src + '\')';
                                //console.log(item);
                            })
                            .finally(resolve);
                    });
                };
            };

            for (i = 0; i < self.items.length; i += 1) {
                var src = self.items[i].src;
                var srcUrl = self.resourceUrl(src);
                self.items[i].src = null; // 'url(\'' + srcUrl +  '\')';
                self.items[i].size = size;
                self.items[i].preload = getPreloadFn(srcUrl, self.items[i]);
            }

            // In dit geval zijn de items de pagina's
            self.settings.amountOnPage = 1;
            self.settings.layout = "<div><div data-bind=\"foreach: items\">" +
                "<div data-bind=\"attr { dataSrc: src },style: { backgroundImage: src, backgroundSize: size } \" style=\"position:absolute;width:100%;height:100%;background-size:cover;background-repeat:no-repeat;background-position:50% 50%;\">" +
                "</div></div>";

            self.knockoutItemsLoaded();
        };

        TncWidgetData.prototype.loadWidgetComponent = function() {
            var self = this;
            self.parentSettings.templateFolder = self.template.folder;

            self.parentSettings.template = {};
            self.parentSettings.template.enableEdit = self.template.enableEdit;
            self.parentSettings.template.exampleItems = self.template.exampleItems;
            self.parentSettings.template.novideo = self.template.novideo;

            self.widget = widgetService.addWidget(self.$element, self.parentSettings);
            self.widget.load()
                .then(function() {
                    self._isLoaded.resolve();
                })
                .catch(function() {
                    self._isLoaded.reject();
                });
        }

        TncWidgetData.prototype.knockoutItemsLoaded = function () {
            var self = this;
            var widgetData = self;

            var templateData = self.templateData || {};

            if (!Array.isArray(self.items)) {
                self.items = [self.items];
            }

            var items = self.items;
            self.$container = $new('div', self.$element);
            self.$container.addClass('knockoutContainer');

            self.$subcontainer = $new('div', self.$container);
            self.$subcontainer.addClass('knockoutSubcontainer');

            self.$panelParent = $new('div', self.$subcontainer);
            self.$panelParent.addClass('knockoutPanelParent');

            var layout = getStr(self.settings.layout, '');

            if (self.knockoutLayout) {
                layout = self.knockoutLayout;
            }

            //console.log('items', self.items);
            //console.log('amountOnPage', self.settings.amountOnPage);
            var pageSize;
            if (self.settings.amountOnPage) {
                pageSize = self.settings.amountOnPage;
            } else {
                pageSize = Math.max(self.settings.amountOnPage || 1, items.length);
            }
            //var pageSize = Math.max(self.settings.amountOnPage || 1, items.length);
            var pageCount = Math.ceil(items.length / pageSize);

            log(self.instanceIdString, {
                pageSize: pageSize,
                pageCount: pageCount
            });

            var backgroundColor = getColor(self.parentSettings.backgroundColor, 'transparent');
            var border = new TncBorder(self.parentSettings.border || {});
            var padding = new TncMargin(self.parentSettings.padding || {});

            function createPage(pageIndex, pageCount, pageItems, border, widget) {
                return new Promise(function (resolve, reject) {
                    var page = {};

                    page.rendered = new Promise(function (resolve, reject) {
                        page.setRendered = function () {
                            resolve();
                        };
                    });
                    page.ready = new Promise(function (resolve, reject) {
                        page.setReady = function () {
                            resolve();
                        };
                    });

                    widget = widget || {};
                    page.$css = widget.css ? $("<style type=\"text/css\"></style>").html(widget.css) : $("");
                    page.$javascript = widget.javascript ? $("<style type=\"text/css\"></style>").html(widget.javascript) : $("");

                    var $frame = $new('iframe', self.$panelParent, {
                        src: "ko-page.html",
                        frameborder: "0"
                    });

                    $frame.css({
                        position: 'absolute',
                        width: '100%',
                        height: '100%',
                        top: '0px',
                        right: '0px',
                        bottom: '0px',
                        left: '0px',
                        overflow: 'hidden',
                        opacity: limitOpacity(0)
                    });

                    $frame.load(function () {
                        var w = tools.getWindowFromElement($frame.contents().find("body"));
                        var d = w.document;
                        var b = d.body;
                        var h = d.head;

                        var s = d.createElement('script');
                        var t = d.createTextNode(widget.javascript);
                        s.appendChild(t);
                        h.appendChild(s);

                        var $panel = $new('div', $(b));
                        $panel.addClass('knockoutPanel');

                        var $panelContent = $new('div', $panel);
                        $panelContent.addClass('knockoutPanelContent');
                        $panelContent.css({
                            //opacity: limitOpacity(0)
                        });

                        $panelContent.css({
                            'background-color': backgroundColor
                                /* , overflow:'hidden' */
                        });

                        border.applyBorder($panelContent);

                        var $ko = $new('div', $panelContent);
                        page.$ko = $ko;
                        $ko.addClass('knockoutContent');

                        $ko.css({
                            position: 'absolute',
                            width: 'auto',
                            height: 'auto',
                            overflow: 'hidden'
                        });
                        padding.applyMargin($ko);

                        $ko.attr('data-bind', 'template: { afterRender: onAfterRender }');

                        var $inner = $new('div', $ko);
                        $inner.attr('data-bind', 'fadeVisible: pageReady');
                        $inner.append($(layout));
                        $inner.append(page.$css);

                        var ViewModelValue = function (dataVal, len) {
                            var self = this;
                            self._strval = null;
                            self._data = dataVal;
                            self.init(dataVal, len);
                        };

                        ViewModelValue.prototype.init = function (data, len) {
                            var self = this;
                            if (typeof (this._data) === 'string') {
                                if (isUrl(this._data)) {
                                    self._strval = widgetData.resourceUrl(this._data);
                                }
                            }

                            if (data instanceof Object) {
                                for (var p in data) {
                                    if (data.hasOwnProperty(p)) {
                                        if (Array.isArray(data[p])) {
                                            self[p] = data[p];
                                        } else {
                                            self[p] = new ViewModelValue(data[p], len - 1);
                                        }
                                    }
                                }

                                if ('#text' in data) {
                                    var text = data['#text'];
                                    if (isUrl(text)) {
                                        self._strval = widgetData.resourceUrl(text);
                                    } else {
                                        self._strval = text;
                                    }
                                } else if ('#cdata-section' in data) {
                                    var cdata = data['#cdata-section'];
                                    if (isUrl(cdata)) {
                                        self._strval = widgetData.resourceUrl(cdata);
                                    } else {
                                        self._strval = cdata;
                                    }
                                }
                            }
                        };

                        ViewModelValue.prototype.toString = function () {
                            var self = this;
                            if (self._strval) {
                                return self._strval;
                            }
                            return self._data;
                        };

                        function ViewModel(pageIndex, pageCount, data, items) {
                            var vm = this;
                            vm.data = new ViewModelValue(data, 10);
                            vm.resolveUrls = function (host, v) {
                                var str = '' + v;
                                str = str.replace(/(["'>])(\/.*?)(["'<])/g, function (a, b, c, d) {
                                    return b + (host + c) + d;
                                });
                                return str;
                            };

                            vm.pageIndex = ko.observable(pageIndex);
                            vm.pageOffset = ko.observable(pageIndex * pageSize);
                            vm.pageCount = ko.observable(pageCount);

                            vm.moment = require('lib/moment-with-locales');

                            vm.items = ko.observableArray([]);

                            vm.pageReady = ko.observable(false);
                            // Ensure it notifies about changes no more than once
                            // per one second
                            vm.items.extend({
                                rateLimit: 20
                            }); // scheelt een hoop schokken in de animaties

                            vm.onAfterRender = function () {
                                page.setRendered();
                            };

                            for (var j = 0; j < items.length; j += 1) {
                                var itemData = items[j];
                                var item = new ViewModelValue(itemData, 10);
                                vm.items.push(item);
                            }
                        }

                        page.preload = function () {
                            return new Promise(function (resolve, reject) {
                                var p = [];

                                arrays.forEach(pageItems, function (item) {
                                    if (typeof (item.preload) === 'function') {
                                        p.push(item.preload());
                                    }
                                });

                                Promise.all(p).timeout(10000).finally(function () {
                                    var vm = new ViewModel(pageIndex, pageCount, templateData, pageItems);
                                    ko.applyBindings(vm, $ko[0]);

                                    page.rendered.finally(function () {
                                        vm.pageReady(true);
                                        page.setReady();
                                        log('pageReady!!');
                                        resolve();
                                    });
                                });
                            });
                        };

                        page.$panel = $frame;
                        resolve(page);
                    });
                });
            }

            var p = [];
            for (var i = 0; i < pageCount; i += 1) {
                var pageItems = [];
                for (var j = 0; j < pageSize; j += 1) {
                    var index = (i * pageSize) + j;
                    if (items[index]) {
                        pageItems.push(items[index]);
                    }
                    log(self.instanceIdString, 'page:', i, 'item:', index);
                }
                p.push(createPage(i, pageCount, pageItems, border, self.currentWidget));
            }

            Promise.all(p).then(function (pages) {
                border = null;
                self.pages = pages;
                setTimeout(function () {
                    self._isLoaded.resolve();
                }, 1000);
            });
        };

        TncWidgetData.prototype.playKnockout = function () {
            var self = this;
            var transition = new TncTransition(self.parentSettings.transition);
            var duration = getNum(self.parentSettings.duration, 0) + transition.duration;
            log('page duration', duration);

            var $prevPage = null;

            var pages = self.pages || [];
            var pageCount = pages.length || 0;

            var pageIndex = 0;

            if (pageCount < 1) {
                log('all pages played (0 pages)', self.instanceIdString);
                self._isPlayed.resolve();
                return;
            }

            function playPage() {
                if (pageIndex < pageCount) {
                    log('playing page:', pageIndex, pages[pageIndex]);
                    //var prevPage = null;
                    var page = pages[pageIndex];
                    var $page = page.$panel;

                    // show the element (preload in video memory)
                    $page.css({ opacity: limitOpacity(0) });

                    var pageLoaded = function () {
                        function pageReady() {
                            // transition + waiting here
                            pageIndex += 1;
                            setTimeout(function () {
                                playPage();
                            }, duration);
                        }

                        log('page transition');
                        transition.transitionDisabled = (pageIndex === 0);
                        //transition.applyTransition(prevPage, page, $page, $prevPage, pageReady);
                        transition.run($page, $prevPage).then(function () {
                            $prevPage = $page;
                            if (pages[pageIndex - 1]) {
                                var prevPage = pages[pageIndex - 1];
                                ko.unapplyBindings(prevPage.$ko, true);
                                prevPage.$panel.remove();
                                prevPage.$panel = null;
                                prevPage.$ko = null;
                                prevPage = null;
                                pages[pageIndex - 1] = null;
                                log('page unloaded', pageIndex - 1);
                            }

                            pageReady();
                        });

                        //prevPage = page;
                        $prevPage = $page;
                    };

                    page.preload().then(function () {
                        log('page preloaded');
                        setTimeout(function() {
                            pageLoaded();
                        }, 100);
                    });
                } else {
                    log('all pages played (' + pageCount + ' pages)', self.instanceIdString);
                    self._isPlayed.resolve();
                }
            }

            playPage();
        };

        TncWidgetData.prototype.unload = function () {
            var self = this;

            if (self.pages) {
                for (var i = 0; i < self.pages.length; i += 1) {
                    if (self.pages[i]) {
                        ko.unapplyBindings(self.pages[i].$ko, true);
                        self.pages[i].$panel.parent().remove();
                        self.pages[i].$panel = null;
                        self.pages[i].$ko = null;
                        log('page unloaded', i);
                    }
                }
            }

            if (self.$frame) {
                // Unbinding window message listener
                log('Unbinding window message listener');

                self.window.removeEventListener('message', self.frameHandler);
            }

            del(self, 'settings');
            del(self, 'transition');

            self.unloaded = true;
            log('Widget data unloaded', self);

            self = null;
        };

        return TncWidgetData;
    });
