import { Double, getType, Int, newDynamic, trie } from "dope2";
import { focusNextElement, focusPrevElement, getCaretPosition, setRightMostCaretPosition } from "./util/dom_trickery";
import { parseDouble, parseInt } from "./util/parse";

import { useEffect, useRef, useState } from "react";
import { Type } from "./Type";

import "./InputBlock.css";
import type { Dynamic, State2Props } from "./util/extra";
import type { EditorState } from "./Editor";
import { ShowIf } from "./ShowIf";

export interface InputBlockState {
  kind: "input";
  env: any;
  text: string;
  resolved: undefined | Dynamic;
  rollback?: EditorState;
}

interface InputBlockProps extends State2Props<InputBlockState> {
  filter: (ls: any[]) => boolean;
  onResolve: (state: InputBlockState) => void;
  onCancel: () => void;
}

export function InputBlock({ state: {kind, env, text, resolved, rollback}, setState, filter, onResolve, onCancel }: InputBlockProps) {
  const ref = useRef<any>(null);
  useEffect(() => {
    ref.current?.focus();
  }, []);

  const [i, setI] = useState(0); // selected suggestion
  const [haveFocus, setHaveFocus] = useState(false); // whether to render suggestions or not

  const setText = (text: string) => {
    setState({kind, env, text, resolved, rollback});
  }
  const setResolved = (resolved: Dynamic) => {
    setState({kind, env, text, resolved, rollback});
  }

  const singleSuggestion = trie.growPrefix(env.name2dyn)(text);

  const asDouble = parseDouble(text);
  const asInt = parseInt(text);

  const suggestions = [
    ... (asDouble ? [[asDouble.toString(), newDynamic(asDouble)(Double)]] : []),
    ... (asInt ? [[asInt.toString(), newDynamic(BigInt(asInt))(Int)]] : []),

    ... (text !== '') ? trie.suggest(env.name2dyn)(text)(Infinity) : [],
  ].filter(filter);

  useEffect(() => {
    setI(0); // reset
    if (ref.current) {
      ref.current.style.width = `${text.length === 0 ? 140 : (text.length*8.7)}px`;
    }
  }, [text]);

  const onSelectSuggestion = ([name, dynamic]) => {
    console.log(name);
    // setText(name);
    // ref.current.textContent = name;
    // setRightMostCaretPosition(ref.current);
    // setI(0);
    // setResolved(dynamic);
    console.log("onResolve inputblock..")
    onResolve({
      kind: "input",
      env,
      text: name,
      resolved: dynamic,
      rollback,
    });
  };

  const onInput = e => {
    setText(e.target.value);
    if (resolved) {
      onResolve({
        kind: "input",
        env,
        text: e.target.value,
        resolved: undefined,
        rollback,
      });
    }
  };

  const getCaretPosition = () => {
    return ref.current.selectionStart;
  }

  const onKeyDown = (e: React.KeyboardEvent) => {
    const fns = {
      Tab: () => {
        if (e.shiftKey) {
          focusPrevElement();
          e.preventDefault();
        }
        else {
          // not shift key
          if (singleSuggestion.length > 0) {
            const newText = text + singleSuggestion;
            setText(newText);
            // ref.current.textContent = newText;
            setRightMostCaretPosition(ref.current);
            e.preventDefault();  
          }
          else {
            onSelectSuggestion(suggestions[i]);
            e.preventDefault();
          }
        }
      },
      ArrowDown: () => {
        setI((i + 1) % suggestions.length);
        e.preventDefault();
      },
      ArrowUp: () => {
        setI((i - 1) % suggestions.length);
        e.preventDefault();  
      },
      ArrowLeft: () => {
        if (getCaretPosition() === 0) {
          focusPrevElement();
          e.preventDefault();
        }
      },
      ArrowRight: () => {
        if (getCaretPosition() === text.length) {
          focusNextElement();
          e.preventDefault();
        }
      },
      Enter: () => {
        onSelectSuggestion(suggestions[i]);
        e.preventDefault();
      },
      Backspace: () => {
        if (text.length === 0) {
          onCancel();
          e.preventDefault();
        }
      }
    };
    fns[e.key]?.();
  };

  return <span>
    <span className="">
      <input ref={ref} placeholder="start typing..." className="editable" value={text} onInput={onInput} onKeyDown={onKeyDown} onFocus={() => setHaveFocus(true)} onBlur={() => setTimeout(() => setHaveFocus(false), 200)}/>
      <span className="text-block suggest">{singleSuggestion}</span>
      </span>
      <ShowIf cond={haveFocus}>
        <Suggestions
          suggestions={suggestions}
          onSelect={onSelectSuggestion}
          i={i} setI={setI} />
      </ShowIf>
  </span>;
}

function Suggestions({ suggestions, onSelect, i, setI }) {
  const onMouseEnter = j => () => {
    setI(j);
  };
  const onMouseDown = j => () => {
    setI(j);
    onSelect(suggestions[i]);
  };

  return (suggestions.length > 0) ?
    <div className={"suggestions"}>
      {suggestions.map(([name, dynamic], j) =>
        <div
          key={`${j}_${name}`}
          className={i === j ? "selected" : ""}
          onMouseEnter={onMouseEnter(j)}
          onMouseDown={onMouseDown(j)}>
            {name} :: <Type type={getType(dynamic)} />
        </div>)
      }
    </div>
    : <></>;
}