
//public column object
var ColumnComponent = function (column){
	this._column = column;
	this.type = "ColumnComponent";
};

ColumnComponent.prototype.getElement = function(){
	return this._column.getElement();
};

ColumnComponent.prototype.getDefinition = function(){
	return this._column.getDefinition();
};

ColumnComponent.prototype.getField = function(){
	return this._column.getField();
};

ColumnComponent.prototype.getCells = function(){
	var cells = [];

	this._column.cells.forEach(function(cell){
		cells.push(cell.getComponent());
	});

	return cells;
};

ColumnComponent.prototype.getVisibility = function(){
	console.warn("getVisibility function is deprecated, you should now use the isVisible function");
	return this._column.visible;
};

ColumnComponent.prototype.isVisible = function(){
	return this._column.visible;
};

ColumnComponent.prototype.show = function(){
	if(this._column.isGroup){
		this._column.columns.forEach(function(column){
			column.show();
		});
	}else{
		this._column.show();
	}
};

ColumnComponent.prototype.hide = function(){
	if(this._column.isGroup){
		this._column.columns.forEach(function(column){
			column.hide();
		});
	}else{
		this._column.hide();
	}
};

ColumnComponent.prototype.toggle = function(){
	if(this._column.visible){
		this.hide();
	}else{
		this.show();
	}
};

ColumnComponent.prototype.delete = function(){
	return this._column.delete();
};

ColumnComponent.prototype.getSubColumns = function(){
	var output = [];

	if(this._column.columns.length){
		this._column.columns.forEach(function(column){
			output.push(column.getComponent());
		});
	}

	return output;
};

ColumnComponent.prototype.getParentColumn = function(){
	return this._column.parent instanceof Column ? this._column.parent.getComponent() : false;
};


ColumnComponent.prototype._getSelf = function(){
	return this._column;
};

ColumnComponent.prototype.scrollTo = function(){
	return this._column.table.columnManager.scrollToColumn(this._column);
};

ColumnComponent.prototype.getTable = function(){
	return this._column.table;
};

ColumnComponent.prototype.headerFilterFocus = function(){
	if(this._column.table.modExists("filter", true)){
		this._column.table.modules.filter.setHeaderFilterFocus(this._column);
	}
};

ColumnComponent.prototype.reloadHeaderFilter = function(){
	if(this._column.table.modExists("filter", true)){
		this._column.table.modules.filter.reloadHeaderFilter(this._column);
	}
};

ColumnComponent.prototype.getHeaderFilterValue = function(){
	if(this._column.table.modExists("filter", true)){
		return this._column.table.modules.filter.getHeaderFilterValue(this._column);
	}
};

ColumnComponent.prototype.setHeaderFilterValue = function(value){
	if(this._column.table.modExists("filter", true)){
		this._column.table.modules.filter.setHeaderFilterValue(this._column, value);
	}
};

ColumnComponent.prototype.move = function(to, after){
	var toColumn = this._column.table.columnManager.findColumn(to);

	if(toColumn){
		this._column.table.columnManager.moveColumn(this._column, toColumn, after)
	}else{
		console.warn("Move Error - No matching column found:", toColumn);
	}
};

ColumnComponent.prototype.getNextColumn = function(){
	var nextCol = this._column.nextColumn();

	return nextCol ? nextCol.getComponent() : false;
};

ColumnComponent.prototype.getPrevColumn = function(){
	var prevCol = this._column.prevColumn();

	return prevCol ? prevCol.getComponent() : false;
};

ColumnComponent.prototype.updateDefinition = function(updates){
	return this._column.updateDefinition(updates);
};


ColumnComponent.prototype.getWidth = function(){
	return this._column.getWidth();
};


ColumnComponent.prototype.setWidth = function(width){
	var result;

	if(width === true){
		result =  this._column.reinitializeWidth(true);
	}else{
		result =  this._column.setWidth(width);
	}

	if(this._column.table.options.virtualDomHoz){
		this._column.table.vdomHoz.reinitialize(true);
	}

	return result;
};

ColumnComponent.prototype.validate = function(){
	return this._column.validate();
};



