require('./style.scss');
var Component = require('vn/component');

module.exports = new Class({
	Extends: Component
	,Tag: 'htk-repeater'
	,Child: 'model'
	,Properties: {
		/**
		 * The source data model.
		 */
		model: {
			type: Db.Model
			,set(x) {
				this.link({_model: x}, {
					 'status-changed': this._onModelChange
					,'row-deleted': this._onRowDelete
					,'row-updated': this._onRowUpdate
					,'row-inserted': this._onRowInsert
				});
				
				this._onModelChange();
			}
			,get() {
				this._model;
			}
		}
		/**
		 * The identifier for internal iterator.
		 */
		,formId: {
			type: String
			,set(x) {
				this._formId = x;
			}
			,get() {
				this._formId;
			}
		}
		/**
		 * {Function (Vn.BuilderResult, Db.Form)} Function to call after every
		 * box rendering.
		 */
		,renderer: {
			type: Function
			,set(x) {
				this._renderer = x;
			}
			,get() {
				this._renderer;
			}
		}
		/**
		 * Wether to show the model status.
		 */
		,showStatus: {
			type: Boolean
			,set(x) {
				this._showStatus = x;
				this._onModelChange();
			}
			,get() {
				this._showStatus;
			}
		}
		/**
		 * Message that should be displayed when source model is not ready.
		 */
		,emptyMessage: {
			type: String
			,value: null
		}
	}
	
	,_builder: null
	,_formId: 'form'
	,_showStatus: true
	
	,render() {
		var div = this.createRoot('div');
		
		this._container = this.createElement('div');
		this._container.className = 'htk-repeater';
		div.appendChild(this._container);
	}
	
	,loadXml(scope, node) {
		Component.prototype.loadXml.call(this, scope, node);
		this._parentScope = scope;

		var builder = this._builder = new Vn.Builder();
		builder.compileNode(node.firstElementChild, [this._formId]);

		this._onModelChange();
	}
	
	,getChild(index) {
		return this._container.childNodes[index];
	}
	
	,getBuilder(index) {
		return this._childsData[index].scope;
	}
	
	,getForm(index) {
		return this._childsData[index].set;
	}
	
	,_buildBox(index) {
		var set = new Db.SimpleIterator({
			model: this._model,
			row: index
		});

		var scope = this._builder.load(this.doc, null, this._parentScope);
		scope.link({
			$iter: set,
			[this._formId]: set.getObject()
		});

		this._childsData.push({scope, set});
		
		if (this._renderer)
			this._renderer(scope, set);
			
		return scope.getMain();
	}

	,_onModelChange() {
		if (!this._model || !this._builder)
			return;

		this.node.removeChild(this._container);
		Vn.Node.removeChilds(this._container);

		this._freeChildsData();
		this._childsData = [];

		switch (this._model.status) {
			case Db.Model.Status.READY:
			{
				for (var i = 0; i < this._model.numRows; i++)
					this._container.appendChild(this._buildBox(i));

				this._showNoRecordsFound();
				break;
			}
			case Db.Model.Status.LOADING:
				this._showMessage(_('Loading'), null);
				break;
			case Db.Model.Status.CLEAN:
			{	
				var emptyMessage = this.emptyMessage ?
					this.emptyMessage : _('NoData');
				this._showMessage(emptyMessage, 'refresh');
				break;
			}
			case Db.Model.Status.ERROR:
				this._showMessage(_('ErrorLoadingData'), 'error');
				break;
		}
	
		this.node.appendChild(this._container);
		this.emit('change');
	}

	,_showNoRecordsFound() {
		if (this._model.numRows === 0)
			this._showMessage(_('EmptyList'), 'block');
	}

	,_showMessage(message, src) {
		if (!this._showStatus)
			return;

		var div =  this.createElement('div');
		div.className = 'message';
		this._container.appendChild(div);
		
		if (src) {
			const icon = new Htk.Icon({
				name: src
			});
			div.appendChild(icon.node);
		} else {
			var spinner = new Htk.Spinner();
			spinner.start();
			div.appendChild(spinner.node);
		}
		
		div.appendChild(this.createTextNode(message));
	}

	,_onRowDelete(model, row) {
		Vn.Node.remove(this._container.childNodes[row]);
		this._unrefChildData(row);
		this._childsData.splice(row, 1);		
		
		for (var i = row; i < this._model.numRows; i++)
			this._childsData[i].set.row = i;
		
		this._showNoRecordsFound();
	}

	,_onRowUpdate(model, row) {
		this._childsData[row].set.iterChanged();
	}

	,_onRowInsert(model, row) {
		var box = this._buildBox(row);
		this._container.appendChild(box);
	}
	
	,_freeChildsData() {
		if (this._childsData)
		for (var i = 0; i < this._childsData.length; i++)
			this._unrefChildData(i);
	}
	
	,_unrefChildData(index) {
		var childData = this._childsData[index];
		childData.set._destroy();
		childData.scope._destroy();
	}
	
	,_destroy() {
		this._freeChildsData();
		Component.prototype._destroy.call(this);
	}
});

