import {
  CompoundElement,
  FontElement,
  FontProperties,
  InputElement,
  InputElementValue,
  LikertColumn,
  LikertRow,
  PositionedElement, PositionProperties,
  UIElement
} from '../../models/uI-element';
import { TextFieldElement } from '../text-field/text-field-element';
import { TextAreaElement } from '../text-area/text-area-element';
import { CheckboxElement } from '../checkbox/checkbox-element';
import { DropdownElement } from '../dropdown/dropdown-element';
import { DropListElement } from '../drop-list/drop-list';
import { initFontElement, initPositionedElement } from '../../util/unit-interface-initializer';
import { TextFieldSimpleElement } from '../textfield-simple/text-field-simple-element';
import { DropListSimpleElement } from '../drop-list-simple/drop-list-simple';

// TODO styles like em dont continue after inserted components

export class ClozeElement extends CompoundElement implements PositionedElement, FontElement {
  text: string = '<p>Lorem ipsum dolor \\z sdfsdf \\i sdfsdf</p>';
  parts: {
    type: string;
    value: string | UIElement;
    style?: string;
  }[][] = [];

  childElements: InputElement[] = [];

  positionProps: PositionProperties;
  fontProps: FontProperties;

  constructor(serializedElement: UIElement) {
    super(serializedElement);
    Object.assign(this, serializedElement);
    this.positionProps = initPositionedElement(serializedElement);
    this.fontProps = initFontElement(serializedElement);
    this.height = 200;
    this.width = 500; // TODO
  }

  setProperty(property: string, value: InputElementValue | string[] | LikertColumn[] | LikertRow[]): void {
    super.setProperty(property, value);

    if (property === 'text') {
      this.createParts(value as string);
    }
  }

  private createParts(htmlText: string): void {
    const elementList = ClozeElement.readElementArray(htmlText);

    this.parts = [];
    elementList.forEach((element: HTMLParagraphElement | HTMLHeadingElement, i: number) => {
      this.parseParagraphs(element, i);
    });
    // console.log('PARTS:', this.parts);
  }

  private static readElementArray(htmlText: string): (HTMLParagraphElement | HTMLHeadingElement)[] {
    const el = document.createElement('html');
    el.innerHTML = htmlText;
    return Array.from(el.children[1].children) as (HTMLParagraphElement | HTMLHeadingElement)[];
  }

  private parseParagraphs(element: HTMLParagraphElement | HTMLHeadingElement, partIndex: number): void {
    this.parts[partIndex] = []; // init array to be able to push
    let [nextSpecialElementIndex, nextElementType] = ClozeElement.getNextSpecialElement(element.innerHTML);
    let indexOffset = 0;

    while (nextSpecialElementIndex !== -1) {
      nextSpecialElementIndex += indexOffset;
      this.parts[partIndex].push({
        type: element.localName,
        value: element.innerHTML.substring(indexOffset, nextSpecialElementIndex),
        style: element.style.cssText
      });

      const newElement = ClozeElement.createElement(nextElementType);
      this.childElements.push(newElement);
      this.parts[partIndex].push({ type: nextElementType, value: newElement });

      indexOffset = nextSpecialElementIndex + 2; // + 2 to get rid of the marker, i.e. '\b'
      [nextSpecialElementIndex, nextElementType] =
        ClozeElement.getNextSpecialElement(element.innerHTML.substring(indexOffset));
    }
    this.parts[partIndex].push({
      type: element.localName,
      value: element.innerHTML.substring(indexOffset),
      style: element.style.cssText
    });
  }

  private static getNextSpecialElement(p: string): [number, string] {
    const x = [];
    if (p.indexOf('\\d') > 0) {
      x.push(p.indexOf('\\d'));
    }
    if (p.indexOf('\\i') > 0) {
      x.push(p.indexOf('\\i'));
    }
    if (p.indexOf('\\z') > 0) {
      x.push(p.indexOf('\\z'));
    }

    const y = Math.min(...x);
    let nextElementType = '';
    switch (p[y + 1]) {
      case 'd': nextElementType = 'dropdown'; break;
      case 'i': nextElementType = 'text-field'; break;
      case 'z': nextElementType = 'drop-list'; break;
      default: return [-1, 'unknown'];
    }
    return [y, nextElementType];
  }

  private static createElement(elementType: string): InputElement {
    const elementModel: UIElement = { type: elementType } as UIElement;
    let newElement: InputElement;
    switch (elementModel.type) {
      case 'text-field':
        newElement = new TextFieldSimpleElement(elementModel);
        (newElement as TextFieldElement).label = '';
        break;
      case 'text-area':
        newElement = new TextAreaElement(elementModel);
        break;
      case 'checkbox':
        newElement = new CheckboxElement(elementModel);
        break;
      case 'dropdown':
        newElement = new DropdownElement(elementModel);
        break;
      case 'drop-list':
        newElement = new DropListElement(elementModel);
        newElement.height = 30;
        newElement.width = 100;
        newElement.onlyOneItem = true;
        break;
      default:
        throw new Error(`ElementType ${elementModel.type} not found!`);
    }
    return newElement;
  }
}