var Column = function(def, parent){
	var self = this;

	this.table = parent.table;
	this.definition = def; //column definition
	this.parent = parent; //hold parent object
	this.type = "column"; //type of element
	this.columns = []; //child columns
	this.cells = []; //cells bound to this column
	this.element = this.createElement(); //column header element
	this.contentElement = false;
	this.titleHolderElement = false;
	this.titleElement = false;
	this.groupElement = this.createGroupElement(); //column group holder element
	this.isGroup = false;
	this.tooltip = false; //hold column tooltip
	this.hozAlign = ""; //horizontal text alignment
	this.vertAlign = ""; //vert text alignment

	//multi dimensional filed handling
	this.field ="";
	this.fieldStructure = "";
	this.getFieldValue = "";
	this.setFieldValue = "";

	this.titleFormatterRendered = false;

	this.setField(this.definition.field);

	if(this.table.options.invalidOptionWarnings){
		this.checkDefinition();
	}

	this.modules = {}; //hold module variables;

	this.cellEvents = {
		cellClick:false,
		cellDblClick:false,
		cellContext:false,
		cellTap:false,
		cellDblTap:false,
		cellTapHold:false,
		cellMouseEnter:false,
		cellMouseLeave:false,
		cellMouseOver:false,
		cellMouseOut:false,
		cellMouseMove:false,
	};

	this.width = null; //column width
	this.widthStyled = ""; //column width prestyled to improve render efficiency
	this.maxWidth = null; //column maximum width
	this.maxWidthStyled = ""; //column maximum prestyled to improve render efficiency
	this.minWidth = null; //column minimum width
	this.minWidthStyled = ""; //column minimum prestyled to improve render efficiency
	this.widthFixed = false; //user has specified a width for this column

	this.visible = true; //default visible state

	this.component = null;

	this._mapDepricatedFunctionality();

	//initialize column
	if(def.columns){

		this.isGroup = true;

		def.columns.forEach(function(def, i){
			var newCol = new Column(def, self);
			self.attachColumn(newCol);
		});

		self.checkColumnVisibility();
	}else{
		parent.registerColumnField(this);
	}

	if(def.rowHandle && this.table.options.movableRows !== false && this.table.modExists("moveRow")){
		this.table.modules.moveRow.setHandle(true);
	}

	this._buildHeader();

	this.bindModuleColumns();
};

Column.prototype.createElement = function (){
	var el = document.createElement("div");

	el.classList.add("tabulator-col");
	el.setAttribute("role", "columnheader");
	el.setAttribute("aria-sort", "none");

	return el;
};

Column.prototype.createGroupElement = function (){
	var el = document.createElement("div");

	el.classList.add("tabulator-col-group-cols");

	return el;
};

Column.prototype.checkDefinition = function(){
	Object.keys(this.definition).forEach((key) => {
		if(this.defaultOptionList.indexOf(key) === -1){
			console.warn("Invalid column definition option in '" + (this.field || this.definition.title) + "' column:", key)
		}
	});
};

Column.prototype.setField = function(field){
	this.field = field;
	this.fieldStructure = field ? (this.table.options.nestedFieldSeparator ? field.split(this.table.options.nestedFieldSeparator) : [field]) : [];
	this.getFieldValue = this.fieldStructure.length > 1 ? this._getNestedData : this._getFlatData;
	this.setFieldValue = this.fieldStructure.length > 1 ? this._setNestedData : this._setFlatData;
};

//register column position with column manager
Column.prototype.registerColumnPosition = function(column){
	this.parent.registerColumnPosition(column);
};

//register column position with column manager
Column.prototype.registerColumnField = function(column){
	this.parent.registerColumnField(column);
};

//trigger position registration
Column.prototype.reRegisterPosition = function(){
	if(this.isGroup){
		this.columns.forEach(function(column){
			column.reRegisterPosition();
		});
	}else{
		this.registerColumnPosition(this);
	}
};

Column.prototype._mapDepricatedFunctionality = function(){
	if(typeof this.definition.hideInHtml !== "undefined"){
		this.definition.htmlOutput = !this.definition.hideInHtml;
		console.warn("hideInHtml column definition property is deprecated, you should now use htmlOutput")
	}

	if(typeof this.definition.align !== "undefined"){
		this.definition.hozAlign = this.definition.align;
		console.warn("align column definition property is deprecated, you should now use hozAlign");
	}

	if(typeof this.definition.downloadTitle !== "undefined"){
		this.definition.titleDownload = this.definition.downloadTitle;
		console.warn("downloadTitle definition property is deprecated, you should now use titleDownload");
	}
};

Column.prototype.setTooltip = function(){
	var self = this,
	def = self.definition;

	//set header tooltips
	var tooltip = def.headerTooltip || def.tooltip === false  ? def.headerTooltip : self.table.options.tooltipsHeader;

	if(tooltip){
		if(tooltip === true){
			if(def.field){
				self.table.modules.localize.bind("columns|" + def.field, function(value){
					self.element.setAttribute("title", value || def.title);
				});
			}else{
				self.element.setAttribute("title", def.title);
			}

		}else{
			if(typeof(tooltip) == "function"){
				tooltip = tooltip(self.getComponent());

				if(tooltip === false){
					tooltip = "";
				}
			}

			self.element.setAttribute("title", tooltip);
		}

	}else{
		self.element.setAttribute("title", "");
	}
};

