/**
 * @mergeTarget
 * @module Src/Screens/Settings
 */
import {
  CursorSizeOptions,
  FontFamilyOptions,
  LisioBooleanParameterNames,
  LisioCategoryNames,
  LisioNumericParameterNames,
  LisioParameterNames,
  LisioProfileNames,
  LisioStringParameterNames,
  LisioUser,
  TextAlignOptions,
  ThemeOptions,
  defautValueOfNumericParameters,
} from "@lisio/lisio-profils";
import { LisioScreen } from "../lisio-screen";
import {
  BooleanParameterRenderObject,
  StringParameterRenderObject,
  NumberParameterRenderObject,
  ComponentAndPosition,
  CategoryRenderObject,
  ProfileRenderObject,
  VisualCategoryNames,
  ScreenNames,
} from "../screen-types";
import { ListContainer } from "../../components/containers/list-container/list-container";
import { HomeScreen } from "../home/home-screen";
import { LisioComponent } from "@lisio/lisio-engine";
import { MoreScreen } from "../more/more-screen";

/**
 * Class representing a settings screen component extending LisioScreen.\
 * It aims to represent a settings screen component.\
 * A settings screen component is basically a component to render all settings categories, profiles and parameters.\
 */
class SettingsScreen extends LisioScreen {
  /**
   * Protected attribute to store category render objects
   * @source
   */
  protected _categoryRenderObjects: Map<
    VisualCategoryNames,
    CategoryRenderObject
  > = new Map<VisualCategoryNames, CategoryRenderObject>([
    [
      VisualCategoryNames.MORE,
      {
        buttonId: `${this.id}-more`,
        icon: {
          fileName: "footer-icons",
          iconName: "More",
        },
        titleId: "more-button",
        nextScreenName: MoreScreen.name,
        position: 10,
        isActive: false,
        activeSubObjects: new Set(),
        firstIconClasses: ["grey2"],
        iconsClasses: ["grey"],
        cssClasses: ["secondary", "footer-button"],
      },
    ],
  ]);

  /**
   * Protected attribute to store profile render objects
   * @source
   */
  protected _profileRenderObjects: Map<LisioProfileNames, ProfileRenderObject> =
    new Map<LisioProfileNames, ProfileRenderObject>();

