import { createMemo, createSignal } from 'solid-js';
import { sourceText } from 'source-region';
import type { CodePointSpan, SourceRegion, SourceText } from 'source-region';
import { parseDocument, programOf } from '../parser';
import type { ConcreteSyntaxResult, PartialConcreteSyntax } from '../parser';
import type { ParseError } from '../parse_errors';
import { spanLabel } from './format';
import { PaneHeader, PaneSplitter } from './Pane';
import { SourceGrid } from './SourceGrid';
import type { SourceGridAnnotation } from './SourceGrid';
import { StructureTree } from './SyntaxPane';
import type { HoverTarget } from './types';

type ParsedDocument = {
  source: SourceText;
  region: SourceRegion;
  syntax: ConcreteSyntaxResult;
  program: PartialConcreteSyntax;
  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])
[a, b c, d]
123fasd`;

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, syntax: result.syntax, program: programOf(result.syntax), 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().syntax.tag}, ${parsed().program.expressions.length} expressions, ${parsed().errors.length} errors`}
        />
        <StructureTree
          program={parsed().program}
          isValid={parsed().syntax.tag === "valid"}
          errorCount={parsed().errors.length}
          onHover={setHovered}
        />
      </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));
}