//build header element
Column.prototype._buildHeader = function(){
	var self = this,
	def = self.definition;

	while(self.element.firstChild) self.element.removeChild(self.element.firstChild);

	if(def.headerVertical){
		self.element.classList.add("tabulator-col-vertical");

		if(def.headerVertical === "flip"){
			self.element.classList.add("tabulator-col-vertical-flip");
		}
	}

	self.contentElement = self._bindEvents();

	self.contentElement = self._buildColumnHeaderContent();

	self.element.appendChild(self.contentElement);

	if(self.isGroup){
		self._buildGroupHeader();
	}else{
		self._buildColumnHeader();
	}

	self.setTooltip();

	//set resizable handles
	if(self.table.options.resizableColumns && self.table.modExists("resizeColumns")){
		self.table.modules.resizeColumns.initializeColumn("header", self, self.element);
	}

	//set resizable handles
	if(def.headerFilter && self.table.modExists("filter") && self.table.modExists("edit")){
		if(typeof def.headerFilterPlaceholder !== "undefined" && def.field){
			self.table.modules.localize.setHeaderFilterColumnPlaceholder(def.field, def.headerFilterPlaceholder);
		}

		self.table.modules.filter.initializeColumn(self);
	}


	//set resizable handles
	if(self.table.modExists("frozenColumns")){
		self.table.modules.frozenColumns.initializeColumn(self);
	}

	//set movable column
	if(self.table.options.movableColumns && !self.isGroup && self.table.modExists("moveColumn")){
		self.table.modules.moveColumn.initializeColumn(self);
	}

	//set calcs column
	if((def.topCalc || def.bottomCalc) && self.table.modExists("columnCalcs")){
		self.table.modules.columnCalcs.initializeColumn(self);
	}

	//handle persistence
	if(self.table.modExists("persistence") && self.table.modules.persistence.config.columns){
		self.table.modules.persistence.initializeColumn(self);
	}


	//update header tooltip on mouse enter
	self.element.addEventListener("mouseenter", function(e){
		self.setTooltip();
	});
};

Column.prototype._bindEvents = function(){

	var self = this,
	def = self.definition,
	dblTap,	tapHold, tap;

	//setup header click event bindings
	if(typeof(def.headerClick) == "function"){
		self.element.addEventListener("click", function(e){def.headerClick(e, self.getComponent());});
	}

	if(typeof(def.headerDblClick) == "function"){
		self.element.addEventListener("dblclick", function(e){def.headerDblClick(e, self.getComponent());});
	}

	if(typeof(def.headerContext) == "function"){
		self.element.addEventListener("contextmenu", function(e){def.headerContext(e, self.getComponent());});
	}

	//setup header tap event bindings
	if(typeof(def.headerTap) == "function"){
		tap = false;

		self.element.addEventListener("touchstart", function(e){
			tap = true;
		}, {passive: true});

		self.element.addEventListener("touchend", function(e){
			if(tap){
				def.headerTap(e, self.getComponent());
			}

			tap = false;
		});
	}

	if(typeof(def.headerDblTap) == "function"){
		dblTap = null;

		self.element.addEventListener("touchend", function(e){

			if(dblTap){
				clearTimeout(dblTap);
				dblTap = null;

				def.headerDblTap(e, self.getComponent());
			}else{

				dblTap = setTimeout(function(){
					clearTimeout(dblTap);
					dblTap = null;
				}, 300);
			}

		});
	}

	if(typeof(def.headerTapHold) == "function"){
		tapHold = null;

		self.element.addEventListener("touchstart", function(e){
			clearTimeout(tapHold);

			tapHold = setTimeout(function(){
				clearTimeout(tapHold);
				tapHold = null;
				tap = false;
				def.headerTapHold(e, self.getComponent());
			}, 1000);

		}, {passive: true});

		self.element.addEventListener("touchend", function(e){
			clearTimeout(tapHold);
			tapHold = null;
		});
	}

	//store column cell click event bindings
	if(typeof(def.cellClick) == "function"){
		self.cellEvents.cellClick = def.cellClick;
	}

	if(typeof(def.cellDblClick) == "function"){
		self.cellEvents.cellDblClick = def.cellDblClick;
	}

	if(typeof(def.cellContext) == "function"){
		self.cellEvents.cellContext = def.cellContext;
	}

	//store column mouse event bindings
	if(typeof(def.cellMouseEnter) == "function"){
		self.cellEvents.cellMouseEnter = def.cellMouseEnter;
	}

	if(typeof(def.cellMouseLeave) == "function"){
		self.cellEvents.cellMouseLeave = def.cellMouseLeave;
	}

	if(typeof(def.cellMouseOver) == "function"){
		self.cellEvents.cellMouseOver = def.cellMouseOver;
	}

	if(typeof(def.cellMouseOut) == "function"){
		self.cellEvents.cellMouseOut = def.cellMouseOut;
	}

	if(typeof(def.cellMouseMove) == "function"){
		self.cellEvents.cellMouseMove = def.cellMouseMove;
	}

	//setup column cell tap event bindings
	if(typeof(def.cellTap) == "function"){
		self.cellEvents.cellTap = def.cellTap;
	}

	if(typeof(def.cellDblTap) == "function"){
		self.cellEvents.cellDblTap = def.cellDblTap;
	}

	if(typeof(def.cellTapHold) == "function"){
		self.cellEvents.cellTapHold = def.cellTapHold;
	}

	//setup column cell edit callbacks
	if(typeof(def.cellEdited) == "function"){
		self.cellEvents.cellEdited = def.cellEdited;
	}

	if(typeof(def.cellEditing) == "function"){
		self.cellEvents.cellEditing = def.cellEditing;
	}

	if(typeof(def.cellEditCancelled) == "function"){
		self.cellEvents.cellEditCancelled = def.cellEditCancelled;
	}
};

