import VueDatatable from '../vue-datatable.vue';
import VueDatatablePager from '../vue-datatable-pager.vue';
import Handler from './handler.js';
import Settings from './settings.js';

/**
 * Defines a type of Datatable, with its {@link Settings} object.
 */
class TableType {
	/**
	 * Creates a new datatable type, instanciating a new {@link Settings} object.
	 * 
	 * @param {string} id - The identifier of this datatable type
	 */
	constructor( id ){
		/**
		 * @private
		 * @member {string} - Identifier of the table type
		 */
		this._id = id;

		/** @member {Handler} - Handler associated with the table type */
		this.handler = new Handler();
		
		/** @member {Settings} - Settings object used to get various values for the datatable & other components */
		this.settings = new Settings();
	}

	/**
	 * Retrieves the Id of the table type
	 * 
	 * @returns {string} The ID if the table type
	 */
	get id(){
		return this._id;
	}

	/**
	 * Defines the function used to filter data
	 * 
	 * @see Handler#filterHandler
	 * @tutorial ajax-handler
	 * @param {Function} closure - The function to use for sorting.
	 * @returns {this} For chaining.
	 */
	setFilterHandler( closure ){
		this.handler.filterHandler = closure;

		return this;
	}

	/**
	 * Defines the function used to sort data
	 * 
	 * @see Handler#sortHandler
	 * @tutorial ajax-handler
	 * @param {Function} closure - The function to use for sorting.
	 * @returns {this} For chaining.
	 */
	setSortHandler( closure ){
		this.handler.sortHandler = closure;

		return this;
	}

	/**
	 * Defines the function used to paginate data
	 * 
	 * @see Handler#paginateHandler
	 * @tutorial ajax-handler
	 * @param {Function} closure - The function to use for pagination.
	 * @returns {this} For chaining.
	 */
	setPaginateHandler( closure ){
		this.handler.paginateHandler = closure;

		return this;
	}

	/**
	 * Defines the function used to paginate data
	 * 
	 * @see Handler#displayHandler
	 * @tutorial ajax-handler
	 * @param {Function} closure - The function to use to post-process processed steps & extract rows & total count.
	 * @returns {this} For chaining.
	 */
	setDisplayHandler( closure ){
		this.handler.displayHandler = closure;

		return this;
	}

	/**
	 * Get or set a {@link Settings} value at a specific path
	 * 
	 * @param {(string | number | Array.<string | number>)} path - Path to the setting value to get/set
	 * @param {*} [value] - If omitted, this method will *get* the value at the specified `path`. Otherwise, it will *set* the value.
	 * @returns {this | *} In *get* mode, the value. In *set* mode, `this`, for chaining.
	 */
	setting( path, value ){
		if ( value === undefined ){
			return this.settings.get( path );
		}

		this.settings.set( path, value );

		return this;
	}

	/**
	 * Merge a settings object with the {@link TableType#settings} object of the instance.
	 * 
	 * @param {SettingsProps} settings - Values to merge
	 * @returns {this} For chaining.
	 */
	mergeSettings( settings ){
		this.settings.merge( settings );

		return this;
	}

	/**
	 * Factory function that copy the {@link VueDatatable} prototype, and configure as this type.
	 * 
	 * @returns {VueDatatable} A new factored {@link VueDatatable} constructor.
	 */
	getTableDefinition(){
		const definition = this.clone( VueDatatable );
		definition.handler = this.handler;
		definition.settings = this.settings;
		definition.name = this.id;

		return definition;
	}

	/**
	 * Factory function that copy the {@link VueDatatablePager} prototype, and configure as this type.
	 * 
	 * @returns {VueDatatablePager} A new factored {@link VueDatatablePager} constructor.
	 */
	getPagerDefinition(){
		const definition = this.clone( VueDatatablePager );
		definition.settings = this.settings;
		definition.name = `${ this.id }-pager`;

		return definition;
	}

	/**
	 * Deep clone a value
	 * 
	 * @param {*} obj - The value to clone
	 * @returns {*} The clone of the original parameter.
	 */
	clone( obj ) {
		let copy;

		// Handle Array
		if ( obj instanceof Array ) {
			return obj.map( v => this.clone( v ) );
		}
		if ( obj instanceof Function ) {
			return obj;
		}

		// Handle Object
		if ( obj instanceof Object ) {
			copy = {};

			for ( const attr in obj ) {
				if ( obj.hasOwnProperty( attr ) ) {
					copy[attr] = this.clone( obj[attr] );
				}
			}

			return copy;
		}
		
		return obj;
	}
}

export default TableType;