import { ProjectState, Selection, UndoStep } from '../types';
import { useCallback, useState, useMemo } from 'react';
import useSelection from './useSelection';

export default (initialState: ProjectState) => {
  const [state, setProjectState] = useState<ProjectState>(initialState);
  const { selection, interactions: selectionInteractions } = useSelection(state);
  const setSelection = selectionInteractions.set;
  const [history, setHistory] = useState<UndoStep[]>([{ state, selectionBefore: selection, selectionAfter: selection }]);
  const [historyIndex, setHistoryIndex] = useState(0);

  const pushProjectState = useCallback(
    (newState, newSelection) => {
      if (history.length === 0) {
        throw new Error('History must always have at least one element');
      }
      setProjectState(newState);
      setSelection(newSelection);
      const storedHistory = history.slice(0, historyIndex + 1);
      setHistory([
        ...storedHistory,
        {
          state: newState,
          selectionBefore: selection,
          selectionAfter: newSelection
        }
      ]);
      setHistoryIndex(historyIndex + 1);
    },
    [history, setHistory, historyIndex, setProjectState, selection, setSelection]
  );

  const alterProjectState = useCallback(
    (newState, newSelection) => {
      if (history.length === 0) {
        throw new Error('History must always have at least one element');
      }
      setProjectState(newState);
      setSelection(newSelection);
      setHistory([
        ...history.slice(0, historyIndex),
        {
          state: newState,
          selectionBefore: selection,
          selectionAfter: newSelection
        }
      ]);
    },
    [history, setProjectState, setSelection, selection, setHistory, historyIndex]
  );

  const undo = useCallback(
    () => {
      // logUndo();
      const oldIndex = historyIndex;
      const newIndex = Math.max(0, historyIndex - 1);
      setHistoryIndex(newIndex);
      setProjectState(history[newIndex].state);
      setSelection(history[oldIndex].selectionBefore);
    },
    [setHistoryIndex, historyIndex, history, setSelection, setProjectState]
  );

  const redo = useCallback(
    () => {
      // logRedo();
      const newIndex = Math.min(history.length - 1, historyIndex + 1);
      setHistoryIndex(newIndex);
      setProjectState(history[newIndex].state);
      setSelection(history[newIndex].selectionAfter);
    },
    [setHistoryIndex, historyIndex, history, setSelection, setProjectState]
  );

  const loadProjectState = useCallback(
    (state, selection: Selection = [[0,0], []]) => {
      setHistory([{ state, selectionBefore: [[0,0], []], selectionAfter: [[0,0], []] }]);
      setHistoryIndex(0);
      setProjectState(state);
      setSelection(selection);
    },
    [setHistory, setHistoryIndex, setProjectState, setSelection]
  );

  return useMemo(
    () => ({ state, selection, selectionInteractions, undo, redo, pushProjectState, alterProjectState, loadProjectState }),
    [state, selection, selectionInteractions, undo, redo, pushProjectState, alterProjectState, loadProjectState]
  );
}