//build header element for header
Column.prototype._buildColumnHeader = function(){
	var def = this.definition,
	table = this.table,
	sortable;

	//set column sorter
	if(table.modExists("sort")){
		table.modules.sort.initializeColumn(this, this.titleHolderElement);
	}

	//set column header context menu
	if((def.headerContextMenu || def.headerClickMenu || def.headerMenu) && table.modExists("menu")){
		table.modules.menu.initializeColumnHeader(this);
	}

	//set column formatter
	if(table.modExists("format")){
		table.modules.format.initializeColumn(this);
	}

	//set column editor
	if(typeof def.editor != "undefined" && table.modExists("edit")){
		table.modules.edit.initializeColumn(this);
	}

	//set colum validator
	if(typeof def.validator != "undefined" && table.modExists("validate")){
		table.modules.validate.initializeColumn(this);
	}


	//set column mutator
	if(table.modExists("mutator")){
		table.modules.mutator.initializeColumn(this);
	}

	//set column accessor
	if(table.modExists("accessor")){
		table.modules.accessor.initializeColumn(this);
	}

	//set respoviveLayout
	if(typeof table.options.responsiveLayout && table.modExists("responsiveLayout")){
		table.modules.responsiveLayout.initializeColumn(this);
	}

	//set column visibility
	if(typeof def.visible != "undefined"){
		if(def.visible){
			this.show(true);
		}else{
			this.hide(true);
		}
	}

	//asign additional css classes to column header
	if(def.cssClass){
		var classeNames = def.cssClass.split(" ");
		classeNames.forEach((className) => {
			this.element.classList.add(className);
		});
	}

	if(def.field){
		this.element.setAttribute("tabulator-field", def.field);
	}

	//set min width if present
	this.setMinWidth(typeof def.minWidth == "undefined" ? this.table.options.columnMinWidth : parseInt(def.minWidth));

	if(def.maxWidth || this.table.options.columnMaxWidth){
		if(def.maxWidth !== false){
			this.setMaxWidth(typeof def.maxWidth == "undefined" ? this.table.options.columnMaxWidth : parseInt(def.maxWidth));
		}
	}

	this.reinitializeWidth();

	//set tooltip if present
	this.tooltip = this.definition.tooltip || this.definition.tooltip === false ? this.definition.tooltip : this.table.options.tooltips;

	//set orizontal text alignment
	this.hozAlign = typeof(this.definition.hozAlign) == "undefined" ? this.table.options.cellHozAlign : this.definition.hozAlign;
	this.vertAlign = typeof(this.definition.vertAlign) == "undefined" ? this.table.options.cellVertAlign : this.definition.vertAlign;

	this.titleElement.style.textAlign = this.definition.headerHozAlign || this.table.options.headerHozAlign;
};

Column.prototype._buildColumnHeaderContent = function(){
	var def = this.definition,
	table = this.table;

	var contentElement = document.createElement("div");
	contentElement.classList.add("tabulator-col-content");

	this.titleHolderElement = document.createElement("div");
	this.titleHolderElement.classList.add("tabulator-col-title-holder");

	contentElement.appendChild(this.titleHolderElement);

	this.titleElement = this._buildColumnHeaderTitle();

	this.titleHolderElement.appendChild(this.titleElement);

	return contentElement;
};

//build title element of column
Column.prototype._buildColumnHeaderTitle = function(){
	var self = this,
	def = self.definition,
	table = self.table,
	title;

	var titleHolderElement = document.createElement("div");
	titleHolderElement.classList.add("tabulator-col-title");

	if(def.editableTitle){
		var titleElement = document.createElement("input");
		titleElement.classList.add("tabulator-title-editor");

		titleElement.addEventListener("click", function(e){
			e.stopPropagation();
			titleElement.focus();
		});

		titleElement.addEventListener("change", function(){
			def.title = titleElement.value;
			table.options.columnTitleChanged.call(self.table, self.getComponent());
		});

		titleHolderElement.appendChild(titleElement);

		if(def.field){
			table.modules.localize.bind("columns|" + def.field, function(text){
				titleElement.value = text || (def.title || "&nbsp;");
			});
		}else{
			titleElement.value  = def.title || "&nbsp;";
		}

	}else{
		if(def.field){
			table.modules.localize.bind("columns|" + def.field, function(text){
				self._formatColumnHeaderTitle(titleHolderElement, text || (def.title || "&nbsp;"));
			});
		}else{
			self._formatColumnHeaderTitle(titleHolderElement, def.title || "&nbsp;");
		}
	}

	return titleHolderElement;
};

