import { LisioNumericParameterNames, LisioParameterNames, LisioStringParameterNames } from "@lisio/lisio-profils";
import LisioStyleSheetController from "../controllers/lisio-stylesheet-controller";
import LisioCSSProperties from "../enums/lisio-css-properties";
import LisioTextTreeWalker from "../walkers/lisio-text-tree-walker";
import LisioAdapter from "./lisio-adapter";

/**
 * Class representing a daltonism adapter extending an adapter.\
 * It aims to represents the font style functionality of Lisio.\
 * A daltonism adapter is basically a functionality of Lisio which will change the daltonism of texts in the main page.\
 */
class LisioDaltonismAdapter extends LisioAdapter<string> {

  private areInDefaultState: Partial<Record<LisioParameterNames, boolean>> = {
    [LisioStringParameterNames.DALTONISM]: true,
    [LisioNumericParameterNames.DALTONISM_R]: true,
    [LisioNumericParameterNames.DALTONISM_G]: true,
    [LisioNumericParameterNames.DALTONISM_B]: true,
    [LisioNumericParameterNames.DALTONISM_HUE]: true,
    [LisioNumericParameterNames.DALTONISM_SATURATION]: true,
    [LisioNumericParameterNames.DALTONISM_BRIGHTNESS]: true,
  }

