import { Controller } from "@tingwu/core";
import { ViewedWord } from "../../model";
import { ComposingData } from "./styled";
import React from "react";

export interface Character {
  type: "text" | "wrapper" | "composing";
  offset: number;
  text: string;
  wrapperClassName?: string;
}

export type Characters = Character[];

function getSearchCls(word: ViewedWord, index: number) {
  const { searchedOffsets } = word;
  let cls = "";
  searchedOffsets.some(offset => {
    const { startOffset, endOffset, isActive } = offset;
    if (startOffset <= index && index < endOffset) {
      cls = "searched";
      if (isActive) {
        cls += " actived";
      }
    }
    return false;
  });

  return cls;
}

export function getCharacter(word: ViewedWord, index: number, offset: number): Character {
  const cls = getSearchCls(word, index);

  return {
    offset,
    text: word.text[index],
    type: cls ? "wrapper" : "text",
    wrapperClassName: cls || undefined,
  };
}

export function getCharacters(
  word: ViewedWord,
  offset: number,
  composingDataOffset?: number,
  composingData?: string
) {
  const { length } = word.text;
  const characters: Characters = [];

  for (let i = 0; i < length; i++) {
    if (i === composingDataOffset && composingData) {
      characters.push({
        offset: composingDataOffset,
        type: "composing",
        text: composingData,
      });
    }
    characters.push(getCharacter(word, i, offset + i));
  }
  if (length === composingDataOffset && composingData) {
    characters.push({
      offset: composingDataOffset,
      type: "composing",
      text: composingData,
    });
  }

  return characters;
}

function getMergedCharacter(character1: Character, character2: Character) {
  if (
    character1.type === character2.type &&
    character1.wrapperClassName === character2.wrapperClassName
  ) {
    return {
      ...character1,
      text: character1.text + character2.text,
    };
  }
  return undefined;
}

function renderCharacter(character: Character, controller: Controller, isHighlight: boolean) {
  const { type, text, wrapperClassName, offset } = character;
  if (type === "composing") {
    return (
      <ComposingData controller={controller} isHighlight={isHighlight}>
        {text}
      </ComposingData>
    );
  } else {
    return (
      <span className={wrapperClassName} data-sel-offset={offset}>
        {text}
      </span>
    );
  }
}

export function getElementsByCharacters(
  characters: Characters,
  controller: Controller,
  isHighlight: boolean
) {
  const { length } = characters;
  let stash: Character | undefined;

  const elements: React.ReactNode[] = [];

  for (let i = 0; i < length; i++) {
    const character = characters[i];
    if (!stash) {
      stash = character;
    } else {
      const result = getMergedCharacter(stash, character);
      if (result) {
        stash = result;
      } else {
        elements.push(renderCharacter(stash, controller, isHighlight));
        stash = character;
      }
    }
  }

  if (stash) {
    elements.push(renderCharacter(stash, controller, isHighlight));
    stash = undefined;
  }

  return elements;
}

export function getElements(
  controller: Controller,
  word: ViewedWord,
  isHighlight: boolean,
  offset: number,
  composingDataOffset?: number,
  composingData?: string
) {
  return getElementsByCharacters(
    getCharacters(word, offset, composingDataOffset, composingData),
    controller,
    isHighlight
  );
}