Column.prototype._formatColumnHeaderTitle = function(el, title){
	var formatter, contents, params, mockCell, onRendered;

	if(this.definition.titleFormatter && this.table.modExists("format")){

		formatter = this.table.modules.format.getFormatter(this.definition.titleFormatter);

		onRendered = (callback) => {
			this.titleFormatterRendered = callback;
		};

		mockCell = {
			getValue:function(){
				return title;
			},
			getElement:function(){
				return el;
			}
		};

		params = this.definition.titleFormatterParams || {};

		params = typeof params === "function" ? params() : params;

		contents = formatter.call(this.table.modules.format, mockCell, params, onRendered);

		switch(typeof contents){
			case "object":
			if(contents instanceof Node){
				el.appendChild(contents);
			}else{
				el.innerHTML = "";
				console.warn("Format Error - Title formatter has returned a type of object, the only valid formatter object return is an instance of Node, the formatter returned:", contents);
			}
			break;
			case "undefined":
			case "null":
			el.innerHTML = "";
			break;
			default:
			el.innerHTML = contents;
		}
	}else{
		el.innerHTML = title;
	}
};


//build header element for column group
Column.prototype._buildGroupHeader = function(){
	this.element.classList.add("tabulator-col-group");
	this.element.setAttribute("role", "columngroup");
	this.element.setAttribute("aria-title", this.definition.title);

	//asign additional css classes to column header
	if(this.definition.cssClass){
		var classeNames = this.definition.cssClass.split(" ");
		classeNames.forEach((className) => {
			this.element.classList.add(className);
		});
	}

	//set column header context menu
	if ((this.definition.headerContextMenu || this.definition.headerMenu) && this.table.modExists("menu")) {
		this.table.modules.menu.initializeColumnHeader(this);
	}

	this.titleElement.style.textAlign = this.definition.headerHozAlign || this.table.options.headerHozAlign;

	this.element.appendChild(this.groupElement);
};

//flat field lookup
Column.prototype._getFlatData = function(data){
	return data[this.field];
};

//nested field lookup
Column.prototype._getNestedData = function(data){
	var dataObj = data,
	structure = this.fieldStructure,
	length = structure.length,
	output;

	for(let i = 0; i < length; i++){

		dataObj = dataObj[structure[i]];

		output = dataObj;

		if(!dataObj){
			break;
		}
	}

	return output;
};

//flat field set
Column.prototype._setFlatData = function(data, value){
	if(this.field){
		data[this.field] = value;
	}
};

//nested field set
Column.prototype._setNestedData = function(data, value){
	var dataObj = data,
	structure = this.fieldStructure,
	length = structure.length;

	for(let i = 0; i < length; i++){

		if(i == length -1){
			dataObj[structure[i]] = value;
		}else{
			if(!dataObj[structure[i]]){
				if(typeof value !== "undefined"){
					dataObj[structure[i]] = {};
				}else{
					break;
				}
			}

			dataObj = dataObj[structure[i]];
		}
	}
};


//attach column to this group
Column.prototype.attachColumn = function(column){
	var self = this;

	if(self.groupElement){
		self.columns.push(column);
		self.groupElement.appendChild(column.getElement());
	}else{
		console.warn("Column Warning - Column being attached to another column instead of column group");
	}
};

//vertically align header in column
Column.prototype.verticalAlign = function(alignment, height){

	//calculate height of column header and group holder element
	var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().clientHeight : (height || this.parent.getHeadersElement().clientHeight);
	// var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().clientHeight : this.parent.getHeadersElement().clientHeight;

	this.element.style.height = parentHeight + "px";

	if(this.isGroup){
		this.groupElement.style.minHeight = (parentHeight - this.contentElement.offsetHeight) + "px";
	}

	//vertically align cell contents
	if(!this.isGroup && alignment !== "top"){
		if(alignment === "bottom"){
			this.element.style.paddingTop = (this.element.clientHeight - this.contentElement.offsetHeight) + "px";
		}else{
			this.element.style.paddingTop = ((this.element.clientHeight - this.contentElement.offsetHeight) / 2) + "px";
		}
	}

	this.columns.forEach(function(column){
		column.verticalAlign(alignment);
	});
};

