import React, { Dispatch, SetStateAction, useEffect, useRef } from 'react';
import './Editor.scss';
import { IConfiguration } from '../../Interfaces/IConfiguration';
import { SVG } from '../SVG/SVG';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import { UI } from '../UI/UI';
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer, OnPropertyChange } from './Actions/ContainerOperations';
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
import { onKeyDown } from './Actions/Shortcuts';
import EditorEvents from '../../Events/EditorEvents';
import { IEditorState } from '../../Interfaces/IEditorState';
import { MAX_HISTORY } from '../../utils/default';
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
import { findContainerById } from '../../utils/itertools';

interface IEditorProps {
  configuration: IConfiguration
  history: IHistoryState[]
  historyCurrentStep: number
}

export function UpdateCounters(counters: Record<string, number>, type: string): void {
  if (counters[type] === null ||
    counters[type] === undefined) {
    counters[type] = 0;
  } else {
    counters[type]++;
  }
}

export const getCurrentHistory = (history: IHistoryState[], historyCurrentStep: number): IHistoryState[] =>
  history.slice(
    Math.max(0, history.length - MAX_HISTORY), // change this to 0 for unlimited (not recommanded because of overflow)
    historyCurrentStep + 1
  );

export const getCurrentHistoryState = (history: IHistoryState[], historyCurrentStep: number): IHistoryState => history[historyCurrentStep];

function useShortcuts(
  history: IHistoryState[],
  historyCurrentStep: number,
  setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
  useEffect(() => {
    const onKeyUp = (event: KeyboardEvent): void => onKeyDown(
      event,
      history,
      historyCurrentStep,
      setHistoryCurrentStep
    );

    window.addEventListener('keyup', onKeyUp);
    return () => {
      window.removeEventListener('keyup', onKeyUp);
    };
  });
}

function useWindowEvents(
  history: IHistoryState[],
  historyCurrentStep: number,
  configuration: IConfiguration,
  editorRef: React.RefObject<HTMLDivElement>
): void {
  useEffect(() => {
    const events = EditorEvents;
    const editorState: IEditorState = {
      history,
      historyCurrentStep,
      configuration
    };

    const funcs = new Map<string, () => void>();
    for (const event of events) {
      const func = (): void => event.func(editorState);
      editorRef.current?.addEventListener(event.name, func);
      funcs.set(event.name, func);
    }
    return () => {
      for (const event of events) {
        const func = funcs.get(event.name);
        if (func === undefined) {
          continue;
        }
        editorRef.current?.removeEventListener(event.name, func);
      }
    };
  });
}

const Editor: React.FunctionComponent<IEditorProps> = (props) => {
  const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
  const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
  const editorRef = useRef<HTMLDivElement>(null);

  useShortcuts(history, historyCurrentStep, setHistoryCurrentStep);
  useWindowEvents(history, historyCurrentStep, props.configuration, editorRef);

  const configuration = props.configuration;
  const current = getCurrentHistoryState(history, historyCurrentStep);
  const selected = findContainerById(current.MainContainer, current.SelectedContainerId);
  return (
    <div ref={editorRef} className="Editor font-sans h-full">
      <UI
        SelectedContainer={selected}
        current={current}
        history={history}
        historyCurrentStep={historyCurrentStep}
        AvailableContainers={configuration.AvailableContainers}
        AvailableSymbols={configuration.AvailableSymbols}
        SelectContainer={(container) => SelectContainer(
          container,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        DeleteContainer={(containerId: string) => DeleteContainer(
          containerId,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        OnPropertyChange={(key, value, isStyle) => OnPropertyChange(
          key, value, isStyle,
          selected,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
          type,
          selected,
          configuration,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        AddContainer={(index, type, parentId) => AddContainer(
          index,
          type,
          parentId,
          configuration,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        AddSymbol={(type) => AddSymbol(
          type,
          configuration,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        OnSymbolPropertyChange={(key, value) => OnSymbolPropertyChange(
          key, value,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        SelectSymbol={(symbolId) => SelectSymbol(
          symbolId,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        DeleteSymbol={(symbolId) => DeleteSymbol(
          symbolId,
          history,
          historyCurrentStep,
          setHistory,
          setHistoryCurrentStep
        )}
        SaveEditorAsJSON={() => SaveEditorAsJSON(
          history,
          historyCurrentStep,
          configuration
        )}
        SaveEditorAsSVG={() => SaveEditorAsSVG()}
        LoadState={(move) => setHistoryCurrentStep(move)}
      />
      <SVG
        width={current.MainContainer?.properties.width}
        height={current.MainContainer?.properties.height}
        selected={selected}
        symbols={current.Symbols}
      >
        { current.MainContainer }
      </SVG>
    </div>
  );
};

export default Editor;
