import { EnumValidateState } from "./enums";
import { IStructuralCodeLevel } from "./Types";

export type IResultOfValidate = true | [string, EnumValidateState];
export type IStructuralCodeState =
  | 'clear'
  | 'ok'
  | 'dirty'
  | 'withoutParent'
  | 'newCode';

export interface IStructuralCodeChangeEvent {
  code: string | null;
  state: IStructuralCodeState;
  level: IStructuralCodeLevel;
}

export interface IStructureCoding {
  coding: string;
  maxLevel: number;
  maxCodingLen: number;
  levelLen1: number;
  levelLen2: number;
  levelLen3: number;
  levelLen4: number;
  levelLen5: number;
  levelLen6: number;
  levelLen7: number;
  levelLen8: number;
  levelLen9: number;
  untilLevelLen1: number;
  untilLevelLen2: number;
  untilLevelLen3: number;
  untilLevelLen4: number;
  untilLevelLen5: number;
  untilLevelLen6: number;
  untilLevelLen7: number;
  untilLevelLen8: number;
  untilLevelLen9: number;
}

export class StructureCoding {
  public normalWidth: number = 0;
  public editedWidth: number = 0;

  constructor(
    public coding: string,
    public maxLevel: number,
    public maxCodingLen: number,
    public levelLen1: number,
    public levelLen2: number,
    public levelLen3: number,
    public levelLen4: number,
    public levelLen5: number,
    public levelLen6: number,
    public levelLen7: number,
    public levelLen8: number,
    public levelLen9: number,
    public untilLevelLen1: number,
    public untilLevelLen2: number,
    public untilLevelLen3: number,
    public untilLevelLen4: number,
    public untilLevelLen5: number,
    public untilLevelLen6: number,
    public untilLevelLen7: number,
    public untilLevelLen8: number,
    public untilLevelLen9: number,
  ) { }

  lengthOfLevel = (level: IStructuralCodeLevel): number => {
    return level === 1
      ? this.untilLevelLen1
      : level === 2
        ? this.untilLevelLen2
        : level === 3
          ? this.untilLevelLen3
          : level === 4
            ? this.untilLevelLen4
            : level === 5
              ? this.untilLevelLen5
              : level === 6
                ? this.untilLevelLen6
                : level === 7
                  ? this.untilLevelLen7
                  : level === 8
                    ? this.untilLevelLen8
                    : level === 9
                      ? this.untilLevelLen9
                      : this.untilLevelLen9 + 1;
  };

  stateOfLevel = (
    value: string,
    level: IStructuralCodeLevel
  ): IStructuralCodeState => {
    if (!value || value === '') {
      return 'clear';
    }
    return value.length === this.lengthOfLevel(level) ? 'ok' : 'dirty';
  };

  stateAndLevelOfCode = (
    value: string
  ): [IStructuralCodeState, IStructuralCodeLevel] => {
    if (typeof value !== 'string' || value.length === 0) {
      return ['clear', 0];
    } else if (value.length <= this.untilLevelLen1) {
      return [this.stateOfLevel(value, 1), 1];
    } else if (value.length <= this.untilLevelLen2) {
      return [this.stateOfLevel(value, 2), 2];
    } else if (value.length <= this.untilLevelLen3) {
      return [this.stateOfLevel(value, 3), 3];
    } else if (value.length <= this.untilLevelLen4) {
      return [this.stateOfLevel(value, 4), 4];
    } else if (value.length <= this.untilLevelLen5) {
      return [this.stateOfLevel(value, 5), 5];
    } else if (value.length <= this.untilLevelLen6) {
      return [this.stateOfLevel(value, 6), 6];
    } else if (value.length <= this.untilLevelLen7) {
      return [this.stateOfLevel(value, 7), 7];
    } else if (value.length <= this.untilLevelLen8) {
      return [this.stateOfLevel(value, 8), 8];
    } else if (value.length <= this.untilLevelLen9) {
      return [this.stateOfLevel(value, 9), 9];
    } else {
      return ['dirty', undefined];
    }
  };

  changeEventOfCode = (value: string): IStructuralCodeChangeEvent => {
    const [state, levelOfCode] = this.stateAndLevelOfCode(value);
    return { code: value, state: state, level: levelOfCode };
  };

  static buildNew(coding: IStructureCoding) {
    return new StructureCoding(
      coding.coding,
      coding.maxLevel,
      coding.maxCodingLen,
      coding.levelLen1,
      coding.levelLen2,
      coding.levelLen3,
      coding.levelLen4,
      coding.levelLen5,
      coding.levelLen6,
      coding.levelLen7,
      coding.levelLen8,
      coding.levelLen9,
      coding.untilLevelLen1,
      coding.untilLevelLen2,
      coding.untilLevelLen3,
      coding.untilLevelLen4,
      coding.untilLevelLen5,
      coding.untilLevelLen6,
      coding.untilLevelLen7,
      coding.untilLevelLen8,
      coding.untilLevelLen9,
    )
  }
}

export class StructureCodingFactory {
  static deserialize(json: IStructureCoding): StructureCoding {
    return new StructureCoding(
      json.coding,
      json.maxLevel,
      json.maxCodingLen,
      json.levelLen1,
      json.levelLen2,
      json.levelLen3,
      json.levelLen4,
      json.levelLen5,
      json.levelLen6,
      json.levelLen7,
      json.levelLen8,
      json.levelLen9,
      json.untilLevelLen1,
      json.untilLevelLen2,
      json.untilLevelLen3,
      json.untilLevelLen4,
      json.untilLevelLen5,
      json.untilLevelLen6,
      json.untilLevelLen7,
      json.untilLevelLen8,
      json.untilLevelLen9
    );
  }
  static empty(): StructureCoding {
    return new StructureCoding(
      '',
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0
    );
  }
  static createCopy(old: StructureCoding): StructureCoding {
    return new StructureCoding(
      old.coding,
      old.maxLevel,
      old.maxCodingLen,
      old.levelLen1,
      old.levelLen2,
      old.levelLen3,
      old.levelLen4,
      old.levelLen5,
      old.levelLen6,
      old.levelLen7,
      old.levelLen8,
      old.levelLen9,
      old.untilLevelLen1,
      old.untilLevelLen2,
      old.untilLevelLen3,
      old.untilLevelLen4,
      old.untilLevelLen5,
      old.untilLevelLen6,
      old.untilLevelLen7,
      old.untilLevelLen8,
      old.untilLevelLen9
    );
  }
}
