/**
 * @mergeTarget
 * @module Src/Components/CustomInput/CustomRange
 */

import { LisioNumericParameterNames } from "@lisio/lisio-profils";
// import { TextsContainer } from "../../containers/texts-container/texts-container";
// import { CustomButton } from "../../custom-button/custom-button";
// import { CustomIcon } from "../../custom-icon/custom-icon";
// import { CustomText } from "../../custom-text/custom-text";
import { ElementsContainer } from "../../containers/elements-container/elements-container";
// import { InputManager } from "../../../../managers/input-manager";
import { RangeInput } from "./input/range-input";
import { customRangeStyles } from "./custom-range.css";
import { LisioComponent, LisioComponentStyles } from "@lisio/lisio-engine";
import { MergedCategoriesNames } from "../../../screens/screen-types";
import { TextsContainer } from "../../containers/texts-container/texts-container";
import { CustomText } from "../../custom-text/custom-text";
import { CustomButton } from "../../custom-button/custom-button";
import { CustomIcon } from "../../custom-icon/custom-icon";
import { InputManager } from "../../../../managers/input-manager";

/**
 * Class representing a slider component extending LisioComponent.\
 * It aims to represent a slider component.\
 * A slider component is basically a component to display a number input as a slider.\
 */
class CustomRange extends LisioComponent {
  /**
   * Private attribute to store parameter name
   * @source
   */
  private _parameterName: LisioNumericParameterNames;
  /**
   * Private attribute to store default value
   * @source
   */
  private _defaultValue: number;
  /**
   * Private attribute to store current value
   * @source
   */
  private _currentValue: number;
  /**
   * Private attribute to store min value
   * @source
   */
  private _minValue: number;
  /**
   * Private attribute to store max value
   * @source
   */
  private _maxValue: number;
  /**
   * Private attribute to store step
   * @source
   */
  private _step: number;
  /**
   * Private attribute to store state of input
   * @source
   */
  private _isEnabled: boolean;
  /**
   * Private attribute to store input
   * @source
   */
  private _input: RangeInput;