  /**
   * Protected attribute to store parameter render objects
   * @source
   */
  protected _parameterRenderObjects: Map<
    LisioParameterNames,
    | BooleanParameterRenderObject
    | StringParameterRenderObject
    | NumberParameterRenderObject
  > = new Map<
    LisioParameterNames,
    | BooleanParameterRenderObject
    | StringParameterRenderObject
    | NumberParameterRenderObject
  >([
    [
      LisioNumericParameterNames.FONT_SIZE,
      {
        type: "number",
        name: LisioNumericParameterNames.FONT_SIZE,
        buttonId: "font-size-button",
        titleId: "font-size-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.FONT_SIZE,
        ),
        currentValue: 0,
        minValue: 0,
        maxValue: 10,
        step: 1,
        position: 0,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "FontSize",
        },
        iconClasses: ["primary"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioNumericParameterNames.LINE_HEIGHT,
      {
        type: "number",
        name: LisioNumericParameterNames.LINE_HEIGHT,
        buttonId: "line-height-button",
        titleId: "line-height-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.LINE_HEIGHT,
        ),
        currentValue: 1,
        minValue: 1,
        maxValue: 5,
        step: 1,
        position: 2,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "LineHeight",
        },
        iconClasses: ["primary"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioStringParameterNames.FONT_FAMILY,
      {
        type: "string",
        name: LisioStringParameterNames.FONT_FAMILY,
        items: Object.values(FontFamilyOptions).map((fontFamilyOption) => {
          return {
            textId: fontFamilyOption.replace(/_/g, "-"),
            value: fontFamilyOption,
            isChecked: false,
            noTranslate: true,
            icon: {
              fileName: "setting-icons",
              iconName: fontFamilyOption
                .split("_")
                .map((elt) => elt.charAt(0).toUpperCase() + elt.slice(1))
                .join(""),
            },
            noText: true,
            iconClasses: ["no-ratio", "font-family-icon"],
          };
        }),
        position: 2,
        cssClasses: ["secondary", "regular"],
        cssClassesItems: ["row"],
        cssClassesLabelItems: ["row", "align-items-center"],
        defaultText: "default-radio-font-family-text",
      } as StringParameterRenderObject,
    ],
    [
      LisioNumericParameterNames.CONTRAST,
      {
        type: "number",
        name: LisioNumericParameterNames.CONTRAST,
        buttonId: "contrast-button",
        titleId: "contrast-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.CONTRAST,
        ),
        currentValue: 10,
        minValue: 1,
        maxValue: 20,
        step: 1,
        position: 3,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "Contrast",
        },
        iconClasses: ["primary"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioNumericParameterNames.BIGGER_CLICK,
      {
        type: "number",
        name: LisioNumericParameterNames.BIGGER_CLICK,
        buttonId: "bigger-click-button",
        titleId: "bigger-click-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.BIGGER_CLICK,
        ),
        currentValue: 0,
        minValue: 0,
        maxValue: 10,
        step: 1,
        position: 0,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "ButtonSize",
        },
        iconClasses: ["primary"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioStringParameterNames.THEME,
      {
        type: "string",
        name: LisioStringParameterNames.THEME,
        items: Object.values(ThemeOptions).map((themeOption) => {
          const optionFormated: string = themeOption.replace(/_/g, "-");

          return {
            textId: optionFormated,
            value: themeOption,
            isChecked: false,
            icon: {
              fileName: "setting-icons",
              iconName: "Theme",
            },
            iconClasses: [optionFormated, "theme-icon"],
          };
        }),
        position: 0,
        cssClasses: ["secondary", "regular"],
        cssClassesItems: ["row", "content-column"],
        cssClassesLabelItems: ["row", "align-items-center"],
        defaultText: "default-radio-theme-text",
      } as StringParameterRenderObject,
    ],
    [
      LisioBooleanParameterNames.LIST,
      {
        type: "boolean",
        name: LisioBooleanParameterNames.LIST,
        buttonId: "list-button",
        titleId: "list-title",
        value: false,
        position: 6,
        cssClasses: [
          "width-100",
          "btn-pill",
          "secondary",
          "regular",
          "space-between",
        ],
        icon: {
          fileName: "setting-icons",
          iconName: "List",
        },
        iconClasses: ["primary"],
        infoBoxes: [
          {
            textId: "infobox-list",
            cssClassesText: ["info-text"],
          },
        ],
        aria: {
          describedby: "infobox-list",
        },
        additionalCss: ["switch-with-icon"],
      } as BooleanParameterRenderObject,
    ],
    [
      LisioBooleanParameterNames.SHOW_ALT,
      {
        type: "boolean",
        name: LisioBooleanParameterNames.SHOW_ALT,
        buttonId: "show-alt-button",
        titleId: "show-alt-title",
        value: false,
        position: 4,
        cssClasses: [
          "width-100",
          "btn-pill",
          "secondary",
          "regular",
          "space-between",
        ],
        icon: {
          fileName: "setting-icons",
          iconName: "Alt",
        },
        iconClasses: ["primary"],
        additionalCss: ["switch-with-icon"],
        infoBoxes: [
          {
            textId: "infobox-show-alt",
            cssClassesText: ["info-text"],
          },
        ],
        aria: {
          describedby: "infobox-show-alt",
        },
      } as BooleanParameterRenderObject,
    ],
    [
      LisioNumericParameterNames.WORD_SPACING,
      {
        type: "number",
        name: LisioNumericParameterNames.WORD_SPACING,
        buttonId: "word-spacing-button",
        titleId: "word-spacing-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.WORD_SPACING,
        ),
        currentValue: 0,
        minValue: 0,
        maxValue: 20,
        step: 1,
        position: 1,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "WordSpacing",
        },
        iconClasses: ["primary", "no-ratio", "word-spacing"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioNumericParameterNames.LETTER_SPACING,
      {
        type: "number",
        name: LisioNumericParameterNames.LETTER_SPACING,
        buttonId: "letter-spacing-button",
        titleId: "letter-spacing-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.LETTER_SPACING,
        ),
        currentValue: 0,
        minValue: 0,
        maxValue: 20,
        step: 1,
        position: 1,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "LetterSpacing",
        },
        iconClasses: ["primary"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioNumericParameterNames.ZOOM,
      {
        type: "number",
        name: LisioNumericParameterNames.ZOOM,
        buttonId: "zoom-button",
        titleId: "zoom-title",
        defaultValue: defautValueOfNumericParameters.get(
          LisioNumericParameterNames.ZOOM,
        ),
        currentValue: 1,
        minValue: 1,
        maxValue: 4,
        step: 0.25,
        position: 2,
        cssClasses: ["secondary", "regular"],
        icon: {
          fileName: "setting-icons",
          iconName: "Zoom",
        },
        iconClasses: ["primary"],
      } as NumberParameterRenderObject,
    ],
    [
      LisioStringParameterNames.TEXT_ALIGN,
      {
        type: "string",
        name: LisioStringParameterNames.TEXT_ALIGN,
        items: Object.values(TextAlignOptions).map((textAlignOption) => {
          return {
            textId: `text-align-${textAlignOption}`,
            value: textAlignOption,
            isChecked: false,
            icon: {
              fileName: "setting-icons",
              iconName: `Align${textAlignOption
                .split("_")
                .map((elt) => elt.charAt(0).toUpperCase() + elt.slice(1))
                .join("")}`,
            },
            iconClasses: ["primary"],
          };
        }),
        position: 1,
        cssClasses: ["secondary", "regular"],
        cssClassesItems: ["row", "content-column"],
        cssClassesLabelItems: ["align-items-center"],
        defaultText: "default-radio-text-align-text",
      } as StringParameterRenderObject,
    ],
    [
      LisioStringParameterNames.CURSOR_SIZE,
      {
        type: "string",
        name: LisioStringParameterNames.CURSOR_SIZE,
        items: Object.values(CursorSizeOptions).map((cursorSizeOption) => {
          return {
            textId: `cursor-size-${cursorSizeOption.replace(/_/g, "-")}`,
            value: cursorSizeOption,
            isChecked: false,
            icon: {
              fileName: "setting-icons",
              iconName: `Cursor${cursorSizeOption
                .split("_")
                .map((elt) => elt.charAt(0).toUpperCase() + elt.slice(1))
                .join("")}`,
            },
            iconClasses: ["primary"],
          };
        }),
        position: 3,
        cssClasses: ["secondary", "regular"],
        cssClassesItems: ["row", "content-column"],
        cssClassesLabelItems: ["row", "align-items-center"],
        defaultText: "default-radio-cursor-size-text",
      } as StringParameterRenderObject,
    ],
    [
      LisioBooleanParameterNames.KEYBOARD_NAVIGATION,
      {
        type: "boolean",
        name: LisioBooleanParameterNames.KEYBOARD_NAVIGATION,
        buttonId: "keyboard-nav-button",
        titleId: "keyboard-nav-title",
        value: false,
        position: 5,
        cssClasses: [
          "width-100",
          "btn-pill",
          "secondary",
          "regular",
          "space-between",
        ],
        icon: {
          fileName: "setting-icons",
          iconName: "KeyboardNav",
        },
        iconClasses: ["primary"],
        additionalCss: ["switch-with-icon"],
        infoBoxes: [
          {
            textId: "infobox-keyboard-nav",
            cssClassesText: ["info-text"],
          },
        ],
        aria: {
          describedby: "infobox-keyboard-nav",
        },
      } as BooleanParameterRenderObject,
    ],
    [
      LisioBooleanParameterNames.DISABLE_ANIMATION,
      {
        type: "boolean",
        name: LisioBooleanParameterNames.DISABLE_ANIMATION,
        buttonId: "toggle-anim-button",
        titleId: "toggle-anim-title",
        value: false,
        position: 4,
        cssClasses: [
          "width-100",
          "btn-pill",
          "secondary",
          "regular",
          "space-between",
        ],
        icon: {
          fileName: "setting-icons",
          iconName: "StopAnimations",
        },
        iconClasses: ["primary"],
        additionalCss: ["switch-with-icon"],
        infoBoxes: [
          {
            textId: "infobox-toggle-anim",
            cssClassesText: ["info-text"],
          },
        ],
        aria: {
          describedby: "infobox-toggle-anim",
        },
      } as BooleanParameterRenderObject,
    ],
    [
      LisioBooleanParameterNames.HIGHLIGHT,
      {
        type: "boolean",
        name: LisioBooleanParameterNames.HIGHLIGHT,
        buttonId: "highlight-button",
        titleId: "highlight-title",
        value: false,
        position: 5,
        cssClasses: [
          "width-100",
          "btn-pill",
          "secondary",
          "regular",
          "space-between",
        ],
        icon: {
          fileName: "setting-icons",
          iconName: "Highlight",
        },
        iconClasses: ["primary"],
        additionalCss: ["switch-with-icon"],
        infoBoxes: [
          {
            textId: "infobox-highlight",
            cssClassesText: ["info-text"],
          },
        ],
        aria: {
          describedby: "infobox-highlight",
        },
      } as BooleanParameterRenderObject,
    ],
    [
      LisioBooleanParameterNames.READING_MASK,
      {
        type: "boolean",
        name: LisioBooleanParameterNames.READING_MASK,
        buttonId: "reading-mask-button",
        titleId: "reading-mask-title",
        value: false,
        position: 3,
        cssClasses: [
          "width-100",
          "btn-pill",
          "secondary",
          "regular",
          "space-between",
        ],
        icon: {
          fileName: "setting-icons",
          iconName: "ReadingMask",
        },
        iconClasses: ["primary"],
        additionalCss: ["switch-with-icon"],
        infoBoxes: [
          {
            textId: "infobox-reading-mask",
            cssClassesText: ["info-text"],
          },
        ],
        aria: {
          describedby: "infobox-reading-mask",
        },
      } as BooleanParameterRenderObject,
    ],
  ]);