//clear vertical alignmenet
Column.prototype.clearVerticalAlign = function(){
	this.element.style.paddingTop = "";
	this.element.style.height = "";
	this.element.style.minHeight = "";
	this.groupElement.style.minHeight = "";

	this.columns.forEach(function(column){
		column.clearVerticalAlign();
	});
};

Column.prototype.bindModuleColumns = function (){
	//check if rownum formatter is being used on a column
	if(this.definition.formatter == "rownum"){
		this.table.rowManager.rowNumColumn = this;
	}
};


//// Retreive Column Information ////

//return column header element
Column.prototype.getElement = function(){
	return this.element;
};

//return colunm group element
Column.prototype.getGroupElement = function(){
	return this.groupElement;
};

//return field name
Column.prototype.getField = function(){
	return this.field;
};

//return the first column in a group
Column.prototype.getFirstColumn = function(){
	if(!this.isGroup){
		return this;
	}else{
		if(this.columns.length){
			return this.columns[0].getFirstColumn();
		}else{
			return false;
		}
	}
};

//return the last column in a group
Column.prototype.getLastColumn = function(){
	if(!this.isGroup){
		return this;
	}else{
		if(this.columns.length){
			return this.columns[this.columns.length -1].getLastColumn();
		}else{
			return false;
		}
	}
};

//return all columns in a group
Column.prototype.getColumns = function(){
	return this.columns;
};

//return all columns in a group
Column.prototype.getCells = function(){
	return this.cells;
};

//retreive the top column in a group of columns
Column.prototype.getTopColumn = function(){
	if(this.parent.isGroup){
		return this.parent.getTopColumn();
	}else{
		return this;
	}
};

//return column definition object
Column.prototype.getDefinition = function(updateBranches){
	var colDefs = [];

	if(this.isGroup && updateBranches){
		this.columns.forEach(function(column){
			colDefs.push(column.getDefinition(true));
		});

		this.definition.columns = colDefs;
	}

	return this.definition;
};

//////////////////// Actions ////////////////////

Column.prototype.checkColumnVisibility = function(){
	var visible = false;

	this.columns.forEach(function(column){
		if(column.visible){
			visible = true;
		}
	});

	if(visible){
		this.show();
		this.parent.table.options.columnVisibilityChanged.call(this.table, this.getComponent(), false);
	}else{
		this.hide();
	}

};

//show column
Column.prototype.show = function(silent, responsiveToggle){
	if(!this.visible){
		this.visible = true;

		this.element.style.display = "";

		if(this.parent.isGroup){
			this.parent.checkColumnVisibility();
		}

		this.cells.forEach(function(cell){
			cell.show();
		});

		if(!this.isGroup && this.width === null){
			this.reinitializeWidth();
		}

		this.table.columnManager._verticalAlignHeaders();

		if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.columns){
			this.table.modules.persistence.save("columns");
		}

		if(!responsiveToggle && this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
			this.table.modules.responsiveLayout.updateColumnVisibility(this, this.visible);
		}

		if(!silent){
			this.table.options.columnVisibilityChanged.call(this.table, this.getComponent(), true);
		}

		if(this.parent.isGroup){
			this.parent.matchChildWidths();
		}

		if(!this.silent && this.table.options.virtualDomHoz){
			this.table.vdomHoz.reinitialize();
		}
	}
};

//hide column
Column.prototype.hide = function(silent, responsiveToggle){
	if(this.visible){
		this.visible = false;

		this.element.style.display = "none";

		this.table.columnManager._verticalAlignHeaders();

		if(this.parent.isGroup){
			this.parent.checkColumnVisibility();
		}

		this.cells.forEach(function(cell){
			cell.hide();
		});

		if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.columns){
			this.table.modules.persistence.save("columns");
		}

		if(!responsiveToggle && this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
			this.table.modules.responsiveLayout.updateColumnVisibility(this, this.visible);
		}

		if(!silent){
			this.table.options.columnVisibilityChanged.call(this.table, this.getComponent(), false);
		}

		if(this.parent.isGroup){
			this.parent.matchChildWidths();
		}

		if(!this.silent && this.table.options.virtualDomHoz){
			this.table.vdomHoz.reinitialize();
		}
	}
};

Column.prototype.matchChildWidths = function(){
	var childWidth = 0;

	if(this.contentElement && this.columns.length){
		this.columns.forEach(function(column){
			if(column.visible){
				childWidth += column.getWidth();
			}
		});

		this.contentElement.style.maxWidth = (childWidth - 1) + "px";

		if(this.parent.isGroup){
			this.parent.matchChildWidths();
		}
	}
};

Column.prototype.removeChild = function(child){
	var index = this.columns.indexOf(child);

	if(index > -1){
		this.columns.splice(index, 1);
	}

	if(!this.columns.length){
		this.delete();
	}
};