  /**
   * Private method to increment range value
   * @returns Returns nothing
   * @source
   */
  private incrementRange(): void {
    const newValue: number = (this._maxValue-this._minValue) > (this._step * 20) ? this._input.value + (this._maxValue/20) : this._input.value + this._step;
    if (newValue <= this._maxValue) {
      (this._input.htmlElement?.querySelector("input") as HTMLInputElement).value = newValue.toFixed(2);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("input"),
      );
    } else {
      (this._input.htmlElement?.querySelector("input") as HTMLInputElement).value = this._maxValue.toFixed(0);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("input"),
      );
    }
  }

  /**
   * Public method to be called after render
   * @returns Returns nothing
   * @source
   */
  public postRender(): void {
    if (this._currentValue !== this._defaultValue) {
      this.addClasses(["active"]);
    }else{
      this.removeClasses(["active"]);
    }
  }

  /**
   * Private method to decrement range value
   * @returns Returns nothing
   * @source
   */
  private decrementRange(): void {
    const newValue: number = (this._maxValue-this._minValue) > (this._step * 20) ? this._input.value - (this._maxValue/20) : this._input.value - this._step;
    if (newValue >= this._minValue) {
      (this._input.htmlElement?.querySelector("input") as HTMLInputElement).value = newValue.toFixed(2);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("input"),
      );
    } else {
      (this._input.htmlElement?.querySelector("input") as HTMLInputElement).value = this._minValue.toFixed(0);
      (this._input.htmlElement as HTMLInputElement).dispatchEvent(
        new Event("input"),
      );
    }
  }

  /**
   * Private method to reset range value
   * @returns Returns nothing
   * @source
   */
  public resetRange(): void {
    if(this._currentValue != this._defaultValue){
      const newValue: number = this._defaultValue;
      this._currentValue = newValue;
      if(this._input?.htmlElement != undefined){
        (this._input.htmlElement?.querySelector("input") as HTMLInputElement).value = newValue.toFixed(2);
        (this._input.htmlElement as HTMLInputElement).dispatchEvent(
          new Event("input"),
        );
      }
      this.removeClasses(["active"]);
    }
  }

  /**
   * @async
   * Public method implementing abstract method render of LisioComponent
   * @returns Returns a promise containing the representation of a slider in HTML string
   * @source
   */
  public async render(): Promise<string> {
    return `
      <div id="${this._id}" class="lisio-custom-range${
        this._isEnabled ? " active" : ""
      } ${this.renderClasses()}">
        <children></children>
      </div>
    `;
  }

  /**
   * Constructor of class {@link CustomRange | CustomRange}
   * @param {string} id - Id of component
   * @param {string} titleId - Title id of text
   * @param {LisioStringParameterNames | "users-choice"} parameterName - Name of functionality
   * @param {number} defaultValue - Default value of input
   * @param {number} currentValue - Value of input
   * @param {number} minValue - Min value of input
   * @param {number} maxValue - Max value of input
   * @param {number} step - Step of input
   * @param {string} screenName - Screen name including this input
   * @param {MergedCategoriesNames | undefined} categoryName - Category name containing parameter or profile related to this input
   * @param {string | undefined} parentScreenName - Parent screen name in screen tree
   * @param {ElementsContainer | undefined} infobox - Infobox linked to this component
   * @param {string[] | undefined} cssClasses - CSS classes of component
   * @param {object | undefined} icon - Icon object
   * @property {string} icon.fileName - Icon file name
   * @property {string} icon.iconName - Icon name
   * @param {string[] | undefined} iconClasses - CSS classes of icon
   * @source
   */
  constructor(
    id: string,
    titleId: string,
    parameterName: LisioNumericParameterNames,
    defaultValue: number,
    currentValue: number,
    minValue: number,
    maxValue: number,
    step: number,
    screenName: string,
    categoryName?: MergedCategoriesNames,
    parentScreenName?: string,
    infobox?: ElementsContainer,
    cssClasses?: string[],
    icon?: {
      fileName: string;
      iconName: string;
    },
    iconClasses?: string[],
  ) {
    super(CustomRange.name, id, cssClasses);
    this._parameterName = parameterName;
    this._defaultValue = defaultValue;
    this._currentValue = currentValue;
    this._isEnabled = defaultValue != currentValue;
    this._minValue = minValue;
    this._maxValue = maxValue;
    this._step = step;
    infobox?.showHideElement(currentValue != defaultValue);

    const handler = async (event: any) => {
      await InputManager.current.disableProfileLinkToParameter(parameterName);
      this._currentValue = event.target.value;
      if (Number.parseFloat(event.target.value) !== defaultValue) {
        this.addClasses(["active"]);
      } else {
        this.removeClasses(["active"]);
      }
    };
    
    this._input = new RangeInput(
      this._currentValue,
      this._defaultValue,
      this._minValue,
      this._maxValue,
      this._step,
      this._parameterName,
      handler,
      `${parameterName}-input`,
      screenName,
      this,
      categoryName,
      parentScreenName,
      infobox,
    );

    const titleSection = new TextsContainer([]);

    if (icon) {
      titleSection.children.push(
        new CustomIcon(icon.fileName, icon.iconName, iconClasses),
      );
    }

    titleSection.children.push(new CustomText("label", `${titleId}-label`, undefined, ["medium"], `${parameterName}-input`.replace(/_/g, "-")));
    
    this._children.push(
      titleSection,
      new ElementsContainer([
        new ElementsContainer(
          [
            new CustomButton(`${id}-decrease-button`,
              [new CustomIcon("misc-icons", "Minus", [])],
              this.decrementRange.bind(this),
              [],
              {
                label: {
                  id: "decrease",
                },
                controls: `${parameterName.replace("_", "-")}-input`,
              }
            ),
            this._input,
            new CustomButton(`${id}-increase-button`,
              [new CustomIcon("misc-icons", "Plus", [])],
              this.incrementRange.bind(this),
              [],
              {
                label: {
                  id: "increase",
                },
                controls: `${parameterName.replace("_", "-")}-input`,
              }
            ),
          ],
          undefined,
          ["input-range-test"]
        ),
        new CustomButton(`${id}-reset-button`,
          [new CustomIcon("misc-icons", "Reset", [])], 
          this.resetRange.bind(this),
          ["btn-outline-grey", "btn-pill", "range-input-reset-button"],
          {
            label: {
              id: "reset",
            },
            controls: `${parameterName.replace("_", "-")}-input`,
          }
        ),
      ],
      undefined,
      ["custom-range-inputs-container"]
    )
    );
    if (currentValue != defaultValue) {
      InputManager.current.addOrRemoveEnabledInput(parameterName, true);
    }
  }
}

new LisioComponentStyles(CustomRange.name, customRangeStyles);

export { CustomRange };
