define(['jquery', 'tools/tools', 'tools/validate', 'tnc/tnc-border', 'tnc/tnc-margin', 'tnc/tnc-transition',
		'tnc/tnc-widgetdata', 'tools/jq-tools', 'tools/tools-3d', 'bluebird'],
function() {
	'use strict';
	//var $ = require('jquery');
	var tools = require('tools/tools');
	var jqTools = require('tools/jq-tools');
	var validate = require('tools/validate');
	var TncBorder = require('tnc/tnc-border');
	var TncMargin = require('tnc/tnc-margin');
	var TncTransition = require('tnc/tnc-transition');
	var TncWidgetData = require('tnc/tnc-widgetdata');

	var log = tools.log;
	var Promise = require('bluebird');

	//var format = tools.format;

	var getStr = validate.getStr;
	var getColor = validate.getColor;
	var getNum = validate.getNum;
	var limitOpacity = tools.limitOpacity;
	var $new = jqTools.$new;
	var del = tools.del;

	var setTimeout = window.setTimeout;

	// =======
	// Widget
	function TncWidget(template, settings) {
		this.init(template, settings);
	}

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

		self.backgroundColor = getColor(self.settings.backgroundColor, 'transparent');
		self.border = new TncBorder(self.settings.border);
		self.opacity = limitOpacity(getNum(self.settings.opacity, 1));
		self.transition = new TncTransition(self.settings.transition, self);

		var widgetDataScope = { template: self.scope.template, layer: self.scope.layer, widget: self};
		self.data = new TncWidgetData(widgetDataScope, self.settings.data, self.settings);
		self.margin = new TncMargin(self.settings.margin);
		self.padding = new TncMargin(self.settings.padding);
		self.preloadTime = getNum(self.settings.preloadTime, 100);
		if (self.preloadTime < 100) {
			self.preloadTime = 100;
		}
		self.duration = getNum(self.settings.duration, 0);
		self.id = getStr(self.settings.id, '');
		self.name = self.settings.name;
		self.widgetType = self.settings.widgetType;
		self.sorting = getNum(self.settings.sorting, 0);
		self.widgetIndex = 0; // Set on widget instance, from it's layer

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

	TncWidget.prototype.instanceId = 0;

	TncWidget.prototype.getWidgetInstance = function() {

		var self = this;

		function TncWidgetInstance() {
			var instance = this;
			instance.instanceId = TncWidget.prototype.instanceId;
			instance.data = self.data.getWidgetDataInstance();
			instance.data.template = self.template;
			instance._isLoaded = Promise.pending();
			TncWidget.prototype.instanceId++;
		}

		TncWidgetInstance.prototype = self;

		return new TncWidgetInstance();
	};

	TncWidget.prototype.load = function($parent) {
		var self = this;
		self.instanceId = self.data.getWidgetDataInstanceId();

		if (self.isLoading) {
			return self._isLoaded.promise;
		}
		self.isLoading = true;

		log('loading widget', self.instanceId);

		// force reload whole widget
		if (self.$element) {
			self.$element.remove();
			self.$element = null;
		}

		self.$element = $new('div', $parent);
		self.$element.addClass('tncWidget');
		self.$element.attr('data-widget-name', self.name);
		self.$element.attr('data-widget-type', self.data.dataType);

		self.$margin = $new('div', self.$element);
		self.$margin.addClass('tncWidgetMargin');

		self.$panel = $new('div', self.$margin);
		self.$panel.addClass('tncWidgetPanel');

		self.$frame = $new('div', self.$panel);
		self.$frame.addClass('tncWidgetFrame');

		if (self.data.dataType !== 'knockout' && self.data.dataType !== 'image') {
			self.$frame.css('overflow', 'hidden'); // for borders;
		}

		self.$margin.css({
			position: 'absolute',
			width: 'auto',
			height: 'auto'
		});
		self.margin.applyMargin(self.$margin);

		self.hide();

		var dataType = getStr(self.data.dataType, '').toLowerCase();
		if (dataType !== 'knockout' && dataType !== 'image') {
			self.$panel.css({
				position: 'absolute',
				width: '100%',
				height: '100%'
			});
			self.border.applyBorder(self.$panel);

			self.padding.applyMargin(self.$frame);
			self.$frame.css({
				position: 'absolute',
				width: 'auto',
				height: 'auto',
				backgroundColor: self.backgroundColor,
				opacity: self.opacity,
				'box-sizing': 'border-box'
			});
		} else {
			self.$frame.css({
				position: 'absolute',
				width: '100%',
				height: '100%'
			});
		}

		self.data.load(self.$frame, self.widgetIndex)
			.then(function() {
				self._isLoaded.resolve();
			})
			.catch(function() {
				self._isLoaded.reject();
			});
		return self._isLoaded.promise;
	};

	TncWidget.prototype.show = function() {
		var self = this;
		self.$element.css({
			opacity: self.opacity
		});
	};

	TncWidget.prototype.hide = function() {
		var self = this;
		self.$element.css({
			opacity: 0.001
		});
	};

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

		return {
			backgroundColor: self.backgroundColor,
			border: self.border.getSettings(),
			opacity: self.opacity,
			transition: self.transition.getSettings(),
			data: self.data.getSettings(),
			duration: self.duration,
			id: self.id,
			name: self.name,
			widgetType: self.widgetType,
			sorting: self.sorting
		};
	};

	TncWidget.prototype.getDuration = function() {
		var self = this;

		var duration = 0;
		// duration += self.transition.duration; // transition is included in
		// widget duration
		duration += self.duration;

		return duration;
	};

	// TncWidget.prototype.beforePlay = function(callback) {
	// 	var self = this;
	//
	//
	// 	if (self.data) {
	// 		self.data.beforePlay(self.widgetIndex, function() {
	//             callback();
	//         });
	// 	}
	// };

	TncWidget.prototype.afterPlay = function() {
		var self = this;
		log('unloading widget', self.instanceId);

		if (self.data) {
			self.data.afterPlay(self.widgetIndex);

			// It's a widget instance, unload immediately
			self.unload();
		}

		if (self.$element) {
			self.$element.remove();
			self.$element = null;
		}
	};

	TncWidget.prototype.play = function(prev) {
		var self = this;
		return new Promise(function(resolve, reject) {
			//log('playing widget[' + self.widgetIndex + '] type: ' + self.data.dataType + ', ' + self.transition.transitionType);
			log('playing widget', self.instanceId);
			//self.playing = true;
			if (prev) {
				self.transition.run(self.$element, prev.$element);

				setTimeout(function() {
					prev.afterPlay();
				}, self.transition.duration);
			} else {
				self.transition.run(self.$element, null);
			}

			if (self.data) {
				self.data.widgetInstance = self;
				self.data.play(self.widgetIndex).finally(resolve);
			} else {
				setTimeout(function() {
					resolve();
				}, self.transition.duration + self.duration);
			}
		});
	};

	// TncWidget.prototype.playCompleted = function() {
	//     var self = this;
	//     if (self.playing) {
	//         self.playing = false;
	//
	//         if (self.onWidgetInstanceReady) {
	//             self.onWidgetInstanceReady();
	//         }
	//     }
	// };

	TncWidget.prototype.unload = function() {
		var self = this;
		log('unloading widget', self.instanceId);

		if (self.$element) {
			self.$element.remove();
		}

		delete self.$element;
		if (self.$panel) {
			self.$panel.remove();
			delete self.$panel;
		}
		if (self.$frame) {
			self.$frame.remove();
			delete self.$frame;
		}

		del(self, 'margin');
		del(self, 'padding');
		del(self, 'transition');
		del(self, 'border');
		del(self, 'data');

		self.unloaded = true;

		log('Widget unloaded', self.instanceId);
		self = null;
	};

	return TncWidget;
});