Column.prototype.setWidth = function(width){
	this.widthFixed = true;
	this.setWidthActual(width);
};

Column.prototype.setWidthActual = function(width){
	if(isNaN(width)){
		width = Math.floor((this.table.element.clientWidth/100) * parseInt(width));
	}

	width = Math.max(this.minWidth, width);

	if(this.maxWidth){
		width = Math.min(this.maxWidth, width);
	}

	this.width = width;
	this.widthStyled = width ? width + "px" : "";

	this.element.style.width = this.widthStyled;

	if(!this.isGroup){
		this.cells.forEach(function(cell){
			cell.setWidth();
		});
	}

	if(this.parent.isGroup){
		this.parent.matchChildWidths();
	}

	//set resizable handles
	if(this.table.modExists("frozenColumns")){
		this.table.modules.frozenColumns.layout();
	}
};


Column.prototype.checkCellHeights = function(){
	var rows = [];

	this.cells.forEach(function(cell){
		if(cell.row.heightInitialized){
			if(cell.row.getElement().offsetParent !== null){
				rows.push(cell.row);
				cell.row.clearCellHeight();
			}else{
				cell.row.heightInitialized = false;
			}
		}
	});

	rows.forEach(function(row){
		row.calcHeight();
	});

	rows.forEach(function(row){
		row.setCellHeight();
	});
};

Column.prototype.getWidth = function(){
	var width = 0;

	if(this.isGroup){
		this.columns.forEach(function(column){
			if(column.visible){
				width += column.getWidth();
			}
		});
	}else{
		width = this.width;
	}

	return width;
};

Column.prototype.getHeight = function(){
	return this.element.offsetHeight;
};

Column.prototype.setMinWidth = function(minWidth){
	this.minWidth = minWidth;
	this.minWidthStyled = minWidth ? minWidth + "px" : "";

	this.element.style.minWidth = this.minWidthStyled;

	this.cells.forEach(function(cell){
		cell.setMinWidth();
	});
};

Column.prototype.setMaxWidth = function(maxWidth){
	this.maxWidth = maxWidth;
	this.maxWidthStyled = maxWidth ? maxWidth + "px" : "";

	this.element.style.maxWidth = this.maxWidthStyled;

	this.cells.forEach(function(cell){
		cell.setMaxWidth();
	});
};

Column.prototype.delete = function(){
	return new Promise((resolve, reject) => {
		var index;

		if(this.isGroup){
			this.columns.forEach(function(column){
				column.delete();
			});
		}

		//cancel edit if column is currently being edited
		if(this.table.modExists("edit")){
			if(this.table.modules.edit.currentCell.column === this){
				this.table.modules.edit.cancelEdit();
			}
		}

		var cellCount = this.cells.length;

		for(let i = 0; i < cellCount; i++){
			this.cells[0].delete();
		}

		if(this.element.parentNode){
			this.element.parentNode.removeChild(this.element);
		}

		this.element = false;
		this.contentElement = false;
		this.titleElement = false;
		this.groupElement = false;

		if(this.parent.isGroup){
			this.parent.removeChild(this);
		}

		this.table.columnManager.deregisterColumn(this);

		if(this.table.options.virtualDomHoz){
			this.table.vdomHoz.reinitialize(true);
		}

		resolve();
	});
};

Column.prototype.columnRendered = function(){
	if(this.titleFormatterRendered){
		this.titleFormatterRendered();
	}
};


Column.prototype.validate = function(){
	var invalid = [];

	this.cells.forEach(function(cell){
		if(!cell.validate()){
			invalid.push(cell.getComponent());
		}
	});

	return invalid.length ? invalid : true;
};


//////////////// Cell Management /////////////////

//generate cell for this column
Column.prototype.generateCell = function(row){
	var self = this;

	var cell = new Cell(self, row);

	this.cells.push(cell);

	return cell;
};

Column.prototype.nextColumn = function(){
	var index = this.table.columnManager.findColumnIndex(this);
	return index > -1 ? this._nextVisibleColumn(index + 1) : false;
};

Column.prototype._nextVisibleColumn = function(index){
	var column = this.table.columnManager.getColumnByIndex(index);
	return !column || column.visible ? column : this._nextVisibleColumn(index + 1);
};

Column.prototype.prevColumn = function(){
	var index = this.table.columnManager.findColumnIndex(this);
	return index > -1 ? this._prevVisibleColumn(index - 1) : false;
};

Column.prototype._prevVisibleColumn = function(index){
	var column = this.table.columnManager.getColumnByIndex(index);
	return !column || column.visible ? column : this._prevVisibleColumn(index - 1);
};