  /**
   * @async
   * Public method implementing abstract method render of LisioComponent
   * @returns Returns a promise containing the representation of a settings screen in HTML string
   * @source
   */
  public async render(): Promise<string> {
    return `
    <section id="${this._id}" role="tabpanel">
      <children></children>
    </section>
    `;
  }

  /**
   * Constructor of class {@link SettingsScreen | SettingsScreen}
   * @param {object} contentAndDisplay - Groups of section
   * @property {LisioParameterNames[]} contentAndDisplay.slidersSection - Number parameters
   * @property {LisioParameterNames[]} contentAndDisplay.switchsSection - Boolean parameters
   * @property {LisioParameterNames[]} contentAndDisplay.radiosSection - String parameters
   * @property {"content-settings-screen-title" | "display-settings-screen-title"} contentAndDisplay.title - Title of section
   * @param {LisioParameterNames[]} hiddenParameters - Names of hidden parameters
   * @param {LisioUser | undefined} user - Current user
   * @source
   */
  constructor(
    contentAndDisplay: {
      slidersSection: LisioParameterNames[];
      switchsSection: LisioParameterNames[];
      radiosSection: LisioParameterNames[];
      title: "content-settings-screen-title" | "display-settings-screen-title";
    },
    hiddenParameters: LisioParameterNames[],
    isCompensation?: boolean,
    user?: LisioUser,
  ) {
    ScreenNames[SettingsScreen.name] = "settings-screen";
    super(
      SettingsScreen.name,
      SettingsScreen.name,
      LisioCategoryNames.SETTINGS,
      HomeScreen.name,
    );

    const elementsTitleContainer: LisioComponent =
      this.createScreenTitle("settings-title");

    const container1Children = [];

    const container1Elements: ComponentAndPosition[] = this.initParameters(
      contentAndDisplay.slidersSection,
      [],
      user?.customParameters,
    );

    container1Elements.sort((a, b) => a.position - b.position);

    for (let element of container1Elements) {
      container1Children.push(element.component);
    }

    const container1: ListContainer = new ListContainer(
      container1Children,
      undefined,
      [
        "row",
        "wrap",
        "settings-section"
      ],
      [
        "full"
      ]
    );

    const container2Children = [];

    const container2Elements: ComponentAndPosition[] = this.initParameters(
      contentAndDisplay.switchsSection,
      [],
      user?.customParameters,
    );

    container2Elements.sort((a, b) => a.position - b.position);

    for (let element of container2Elements) {
      container2Children.push(element.component);
    }

    const container2: ListContainer = new ListContainer(
      container2Children,
      undefined,
      ["row wrap", "settings-section"],
      ["full"],
    );

    const container3Elements: ComponentAndPosition[] = this.initParameters(
      contentAndDisplay.radiosSection,
      hiddenParameters,
      user?.customParameters,
    );

    const container3Children = [];

    container3Elements.sort((a, b) => a.position - b.position);

    for (let element of container3Elements) {
      container3Children.push(element.component);
    }

    const container3: ListContainer = new ListContainer(
      container3Children,
      undefined,
      ["row wrap", "settings-section", "settings-section-radio"],
    );

    this._children.push(
      this.createScreenSubtitle("accessibility-title"),
      elementsTitleContainer,
      new ListContainer(
        [
          container1,
          container2,
          container3,
          ...(isCompensation ? this.createFooter(isCompensation, true) : []),
        ],
        undefined,
        ["settings-parameters-container"],
      ),
    );
  }
}

export { SettingsScreen };
