import { Rect, Txt, makeScene2D } from "@motion-canvas/2d";
import {
  Direction,
  all,
  beginSlide,
  createRef,
  slideTransition,
} from "@motion-canvas/core";

import { FunctionBox } from "../components/function_box";
import { theme } from "../theme";

const pureFib = `const pureFib = (n: number) => {
  if (n <= 1)
    return 1;
  return pureFib(n - 1) + pureFib(n - 2);
};
pureFib;`;

const impureFibFunction = `window.cache = [1, 1];
const impureFib = (n: number) => {
  while (cache.length <= n + 1)
    cache.push(cache.at(-1) + cache.at(-2));
  return cache[n];
};
impureFib;`;
const impureFactFunction = `const impureFact = (n: number): number => {
  while (cache.length <= n)
    cache.push(cache.length *
              cache[cache.length - 1]);
  return cache[n];
};
impureFact;`;

export default makeScene2D(function* (view) {
  const impureFibFunctionBox = createRef<FunctionBox>();
  const impureFactFunctionBox = createRef<FunctionBox>();
  const pureFunctionBox = createRef<FunctionBox>();

  view.add(
    <Rect direction="row" gap={300} layout>
      <Rect direction="column" alignItems="center" gap={20}>
        <Txt fontSize={30} fontFamily={theme.font} fill={theme.text.hex}>
          IMPURE
        </Txt>
        <FunctionBox
          arity={1}
          idlingText=""
          workingText=""
          source={impureFibFunction}
          ref={impureFibFunctionBox}
        />
        <FunctionBox
          arity={1}
          idlingText=""
          workingText=""
          source={impureFactFunction}
          ref={impureFactFunctionBox}
        />
      </Rect>
      <Rect direction="column" alignItems="center" gap={20}>
        <Txt fontSize={30} fontFamily={theme.font} fill={theme.text.hex}>
          PURE
        </Txt>
        <FunctionBox
          arity={1}
          idlingText=""
          workingText=""
          source={pureFib}
          ref={pureFunctionBox}
        />
      </Rect>
    </Rect>,
  );

  yield* slideTransition(Direction.Right);
  yield* beginSlide("Pure vs Impure");

  yield* all(
    impureFibFunctionBox().showCode(0.5),
    impureFactFunctionBox().showCode(0.5),
  );

  yield* impureFibFunctionBox().setInputs([{ val: 5 }], 0.2);
  yield* beginSlide("Show FibImpure(5)");

  yield* impureFibFunctionBox().propogateInput(0.2);
  yield* impureFibFunctionBox().propogateOutput(0.2);
  yield* beginSlide("FibImpure(5)");

  yield* impureFactFunctionBox().setInputs([{ val: 5 }], 0.2);
  yield* beginSlide("Show FactImpure(5)");

  yield* impureFactFunctionBox().propogateInput(0.2);
  yield* impureFactFunctionBox().propogateOutput(0.2);
  yield* beginSlide("FactImpure(5)");

  yield* impureFactFunctionBox().reset(0.15);
  yield* impureFactFunctionBox().setInputs([{ val: 5 }], 0.15);
  yield* beginSlide("FactImpure(5) Add 5");
  yield* impureFactFunctionBox().propogateInput(0.2);
  yield* impureFactFunctionBox().propogateOutput(0.2);
  yield* beginSlide("FactImpure(5) Correct");

  yield* all(
    impureFibFunctionBox().reset(0.5),
    impureFactFunctionBox().reset(0.5),
    impureFibFunctionBox().hideCode(0.5),
    pureFunctionBox().showCode(0.5),
  );

  yield* beginSlide("Show Fib Pure");
  yield* pureFunctionBox().reset(0.15);
  yield* all(
    pureFunctionBox().setInputs([{ val: 5 }], 0.15),
    impureFactFunctionBox().setInputs([{ val: 5 }], 0.15),
  );

  yield* beginSlide("Set Inputs");

  yield* pureFunctionBox().propogateInput(0.2);
  yield* pureFunctionBox().propogateOutput(0.2);
  yield* impureFactFunctionBox().propogateInput(0.2);
  yield* impureFactFunctionBox().propogateOutput(0.2);

  yield* beginSlide("Compute Fib");
});