Column.prototype.reinitializeWidth = function(force){
	this.widthFixed = false;

	//set width if present
	if(typeof this.definition.width !== "undefined" && !force){
		this.setWidth(this.definition.width);
	}

	//hide header filters to prevent them altering column width
	if(this.table.modExists("filter")){
		this.table.modules.filter.hideHeaderFilterElements();
	}

	this.fitToData();

	//show header filters again after layout is complete
	if(this.table.modExists("filter")){
		this.table.modules.filter.showHeaderFilterElements();
	}
};

//set column width to maximum cell width
Column.prototype.fitToData = function(){
	var self = this;

	if(!this.widthFixed){
		this.element.style.width = "";

		self.cells.forEach(function(cell){
			cell.clearWidth();
		});
	}

	var maxWidth = this.element.offsetWidth;

	if(!self.width || !this.widthFixed){
		self.cells.forEach(function(cell){
			var width = cell.getWidth();

			if(width > maxWidth){
				maxWidth = width;
			}
		});

		if(maxWidth){
			self.setWidthActual(maxWidth + 1);
		}

	}
};

Column.prototype.updateDefinition = function(updates){
	return new Promise((resolve, reject) => {
		var definition;

		if(!this.isGroup){
			if(!this.parent.isGroup){
				definition = Object.assign({}, this.getDefinition());
				definition = Object.assign(definition, updates);

				this.table.columnManager.addColumn(definition, false, this)
				.then((column) => {

					if(definition.field == this.field){
						this.field = false; //cleair field name to prevent deletion of duplicate column from arrays
					}

					this.delete()
					.then(() => {
						resolve(column.getComponent());
					}).catch((err) => {
						reject(err);
					});

				}).catch((err) => {
					reject(err);
				});
			}else{
				console.warn("Column Update Error - The updateDefinition function is only available on ungrouped columns");
				reject("Column Update Error - The updateDefinition function is only available on columns, not column groups");
			}
		}else{
			console.warn("Column Update Error - The updateDefinition function is only available on ungrouped columns");
			reject("Column Update Error - The updateDefinition function is only available on columns, not column groups");
		}
	});
};


Column.prototype.deleteCell = function(cell){
	var index = this.cells.indexOf(cell);

	if(index > -1){
		this.cells.splice(index, 1);
	}
};


Column.prototype.defaultOptionList = [
"title",
"field",
"columns",
"visible",
"align",
"hozAlign",
"vertAlign",
"width",
"minWidth",
"maxWidth",
"widthGrow",
"widthShrink",
"resizable",
"frozen",
"responsive",
"tooltip",
"cssClass",
"rowHandle",
"hideInHtml",
"print",
"htmlOutput",
"sorter",
"sorterParams",
"formatter",
"formatterParams",
"variableHeight",
"editable",
"editor",
"editorParams",
"validator",
"mutator",
"mutatorParams",
"mutatorData",
"mutatorDataParams",
"mutatorEdit",
"mutatorEditParams",
"mutatorClipboard",
"mutatorClipboardParams",
"accessor",
"accessorParams",
"accessorData",
"accessorDataParams",
"accessorDownload",
"accessorDownloadParams",
"accessorClipboard",
"accessorClipboardParams",
"accessorPrint",
"accessorPrintParams",
"accessorHtmlOutput",
"accessorHtmlOutputParams",
"clipboard",
"download",
"downloadTitle",
"topCalc",
"topCalcParams",
"topCalcFormatter",
"topCalcFormatterParams",
"bottomCalc",
"bottomCalcParams",
"bottomCalcFormatter",
"bottomCalcFormatterParams",
"cellClick",
"cellDblClick",
"cellContext",
"cellTap",
"cellDblTap",
"cellTapHold",
"cellMouseEnter",
"cellMouseLeave",
"cellMouseOver",
"cellMouseOut",
"cellMouseMove",
"cellEditing",
"cellEdited",
"cellEditCancelled",
"headerSort",
"headerSortStartingDir",
"headerSortTristate",
"headerClick",
"headerDblClick",
"headerContext",
"headerTap",
"headerDblTap",
"headerTapHold",
"headerTooltip",
"headerVertical",
"headerHozAlign",
"editableTitle",
"titleFormatter",
"titleFormatterParams",
"headerFilter",
"headerFilterPlaceholder",
"headerFilterParams",
"headerFilterEmptyCheck",
"headerFilterFunc",
"headerFilterFuncParams",
"headerFilterLiveFilter",
"print",
"headerContextMenu",
"headerMenu",
"contextMenu",
// "headerClickMenu",
"clickMenu",
"formatterPrint",
"formatterPrintParams",
"formatterClipboard",
"formatterClipboardParams",
"formatterHtmlOutput",
"formatterHtmlOutputParams",
"titlePrint",
"titleClipboard",
"titleHtmlOutput",
"titleDownload",
];

//////////////// Event Bindings /////////////////

//////////////// Object Generation /////////////////
Column.prototype.getComponent = function(){
	if(!this.component){
		this.component = new ColumnComponent(this);
	}

	return this.component;
};
