/**
 * Copyright schukai GmbH and contributors 2023. All Rights Reserved.
 * Node module: @schukai/monster
 * This file is licensed under the AGPLv3 License.
 * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
 */
import { instanceSymbol } from "../../constants.mjs";
import { registerCustomElement } from "../../dom/customelement.mjs";
import { isInteger } from "../../types/is.mjs";
import { validateInstance, validateString } from "../../types/validate.mjs";
import { Button } from "./button.mjs";
import { StateButtonStyleSheet } from "./stylesheet/state-button.mjs";
import { getStateInstanceFor, State } from "./types/state.mjs";

export { StateButton };

/**
 * This CustomControl creates a button element with a variety of options.
 *
 * <img src="./images/state-button.png">
 *
 * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library
 *
 * You can create this control either by specifying the HTML tag <monster-state-button />` directly in the HTML or using
 * Javascript via the `document.createElement('monster-state-button');` method.
 *
 * ```html
 * <monster-state-button></monster-state-button>
 * ```
 *
 * Or you can create this CustomControl directly in Javascript:
 *
 * ```js
 * import {StateButton} from '@schukai/component-form/source/state-button.js';
 * document.createElement('monster-state-button');
 * ```
 *
 * The `data-monster-button-class` attribute can be used to change the CSS class of the button.
 *
 * @startuml state-button.png
 * skinparam monochrome true
 * skinparam shadowing false
 * HTMLElement <|-- CustomElement
 * CustomElement <|-- CustomControl
 * CustomControl <|-- Button
 * Button <|-- StateButton
 * @enduml
 *
 * @since 1.5.0
 * @copyright schukai GmbH
 * @memberOf Monster.Components.Form
 * @summary A state button with icons
 */
class StateButton extends Button {
	/**
	 * This method is called by the `instanceof` operator.
	 * @returns {symbol}
	 * @since 2.1.0
	 */
	static get [instanceSymbol]() {
		return Symbol.for(
			"@schukai/monster/components/form/state-button@@instance",
		);
	}

	/**
	 * To set the options via the html tag the attribute `data-monster-options` must be used.
	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
	 *
	 * The individual configuration values can be found in the table.
	 *
	 * @property {Object} templates Template definitions
	 * @property {string} templates.main Main template
	 * @property {Object} states Available status
	 * @property {Monster.Components.Form.Types.State} states.successful= successful
	 * @property {Monster.Components.Form.Types.State} states.activity= activity
	 * @property {Monster.Components.Form.Types.State} states.failed= failed
	 * @property {Monster.Components.Form.Types.State} current current status
	 * @property {Monster.Components.Form~exampleActionCallback} actions.click
	 * @extends {Button}
	 * @see {@link https://github.com/twbs/icons/blob/main/LICENSE.md|Bootstrap icons license}
	 */
	get defaults() {
		return Object.assign({}, super.defaults, {
			templates: {
				main: getTemplate(),
			},
			states: {
				successful: getStateInstanceFor("successful"),
				activity: getStateInstanceFor("activity"),
				failed: getStateInstanceFor("failed"),
			},
			current: getStateInstanceFor("stateless"),
		});
	}

	/**
	 * This method sets the current state of the button.
	 * If a timeout is set, the state is automatically removed after the
	 * specified time.
	 *
	 * @since 3.18.0 a previously set timeout is cleared
	 *
	 * @param {state} state
	 * @param {number} timeout
	 * @return {Monster.Components.Form.StateButton}
	 * @throws {TypeError} value is not a string
	 * @throws {TypeError} value is not an instance
	 */
	setState(state, timeout) {
		const timeoutSymbol = Symbol.for("timeout");
		if (this[timeoutSymbol] !== undefined) {
			clearTimeout(this[timeoutSymbol]);
			delete this[timeoutSymbol];
		}

		const obj = this.getOption(`states.${validateString(state)}`);
		if (obj === undefined) {
			throw new Error("not found");
		}

		this.setOption("current", validateInstance(obj, State));

		if (isInteger(timeout) && timeout > 0) {
			this[timeoutSymbol] = setTimeout(() => {
				this.removeState();
				delete this[timeoutSymbol];
			}, timeout);
		}

		return this;
	}

	/**
	 *
	 * @return {Monster.Components.Form.StateButton}
	 */
	removeState() {
		this.setOption("current", getStateInstanceFor("stateless"));
		return this;
	}

	/**
	 * @return {Monster.Components.Form.Types.State|undefined}
	 */
	getState() {
		return this.getOption("current");
	}

	/**
	 *
	 * @return {string}
	 */
	static getTag() {
		return "monster-state-button";
	}

	/**
	 *
	 * @return {Array<CSSStyleSheet>}
	 */
	static getCSSStyleSheet() {
		const styles = Button.getCSSStyleSheet();
		styles.push(StateButtonStyleSheet);
		return styles;
	}
}

/**
 * @private
 * @return {string}
 */
function getTemplate() {
	// language=HTML
	return `<div data-monster-role="control" part="control">
    <button data-monster-attributes="disabled path:disabled | if:true, class path:classes.button"
            data-monster-role="button"
            part="button">
        <div data-monster-role="label" data-monster-replace="path:labels.button"></div>
        <div data-monster-role="state"
			 data-monster-attributes="class path:current.state"
			 data-monster-replace="path:current.presentation"></div>
    </button>
</div>`;
}

registerCustomElement(StateButton);