import useEventListener from '@use-it/event-listener';
import { useRef, useCallback, useReducer, useMemo } from 'react';
import {
  FaSave,
  FaWindowClose,
  FaEllipsisH,
  FaVolumeUp,
  FaVolumeMute,
  FaKeyboard,
} from 'react-icons/fa';
import { usePersistedBoolean } from '../lib/hooks';
import {
  Direction,
  fromKeyboardEvent,
  fromKeyString,
  KeyK,
} from '../lib/types';
import { fromCells } from '../lib/viewableGrid';
import { Rebus, EscapeKey } from './Icons';
import { Keyboard } from './Keyboard';
import {
  TopBar,
  TopBarLink,
  TopBarDropDown,
  TopBarDropDownLink,
} from './TopBar';
import {
  gridInterfaceReducer,
  KeypressAction,
  PasteAction,
} from '../reducers/reducer';
import { Square } from './Square';
import { GridView } from './Grid';
import { isSome } from 'fp-ts/lib/Option';

export function AlternateSolutionEditor(props: {
  grid: string[];
  width: number;
  height: number;
  vBars: Set<number>;
  hBars: Set<number>;
  hidden: Set<number>;
  highlighted: Set<number>;
  highlight: 'circle' | 'shade';
  cancel: () => void;
  save: (alt: Record<number, string>) => Promise<void>;
}) {
  const initialGrid = fromCells({
    mapper: (e) => e,
    width: props.width,
    height: props.height,
    cells: props.grid,
    vBars: props.vBars,
    hBars: props.hBars,
    allowBlockEditing: false,
    highlighted: props.highlighted,
    highlight: props.highlight,
    hidden: props.hidden,
  });

  const [state, dispatch] = useReducer(gridInterfaceReducer, {
    type: 'alternate-editor',
    active: { col: 0, row: 0, dir: Direction.Across },
    grid: initialGrid,
    wasEntryClick: false,
    showExtraKeyLayout: false,
    isEnteringRebus: false,
    rebusValue: '',
    downsOnly: false,
    isEditable: () => true,
  });

  const [muted, setMuted] = usePersistedBoolean('muted', false);
  const [toggleKeyboard, setToggleKeyboard] = usePersistedBoolean(
    'keyboard',
    false
  );

  const gridRef = useRef<HTMLDivElement | null>(null);

  const focusGrid = useCallback(() => {
    if (gridRef.current) {
      gridRef.current.focus();
    }
  }, []);

  const physicalKeyboardHandler = useCallback(
    (e: KeyboardEvent) => {
      const mkey = fromKeyboardEvent(e);
      if (isSome(mkey)) {
        const kpa: KeypressAction = { type: 'KEYPRESS', key: mkey.value };
        dispatch(kpa);
        e.preventDefault();
      }
    },
    [dispatch]
  );
  useEventListener(
    'keydown',
    physicalKeyboardHandler,
    gridRef.current || undefined
  );

  const pasteHandler = useCallback(
    (e: ClipboardEvent) => {
      const tagName = (e.target as HTMLElement)?.tagName?.toLowerCase();
      if (tagName === 'textarea' || tagName === 'input') {
        return;
      }
      const pa: PasteAction = {
        type: 'PASTE',
        content: e.clipboardData?.getData('Text') || '',
      };
      dispatch(pa);
      e.preventDefault();
    },
    [dispatch]
  );
  useEventListener('paste', pasteHandler);

  const keyboardHandler = useCallback(
    (key: string) => {
      const mkey = fromKeyString(key);
      if (isSome(mkey)) {
        const kpa: KeypressAction = { type: 'KEYPRESS', key: mkey.value };
        dispatch(kpa);
      }
    },
    [dispatch]
  );

  const topBarChildren = useMemo(() => {
    return (
      <>
        <TopBarLink
          icon={<FaSave />}
          text="Add Alternate"
          onClick={async () => {
            const alt: Record<number, string> = {};
            let hadAny = false;
            for (const [idx, cellValue] of state.grid.cells.entries()) {
              const defaultCellValue = initialGrid.cells[idx];
              if (
                cellValue.trim() &&
                cellValue.trim() != defaultCellValue?.trim()
              ) {
                hadAny = true;
                alt[idx] = cellValue;
              }
            }
            if (!hadAny) {
              props.cancel();
              return;
            }
            return props.save(alt).then(() => props.cancel());
          }}
        />
        <TopBarLink
          icon={<FaWindowClose />}
          text="Cancel"
          onClick={props.cancel}
        />
        <TopBarDropDown onClose={focusGrid} icon={<FaEllipsisH />} text="More">
          {() => (
            <>
              <TopBarDropDownLink
                icon={<Rebus />}
                text="Enter Rebus"
                shortcutHint={<EscapeKey />}
                onClick={() => {
                  const a: KeypressAction = {
                    type: 'KEYPRESS',
                    key: { k: KeyK.Escape },
                  };
                  dispatch(a);
                }}
              />
              {muted ? (
                <TopBarDropDownLink
                  icon={<FaVolumeUp />}
                  text="Unmute"
                  onClick={() => setMuted(false)}
                />
              ) : (
                <TopBarDropDownLink
                  icon={<FaVolumeMute />}
                  text="Mute"
                  onClick={() => setMuted(true)}
                />
              )}
              <TopBarDropDownLink
                icon={<FaKeyboard />}
                text="Toggle Keyboard"
                onClick={() => setToggleKeyboard(!toggleKeyboard)}
              />
            </>
          )}
        </TopBarDropDown>
      </>
    );
  }, [
    focusGrid,
    initialGrid.cells,
    muted,
    props,
    setMuted,
    setToggleKeyboard,
    state.grid.cells,
    toggleKeyboard,
  ]);

  const parentRef = useRef<HTMLDivElement | null>(null);

  return (
    <>
      <div
        css={{
          display: 'flex',
          flexDirection: 'column',
          height: '100%',
        }}
      >
        <div css={{ flex: 'none' }}>
          <TopBar>{topBarChildren}</TopBar>
        </div>
        <div
          css={{ flex: '1 1 auto', overflow: 'scroll', position: 'relative' }}
        >
          <div
            // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
            tabIndex={0}
            ref={(instance) => {
              parentRef.current = instance;
            }}
            css={{
              outline: 'none',
              display: 'flex',
              flex: '1 1 auto',
              flexDirection: 'column',
              alignItems: 'center',
              height: '100%',
              width: '100%',
              position: 'absolute',
              flexWrap: 'nowrap',
            }}
          >
            <Square
              noColumns={true}
              waitToResize={false}
              parentRef={parentRef}
              aspectRatio={props.width / props.height}
              contents={(width: number, _height: number) => {
                return (
                  <GridView
                    isEnteringRebus={state.isEnteringRebus}
                    rebusValue={state.rebusValue}
                    squareWidth={width}
                    grid={state.grid}
                    defaultGrid={initialGrid}
                    active={state.active}
                    dispatch={dispatch}
                    allowBlockEditing={false}
                    autofill={[]}
                  />
                );
              }}
            />
          </div>
        </div>
        <div css={{ flex: 'none', width: '100%' }}>
          <Keyboard
            toggleKeyboard={toggleKeyboard}
            keyboardHandler={keyboardHandler}
            muted={muted}
            showExtraKeyLayout={state.showExtraKeyLayout}
            includeBlockKey={false}
          />
        </div>
      </div>
    </>
  );
}