var Template = Class.extend({
	init: function(data, targetActions, pageId)
	{
		this.id = 0;
		this.page = null;
		this.name = '';
		this.description = '';
		this.type = 0;
		this.width = 0;
		this.columns = 0;
		this.gutterWidth = 0;
		this.color = '';
		this.layouts = [];
		this.breakpoints = [];
		this.css = [];
		this.item = null;
		this.toolbar = null;
		this.currentBreakpoint = '';
		this.droppable = null;
		this.sortable = null;
		this.resizable = null;
		this.layoutsInfo = {};
		this.layoutObjects = {};
		this.removedLayouts = [];
        this.defaultLocale = null;
        this.locales = [];
        this.contentManager = null;
        this.mode = Template.STRUCTURE_MODE;
        this.widgetsStorage = new WidgetStorage();
        this.countLayouts = 0;
        this.allWidgetsLoaded = false;

		if (typeof(pageId) !== "undefined") {
			this.page = pageId;
		}
		if (typeof(data) !== "undefined") {
			this.load(data);
		}
		this.actions = new Actions(targetActions, this);
        this.keyboardShortcutsManager = new ShortcutsManager();
	},
	getTarget: function()
	{
		return this.item;
	},
	load: function(data)
	{
		var $this = this;

		try {
			if (typeof(data) == "string") {
				data = jQuery.parseJSON(data);
			}
			$this.id = data.id;
			$this.name = data.name;
			$this.description = data.description;
			$this.type = data.type;
			$this.width = data.width;
			$this.columns = data.columns;
			$this.gutterWidth = data.gutterWidth;
			$this.color = data.color;
            $this.defaultLocale = data.defaultLocale;
            $this.locales = data.locales;

			$this.breakpoints = [];
			jQuery.each(data.breakpoints, function(index, breakpointData){
				$this.breakpoints.push(
					new Breakpoint(breakpointData)
				);
			});
			$this.css = [];
			jQuery.each(data.css, function(index, cssData){
				$this.css.push(
					new Css(cssData)
				);
			});
			$this.layouts = [];
			jQuery.each(data.layouts, function(index, layoutData){
				$this.layouts.push(
					new Layout($this, layoutData)
				);
			});
		} catch(error) {
            console.log(error);
		}
	},
	render: function(target)
	{
		var $this = this;

		target.data('object', $this);
		target.data('resizing', false);
		target.off("mouseenter mouseleave");
		target.hover(function(){
			if (!target.data('resizing')) {
				jQuery(this).addClass('select-hover');
			}
		}, function(){
			if (!target.data('resizing')) {
				jQuery(this).removeClass('select-hover');
				$this.removeToolbar();
			}
		});
		$this.item = target;
		jQuery.each($this.layouts, function(index, layout){
			layout.render();
		});
        $this.countLayouts = target.find(Layout.TAG).length;
        $this.widgetsStorage.bindObjects();

        if(CSWidgetQueue.size() > 0) {
            $this.actions.save(false);
            $this.actions.publish(false);
        }

        CSWidgetQueue.dispatch(WidgetQueue.PROCESS_QUEUE_EVENT);

        this.keyboardShortcutsManager
            .setItem(this.getTarget())
            .initKeyboard();
	},
	update: function(changed)
	{
		this.actions.refresh(changed);
	},
	resetUpdate: function()
	{
		jQuery.each(this.layouts, function(index, layout){
			layout.resetUpdate();
		});
		this.actions.refresh(false);
	},
	enable: function(onlyResizable)
	{
        if(this.mode === Template.CONTENT_MODE) {
            this.getContentManager().enableContentMode();
        } else if(this.mode === Template.STRUCTURE_MODE){
            if (!onlyResizable) {
                jQuery('body').disableSelection();
                this.getTarget().addClass(Template.EDITABLE_CLASS);
                jQuery.each(this.layouts, function(index, layout){
                    layout.enable();
                });
                this.enableDroppable();
            }
            this.enableResizable();
        }
	},
	disable: function(onlyResizable, lock)
	{
        if (!onlyResizable) {
			jQuery('body').enableSelection();
			this.getTarget().removeClass(Template.EDITABLE_CLASS);
			jQuery.each(this.layouts, function(index, layout){
				layout.disable(lock);
			});

			CSTemplate.disableDroppable();
			this.removeToolbar();
            this.getContentManager().cleanLayouts();
            this.getContentManager().removeCSS();
		}
		this.disableResizable();
	},
	focusItem: function(item)
	{
		jQuery('html,body').animate({
			scrollTop: item.offset().top
		}, 400);
	},
	getHtml: function()
	{
		return this.getTarget().html();
	},
	setHtml: function(html)
	{
		this.getTarget().html(html);
		this.refreshLayouts();
		this.enable();
		this.update(true);
        this.attachWidgets(this.getTarget());
	},
	resize: function(locked, item)
	{
		if (locked) {
			this.disableToolbar();
			this.resizeLayout(item);
		} else {
			this.resizeLayout(item, true);
			this.enableToolbar();
		}
		this.getTarget().data('resizing', locked);
	},
	disableToolbar: function()
	{
		this.getToolbar().disable();
	},
	enableToolbar: function()
	{
		this.getToolbar().enable();
	},
	changeBreakpoint: function(type)
	{
		this.currentBreakpoint = type;
	},
	columnsClass: function(columns, type)
	{
		if (!columns) {
			columns = this.columns;
		}
		if (!type) {
			type = this.currentBreakpoint;
		}

		return Breakpoint.PREFIX_COLUMNS_CLASS+'-'+type+'-'+columns;
	},
	hiddenClass: function(type)
	{
		if (!type) {
			type = this.currentBreakpoint;
		}

		return Breakpoint.PREFIX_HIDDEN_CLASS+'-'+type;
	},
	extractBreakpoints: function(classes)
	{
		var columnsClassRE = new RegExp(Breakpoint.PREFIX_COLUMNS_CLASS+"-(\\w+)-(\\d+)");
		var hiddenClassRE = new RegExp(Breakpoint.PREFIX_HIDDEN_CLASS+"-(\\w+)");
		var breakpoints = {};

		jQuery.each(classes, function(index, value){
			var matches = columnsClassRE.exec(value);
			if(matches) {
				breakpoints[matches[1]] = matches[2];
			} else {
				matches = hiddenClassRE.exec(value);
				if(matches) {
					breakpoints[matches[1]] = false;
				}
			}
		});

		return breakpoints;
	},
	generateLayout: function(columns)
	{
		var $this = this;
		var item = Layout.prototype.generateItem();

		jQuery.each($this.breakpoints, function(index, breakpoint){
			item.addClass(
				$this.columnsClass(columns, breakpoint.type)
			);
		});

		return item;
	},
	getBreakpoint: function(type)
	{
		var result = null;

		jQuery.each(this.breakpoints, function(index, breakpoint){
			if (breakpoint.type == type) {
				result = breakpoint;
				return false;
			}
		});

		return result;
	},
	addLayout: function(item, render, force)
	{
		var layout, id, data;

		if (item.children(Layout.TAG).length > 0) {
			item = item.children(Layout.TAG);
		}
		data = item.data('info');
		if (typeof(data) == "string") {
			data = jQuery.parseJSON(data);
		}

		if ((data && data.id) || item.attr('id') || item.attr('tmpId')) {
			if ((data && data.id)) {
				id = data.id;
			} else {
				id = (item.attr('id') ? item.attr('id') : item.attr('tmpId'));
			}
			layout = this.getLayoutObject(id);
		}
		if (layout) {
			if (force) {
				this.layouts.push(layout);
				if (item.html() != '' || layout.children.length == 0) {
					layout.html = item.html();
				}
			}
			layout.defineAttributes(item, true);
			item.find(Layout.TAG).each(function(){
				if (
					jQuery(this).parentsUntil(item, Layout.TAG).length == 0
				) {
					layout.addLayout(jQuery(this), false, force, true);
				}
			});
		} else {
			layout = new Layout(this, data);
			this.layouts.push(layout);
			layout.defineAttributes(item);
			if (!layout.id || force) {
				item.find(Layout.TAG).each(function(){
					if (
						jQuery(this).parentsUntil(item, Layout.TAG).length == 0
					) {
						layout.addLayout(jQuery(this), false, force, true);
					}
				});
			}
		}
		layout.enable();
		if (render) {
			layout.render();
		}
        layout.update(true);
        this.cleanRemovedLayout(layout);

        return layout;
	},
	removeLayout: function(layout, removeObject)
	{
		var index = this.layouts.indexOf(layout);
		if (index >= 0) {
			this.layouts.splice(index, 1);
		}

		if (layout.id > 0 && removeObject) {
			this.removedLayouts.push(
				layout.id
			);
			this.removeLayoutObject(layout);
		}
	},
	cleanRemovedLayout: function(layout)
	{
		var index;

		if (layout && layout.id) {
			index = this.removedLayouts.indexOf(
				layout.id
			);
			if (index >= 0) {
				this.removedLayouts.splice(
					index, 1
				);
			}
		}
	},
	refreshLayouts: function()
	{
		var $this = this;

		jQuery.each($this.layouts, function(index, layout){
			if (layout) {
				$this.removedLayouts.push(
					layout.id
				);
			}
		});

		$this.layouts = [];
		$this.resetLayoutObjects();
		$this.getTarget().find(Layout.TAG).each(function(){
			if (
				jQuery(this).parentsUntil(
					$this.getTarget(), Layout.TAG
				).length == 0
			) {
				$this.addLayout(jQuery(this), true, true, true);
			}
		});
	},
	resizeLayout: function(item, finished)
	{
		jQuery(item)
			.data('object')
			.resize(finished);
	},
	updateSizesLayout: function(item, sizes)
	{
		jQuery(item)
			.data('object')
			.updateSizes(sizes);
	},
	addLayoutInfo: function(data, item)
	{
		if (data && data.id) {
			this.layoutsInfo[data.id] = data;
			if (item) {
				this.layoutObjects[data.id] = item;
			}
		}
		if (item) {
			if (item.id) {
				this.layoutObjects[item.id] = item;
			}
			if (item.tmpId) {
				this.layoutObjects[item.tmpId] = item;
			}
		}
	},
	getLayoutInfo: function(id)
	{
		var result = null;

		if (id && this.layoutsInfo[id]) {
			result = this.layoutsInfo[id];
		}

		return result;
	},
	getLayoutObject: function(id)
	{
		var result = null;

		if (id && this.layoutObjects[id]) {
			result = this.layoutObjects[id];
		}

		return result;
	},
	resetLayoutObjects: function()
	{
		jQuery.each(this.layoutObjects, function (index, layout) {
			if (layout) {
				layout.resetTargets();
				layout.resetChildren();
			}
		});
	},
	removeLayoutObject: function(layout)
	{
		if (this.layoutObjects[layout.id]) {
			this.layoutObjects[layout.id] = null;
		}
		if (this.layoutObjects[layout.tmpId]) {
			this.layoutObjects[layout.tmpId] = null;
		}
	},
	getToolbar: function(check)
	{
		if (!check && !this.toolbar) {
			this.toolbar = new Toolbar(this.getTarget());
		}
		return this.toolbar;
	},
	removeToolbar: function()
	{
		if (this.getToolbar(true)) {
			this.getToolbar().reset();
			this.toolbar = null;
		}
	},
	enableDroppable: function(onlyRoot)
	{
		var $target = this;

		$target.disableDroppable();
		if (onlyRoot) {
			$target.droppable = $target.getTarget();
		} else {
			$target.droppable = $target.getTarget().parent().find(
				'.'+Template.EDITABLE_CLASS+', .'+Layout.ROW_CLASS
			);
		}

        $target.droppable = $target.droppable.CSDroppable({
            excludedSelectors: Template.DROPPABLE_EXCLUDED_SELECTORS,
            dragenter: function(){},
            dragover: function(){},
            dragleave: function(){},
            drop: this.dropCallback,
            hoverClass: Template.HOVER_CLASS
        });
	},
	disableDroppable: function()
	{
		if (this.droppable) {
			try {
				this.droppable.CSDroppable('destroy');
				this.droppable = null;
			} catch(error){}
		}
	},
	enableResizable: function()
	{
		var $this = this;

		$this.resizable = $this.getTarget().find(
			Layout.TAG+':not(.'+Layout.BLOCKED_CLASS+')'
		).resizable({
			start: function(event, data){
				console.log("Resize...");

                event.stopPropagation();

                if (jQuery(event.srcElement).hasClass('ui-resizable-s')) {
                	jQuery(data.element)
                		.data('resize-type', Template.RESIZE_HEIGHT);
                } else {
                	jQuery(data.element)
                		.data('resize-type', Template.RESIZE_WIDTH);
                }

                jQuery(data.element)
					.data('object')
					.selectTarget(jQuery(data.element));
				$this.resize(true, data.element);
			},
			resize: function(event, data){
				$this.updateSizesLayout(
					data.element, data.size
				);
			},
			stop: function(event, data){
				console.log("Stop resizable...");
				$this.resize(false, data.element);
			},
            handles: "e, s"
		});
	},
	disableResizable: function()
	{
		if (this.resizable) {
			try {
				this.resizable.resizable('destroy');
				this.resizable = null;
			} catch(error){
				console.log(error);
			}
		}
	},
	export: function()
	{
		var item = {};

		this.disable();

		item.id = this.id;
		item.name = this.name;
		item.description = this.description;
		item.type = this.type;
		item.width = this.width;
		item.columns = this.columns;
		item.gutterWidth = this.gutterWidth;
		item.color = this.color;
		//item.css = this.css;
		item.removedLayouts = this.removedLayouts;

		item.layouts = [];
		jQuery.each(this.layouts, function(index, layout){
			if (layout) {
				item.layouts.push(
					layout.export()
				);
			}
		});
		item.code = this.cleanCode(
			this.getTarget().html()
		);

		this.enable();

		return item;
	},
	json: function()
	{
		return JSON.stringify(
			this.export()
		);
	},
    exportLayoutContent: function(layouts)
    {
        var $this = this;
        jQuery.each(layouts, function(index, layout){
        	var id = (layout.id > 0 ? layout.id : layout.tmpId);

            $this.contents[id] = layout.exportContent();
            $this.exportLayoutContent(layout.children);
        });
    },
    exportContent: function()
    {
		this.disable();

        this.contents = {};
        this.exportLayoutContent(this.layouts);

		this.enable();

        return this.contents;
    },
    jsonContent: function()
    {
        return JSON.stringify(
            this.exportContent()
        );
    },
	cleanCode: function(code)
	{
		var search = new RegExp('></'+Layout.TAG+'>', 'g');
        var _self = this;
		code = jQuery('<div/>', {
			'html': code
		});
		code.find(Layout.TAG).empty();
		code.find(Layout.TAG).removeAttr('style draggable');
		code.find('.'+Template.EDITABLE_CLASS).removeClass('.'+Template.EDITABLE_CLASS);
        code.find(Widget.TAG).each(function() {
            var object = jQuery( '#'+jQuery(this).attr('id'), _self.getTarget()).data('object');
            jQuery(this).replaceWith(object.render(true));
        });
		return code.html().replace(search, ' />');
	},
    dropCallback: function(event, item)
    {
        var layout, $this = jQuery(this);

        if($this.is('.'+Layout.ROW_CLASS) && item.is('.'+Layout.ROW_CLASS)) {
            item = item.children().unwrap();
        } else {
            jQuery(item).CSDroppable({drop: Template.prototype.dropCallback, excludedSelectors: Template.DROPPABLE_EXCLUDED_SELECTORS});

            if(!item.is(Layout.TAG)) {
                item = jQuery(item).children(Layout.TAG).first();
            }
        }

        $this = $this.closest(Layout.TAG);

        if($this.length === 0){
            $this = $this.end().closest(Template.SELECTOR);
        }

    	if ($this.data('object').selectTarget) {
    		$this.data('object').selectTarget($this);
    	}

    	if (
    		!item.attr('id') && !item.attr('tmpId') &&
    		$this.children(
    			':not('+Template.DROPPABLE_EXCLUDED_SELECTORS+')'
    		).length === 1 &&
    		$this.prop("tagName").toLowerCase() == Layout.TAG
    	) {
            item.children(Layout.TAG).unwrap().each(function(index, child){
                $this.data('object').addLayout(jQuery(child), true, true);
            });
    	} else {
            layout = $this.data('object').addLayout(item, true);
            layout.template.attachWidgets(item);
            layout.updateParent();
        }

        jQuery(item).data('info', null);
        jQuery(item).removeAttr('data-info');

        if(!jQuery(item).parent().is('.'+Layout.ROW_CLASS)) {
            jQuery(item)
                .wrap('<div class="'+Layout.ROW_CLASS+'" />')
                .parent()
                .CSDroppable({drop: Template.prototype.dropCallback, excludedSelectors: Template.DROPPABLE_EXCLUDED_SELECTORS});
        }

        //CLEAN EMPTY ROW DIV'S
        jQuery('.'+Layout.ROW_CLASS+':empty', $this.closest(Template.SELECTOR)).remove();
    },
    dragOverCallback : function(event, target)
    {
        var ids = jQuery.CSDragAndDropStore.customData['non-droppable-layouts'];
        var object = jQuery(target).closest(Layout.TAG).data('object');

        if(object !== null && object.id !== 0 && ids.length > 0 && ids.indexOf(object.id) > -1){
            event.dataTransfer.dropEffect = 'none';
        }
    },
    setContentManager: function(contentManager)
    {
        this.contentManager = contentManager;
    },
    getContentManager: function()
    {
        return this.contentManager;
    },
    getCountLayouts: function()
    {
        this.countLayouts++;
        return this.countLayouts;
    },
    setMode: function(mode)
    {
        if([Template.CONTENT_MODE, Template.STRUCTURE_MODE].indexOf(mode) === -1) {
            throw 'invalid mode exception';
        }

        this.mode = mode;
    },
    attachWidgets: function(node)
    {
        var _self = this,
            predefined = node.data('info') ? true : false;

        _self._attatchWidgetsOfLayout(node, predefined);
        if(predefined) {
            CSWidgetQueue.dispatch(WidgetQueue.PROCESS_QUEUE_IN_RUNTIME_EVENT);
        }
    },
    _attatchWidgetsOfLayout: function(node, predefined)
    {
        var _self = this;

        jQuery(Widget.TAG, node).each(function(){
            var widget;
            if(predefined) {
                widget = _self.widgetsStorage.getBySpecificField('uuid', jQuery(this).prop('id'));
                widget.item = jQuery(this);
            } else {
                widget = _self.widgetsStorage.get(jQuery(this).prop('id'));
            }

            jQuery(this).data('object', widget);
        });
    },
    getDefaultLocale: function (acronym)
    {
    	var locale;

    	if (!this.defaultLocale) {
    		jQuery.each(this.locales, function(index, item){
    			locale = (acronym ? item.locale : item);
    			return false;
    		});
    	} else {
    		locale = (acronym ? this.defaultLocale.locale : this.defaultLocale);
    	}

    	return locale;
    }
});

Template.SELECTOR = '.cscontent';
Template.EDITABLE_CLASS = 'editable_item';
Template.DRAGGABLE_CLASS = 'draggable';
Template.HOVER_CLASS = 'dropzone-hover';
Template.DROPPABLE_EXCLUDED_SELECTORS = ':not(cslayout)';
Template.RESIZE_HEIGHT = 1;
Template.RESIZE_WIDTH = 2;
Template.STRUCTURE_MODE = 1;
Template.CONTENT_MODE = 2;
Template.LOADING_CLASS = 'csd-loading';