  /**
   * Public method implementing abstract method adapt of Adapter
   * @param {LisioTextTreeWalker} walker - A walker to explore the DOM
   * @param {string} value - Value of the functionality
   * @param {LisioParameterNames | undefined} feature - feature that has to be applied by this adapter, if not the default one
   * @param {boolean | number | string | undefined} defaultValue - default value of the input
   * @returns Returns nothing
   * @source
   */
  public adapt(_: LisioTextTreeWalker, value: string, feature?: LisioParameterNames, defaultValue?: boolean | number | string): void {
    if(feature){
      this.areInDefaultState[feature] = value === defaultValue;
    } else {
      this.areInDefaultState[LisioStringParameterNames.DALTONISM] = !(["protanopia", "deuteranopia", "tritanopia", "monochromacy", "enhance_r", "enhance_g"].includes(value));
    }
    this.adaptFunction(document.documentElement);
    let svg = document.querySelector("#lisio-svg");
    let matrice = document.querySelector("#lisio-svg .daltonism-profile-matrix");
    let colorMatrix = document.querySelector("#lisio-svg .daltonism-color-matrix");
    let hueMatrix = document.querySelector("#lisio-svg .daltonism-hue-matrix");
    let saturationMatrix = document.querySelector("#lisio-svg .daltonism-saturation-matrix");
    let brightnessMatrix = document.querySelector("#lisio-svg .daltonism-brightness-matrix");
    if (svg && !matrice) return;
    if (svg == undefined) {
      const filter = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "filter",
      );
      svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      matrice = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "feColorMatrix",
      );
      matrice.classList.add("daltonism-profile-matrix");
      colorMatrix = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "feColorMatrix",
      );
      colorMatrix.classList.add("daltonism-color-matrix");
      hueMatrix = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "feColorMatrix",
      );
      hueMatrix.classList.add("daltonism-hue-matrix");
      hueMatrix.setAttribute("type", "hueRotate");
      hueMatrix.setAttribute("value","0");
      saturationMatrix = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "feColorMatrix",
      );
      saturationMatrix.classList.add("daltonism-saturation-matrix");
      saturationMatrix.setAttribute("type", "saturate");
      saturationMatrix.setAttribute("value","0");
      brightnessMatrix = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "feComponentTransfer",
      );
      brightnessMatrix.classList.add("daltonism-brightness-matrix");
      const brightnessMatrixR = document.createElementNS("http://www.w3.org/2000/svg", "feFuncR");
      const brightnessMatrixG = document.createElementNS("http://www.w3.org/2000/svg", "feFuncG");
      const brightnessMatrixB = document.createElementNS("http://www.w3.org/2000/svg", "feFuncB");
      [brightnessMatrixR, brightnessMatrixG, brightnessMatrixB].forEach(matrix => {
        matrix.setAttribute('type', 'linear');
        matrix.setAttribute('slope', '1');
      })
      brightnessMatrix.append(brightnessMatrixR);
      brightnessMatrix.append(brightnessMatrixG);
      brightnessMatrix.append(brightnessMatrixB);
      svg.setAttribute("height", "0");
      svg.id = "lisio-svg";
      filter.id = "lisio-filter";
      filter.append(matrice);
      filter.append(colorMatrix);
      filter.append(hueMatrix);
      filter.append(saturationMatrix);
      filter.append(brightnessMatrix);
      svg.append(filter);
      document.body.appendChild(svg);
      LisioStyleSheetController.current.insertOrReplaceRule(
        new Map<LisioCSSProperties, string>([
          [LisioCSSProperties.FILTER, "url(#lisio-filter) contrast(1)"],
        ]),
        ".lisio-filters",
        ".lisio-filters",
      );
      LisioStyleSheetController.current.insertOrReplaceRule(
        new Map<LisioCSSProperties, string>([
          [LisioCSSProperties.DISPLAY, "none"],
        ]),
        "#lisio-svg",
        "#lisio-svg",
      );
    }
    if(this.isAllDefault()){
      svg.remove();
      LisioStyleSheetController.current.removeRule(".lisio-filters");
      LisioStyleSheetController.current.removeRule("#lisio-filters");
    }
    if(feature===undefined){
      if (value !== "default") {
        LisioStyleSheetController.current.insertOrReplaceRule(
          new Map<LisioCSSProperties, string>([
            [LisioCSSProperties.FILTER, "url(#lisio-filter) contrast(1)"],
          ]),
          ".lisio-filters",
          ".lisio-filters",
        );
        LisioStyleSheetController.current.insertOrReplaceRule(
          new Map<LisioCSSProperties, string>([
            [LisioCSSProperties.DISPLAY, "none"],
          ]),
          "#lisio-svg",
          "#lisio-svg",
        );
        let matriceValue = "";
        if (value == "protanopia") {
          matriceValue =
            "0.56667 0.43333 0.00000 0 0 0.55833 0.44167 0.00000 0 0 0.00000 0.24167 0.75833 0 0 0 0 0 1 0";
        } else if (value == "deuteranopia") {
          matriceValue =
            "0.4251 0.6934 -0.1147 0 0 0.3417 0.5882 0.0692 0 0 -0.0105 0.0234 0.9870 0 0 0 0 0 1 0";
        } else if (value == "tritanopia") {
          matriceValue =
            "0.95000 0.05000 0.00000 0 0 0.00000 0.83333 0.16700 0 0 0.00000 0.87500 0.12500 0 0 0 0 0 1 0";
        } else if (value == "monochromacy") {
          matriceValue =
            "0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0 0 0 1 0";
        } else if (value == "enhance_r") {
          matriceValue =
            "1.00 0.00 0.00 0 0 0.00 1.00 0.00 0 0 1.00 0.00 0.00 0 0 0 0 0 1 0";
        } else if (value == "enhance_g") {
          matriceValue =
            "1.00 0.00 0.00 0 0 0.00 1.00 0.00 0 0 0.00 1.00 0.00 0 0 0 0 0 1 0";
        }
        matrice?.setAttribute("values", matriceValue);
      } else {
        matrice?.removeAttribute("values");
      }
    } else if(([LisioNumericParameterNames.DALTONISM_R, LisioNumericParameterNames.DALTONISM_G, LisioNumericParameterNames.DALTONISM_B] as unknown as LisioParameterNames).includes(feature) && !this.isAllDefault()){

      let matrixValues: string = document.querySelector("#lisio-filter .daltonism-color-matrix")!.getAttribute("values")!;
      if(!matrixValues){
        matrixValues = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0";
      }
      const matrixValuesArray = matrixValues.split(" ");
      if(feature === LisioNumericParameterNames.DALTONISM_R){
        this.updateColorMatrix(0, parseInt(value, 10), matrixValuesArray);
      } else if(feature === LisioNumericParameterNames.DALTONISM_G){
        this.updateColorMatrix(6, parseInt(value, 10), matrixValuesArray);
      } else if(feature === LisioNumericParameterNames.DALTONISM_B){
        this.updateColorMatrix(12, parseInt(value, 10), matrixValuesArray);
      }
    } else if(feature === LisioNumericParameterNames.DALTONISM_HUE){
      hueMatrix!.setAttribute("values", value);
    } else if(feature === LisioNumericParameterNames.DALTONISM_SATURATION){
      saturationMatrix!.setAttribute("values", (parseInt(value, 10)/100).toString());
    } else {

      const v = parseInt(value, 10) / 100;
      brightnessMatrix!.querySelector("feFuncR")!.setAttribute("slope",v.toString());
      brightnessMatrix!.querySelector("feFuncG")!.setAttribute("slope",v.toString());
      brightnessMatrix!.querySelector("feFuncB")!.setAttribute("slope",v.toString());
    }
  }

  private updateColorMatrix(index: number, value: number, matrixValuesArray: string[]){
    matrixValuesArray[index] = (value/100).toString();
    let endValues: string = "";
    matrixValuesArray.forEach((matrixValue: string, indexValue) => {
      if(indexValue !== 0){
        endValues+= " ";
      }
      endValues+=matrixValue;
    })
    document.querySelector("#lisio-filter .daltonism-color-matrix")!.setAttribute('values', endValues);
  }

  private isAllDefault(){
    return this.areInDefaultState[LisioStringParameterNames.DALTONISM] && this.areInDefaultState[LisioNumericParameterNames.DALTONISM_R] && this.areInDefaultState[LisioNumericParameterNames.DALTONISM_G] && this.areInDefaultState[LisioNumericParameterNames.DALTONISM_B] && this.areInDefaultState[LisioNumericParameterNames.DALTONISM_HUE] && this.areInDefaultState[LisioNumericParameterNames.DALTONISM_SATURATION] && this.areInDefaultState[LisioNumericParameterNames.DALTONISM_BRIGHTNESS]
  }


  protected adaptFunction(element: HTMLElement): void {
    if (this.isAllDefault()) {
      LisioStyleSheetController.current.replaceImportantInStyleAttribute(
        [LisioCSSProperties.FILTER],
        element,
      );
      element.classList.remove("lisio-filters");
    } else {
      element.classList.add("lisio-filters");
    }
  }
}

export default LisioDaltonismAdapter;
