import { createMemo, createSignal, Show } from 'solid-js';
import { sourceText } from 'source-region';
import type { CodePointSpan, SourceRegion, SourceText } from 'source-region';
import { parseDocument } from '../parser';
import type { ParseError } from '../parser';
import type { ConcreteSyntax } from '../syntax';
import { spanLabel } from './format';
import { PaneHeader, PaneSplitter } from './Pane';
import { SourceGrid } from './SourceGrid';
import type { SourceGridAnnotation } from './SourceGrid';
import { ErrorList, ExpressionList } from './SyntaxPane';
import type { HoverTarget } from './types';

type ParsedDocument = {
  source: SourceText;
  region: SourceRegion;
  values: ConcreteSyntax[];
  errors: ParseError[];
};

const SAMPLE_INPUT = `(define square (_ x) (mul x x))

(add 1 2)

(define pyth (_ x y) (+ (square x) (square y)))

foo ) @@@ (bar 1)
(nested (list 123 abc_9 name-with-dash))`;

export function App() {
  const [input, setInput] = createSignal(SAMPLE_INPUT);
  const [hovered, setHovered] = createSignal<HoverTarget | undefined>();
  const [leftWidth, setLeftWidth] = createSignal(420);
  const [middleWidth, setMiddleWidth] = createSignal(420);

  const parsed = createMemo<ParsedDocument>(() => {
    const source = sourceText(input());
    const region = source.fullRegion();
    const result = parseDocument(region);
    return { source, region, values: result.values, errors: result.errors };
  });

  return (
    <main
      class="app-shell"
      style={{
        "--left-width": `${leftWidth()}px`,
        "--middle-width": `${middleWidth()}px`,
      }}
    >
      <section class="pane input-pane">
        <PaneHeader title="Source" detail={`${input().length} UTF-16 units`} />
        <textarea
          class="source-input"
          spellcheck={false}
          value={input()}
          onInput={(event) => {
            setInput(event.currentTarget.value);
            setHovered(undefined);
          }}
        />
      </section>

      <PaneSplitter
        label="Resize source and structure panes"
        onDrag={(delta) => {
          setLeftWidth((width) => clamp(width + delta, 280, 760));
        }}
      />

      <section class="pane structure-pane">
        <PaneHeader
          title="Structure"
          detail={`${parsed().values.length} expressions, ${parsed().errors.length} errors`}
        />
        <Show
          when={parsed().errors.length > 0}
          fallback={
            <ExpressionList
              values={parsed().values}
              onHover={setHovered}
            />
          }
        >
          <ErrorList
            errors={parsed().errors}
            values={parsed().values}
            onHover={setHovered}
          />
        </Show>
      </section>

      <PaneSplitter
        label="Resize structure and source grid panes"
        onDrag={(delta) => {
          setMiddleWidth((width) => clamp(width + delta, 260, 760));
        }}
      />

      <section class="pane source-pane">
        <PaneHeader
          title="Source Grid"
          detail={hovered() ? spanLabel(hovered()!.span) : "nothing hovered"}
        />
        <SourceGrid
          source={parsed().source}
          region={parsed().region}
          annotations={hovered() ? [hoverAnnotation(hovered()!)] : []}
        />
      </section>
    </main>
  );
}

function hoverAnnotation(target: HoverTarget): SourceGridAnnotation {
  return {
    id: "hovered",
    span: target.span,
    label: target.label,
    cellClass: "annotation-hovered",
    markerClass: "annotation-hovered-marker",
  };
}

function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value));
}
