<script>
  import Graph from "./Graph.js"
  import { onMount } from "svelte"
  import { crossfade, scale } from "svelte/transition"

  const [send, receive] = crossfade({
    duration: 400,
    fallback: scale,
  })

  const MAX_STEPS = 13
  const pole = 0
  const hole = 1
  const pick = 2
  const dest = 3

  let circles = []
  let steps = 0
  let gameover
  let victory
  let bold
  let adjcs
  let jumps

  const restart = () => {
    adjcs = new Graph()
    jumps = new Graph()

    circles = []
    steps = 0
    gameover = false
    victory = false
    bold = false

    const numCircles = 15
    for (let i = 0; i < numCircles; i++) {
      circles[i] = pole
      adjcs.addVertex(i)
      jumps.addVertex(i)

      if (i > 0) {
        adjcs.addEdge(i, i - 1)
      }
    }

    circles[0] = hole

    adjcs.addEdge(0, 8)
    adjcs.addEdge(1, 7)
    adjcs.addEdge(1, 8)
    adjcs.addEdge(2, 6)
    adjcs.addEdge(2, 7)
    adjcs.addEdge(3, 5)
    adjcs.addEdge(3, 6)
    adjcs.addEdge(5, 11)
    adjcs.addEdge(6, 10)
    adjcs.addEdge(6, 11)
    adjcs.addEdge(7, 9)
    adjcs.addEdge(7, 10)
    adjcs.addEdge(9, 13)
    adjcs.addEdge(10, 12)
    adjcs.addEdge(10, 13)
    adjcs.addEdge(12, 14)

    jumps.addEdge(0, 2)
    jumps.addEdge(0, 9)
    jumps.addEdge(1, 3)
    jumps.addEdge(1, 10)
    jumps.addEdge(2, 4)
    jumps.addEdge(2, 9)
    jumps.addEdge(2, 11)
    jumps.addEdge(3, 10)
    jumps.addEdge(4, 11)
    jumps.addEdge(5, 7)
    jumps.addEdge(5, 12)
    jumps.addEdge(6, 8)
    jumps.addEdge(6, 13)
    jumps.addEdge(7, 12)
    jumps.addEdge(8, 13)
    jumps.addEdge(9, 11)
    jumps.addEdge(9, 14)
    jumps.addEdge(11, 14)
  }

  const commonBetween = (source, destination) => {
    const common = adjcs.adjacencyList[source].filter(common =>
      adjcs.adjacencyList[destination].includes(common)
    )

    if (common.length != 1) {
      throw new Error(
        `Jump from ${source} to ${destination} cannot be performed`
      )
    }

    return common
  }

  const clearDests = () =>
    circles.forEach(
      (_, i) => (circles[i] = circles[i] == dest ? hole : circles[i])
    )

  const getDests = (circles, curr) => {
    let dests = []

    jumps.adjacencyList[curr].forEach(jump => {
      const jumpedOver = commonBetween(curr, jump)

      if (circles[jump] == hole && circles[jumpedOver] == pole) {
        dests.push(jump)
      }
    })

    return dests
  }

  const getHints = circles => {
    let hints = []
    circles.forEach((circle, i) => {
      if (circle == pole) {
        const dests = getDests(circles, i)
        if (dests.length > 0) {
          hints.push(i)
        }
      }
    })

    return hints
  }

  const jumpReverse = (circles, source, destination) => {
    const jumpedOver = commonBetween(source, destination)
    circles[source] = pole
    circles[jumpedOver] = pole
    circles[destination] = hole
  }

  const jumpOver = (circles, source, destination) => {
    const jumpedOver = commonBetween(source, destination)
    circles[source] = hole
    circles[jumpedOver] = hole
    circles[destination] = pole
  }

  const change = curr => {
    const prev = circles.indexOf(pick)

    if (circles[curr] == pole && prev == -1) {
      let dests = getDests(circles, curr)
      dests.forEach(d => (circles[d] = dest))

      if (dests.length > 0) {
        circles[curr] = pick
      }

      return
    }

    if (circles[curr] == dest) {
      jumpOver(circles, prev, curr)
      clearDests()
      steps++
      console.log(steps)

      if (steps == MAX_STEPS) {
        bold = true
        victory = true

        return
      }

      const hints = getHints(circles)
      if (hints.length == 0) {
        gameover = true
        bold = true
      }

      return
    }

    if (circles[curr] == pick) {
      circles[curr] = pole
      clearDests()
    }
  }

  class Tree {
    constructor(solution, depth) {
      this.depth = depth
      this.solution = solution
      this.children = []
    }
  }

  onMount(() => {
    restart()

    let depth = 0
    let solutions = []
    let shadow = [...circles]
    const MAX_DEPTH = 13
    const moves = new Tree("", depth)

    const recurse = move => {
      const hints = getHints(shadow)
      if (hints.length == 0) {
        return
      }

      hints.forEach(hint => {
        getDests(shadow, hint).forEach(destination => {
          const solution = `${move.solution}  ${hint}>${destination}`
          const child = new Tree(solution, depth)
          const lastIndex = move.children.push(child) - 1
          jumpOver(shadow, hint, destination)
          depth = depth + 1

          if (depth == MAX_DEPTH) {
            solutions.push(solution)
          }
          recurse(move.children[lastIndex])
          depth = depth - 1
          jumpReverse(shadow, hint, destination)
        })
      })
    }

    // recurse(moves)
    // console.log(solutions)
  })
</script>

<style>
  .div0 {
    grid-area: 5 / 1 / 6 / 2;
  }
  .div1 {
    grid-area: 5 / 3 / 6 / 4;
  }
  .div2 {
    grid-area: 5 / 5 / 6 / 6;
  }
  .div3 {
    grid-area: 5 / 7 / 6 / 8;
  }
  .div4 {
    grid-area: 5 / 9 / 6 / 10;
  }
  .div5 {
    grid-area: 4 / 8 / 5 / 9;
  }
  .div6 {
    grid-area: 4 / 6 / 5 / 7;
  }
  .div7 {
    grid-area: 4 / 4 / 5 / 5;
  }
  .div8 {
    grid-area: 4 / 2 / 5 / 3;
  }
  .div9 {
    grid-area: 3 / 3 / 4 / 4;
  }
  .div10 {
    grid-area: 3 / 5 / 4 / 6;
  }
  .div11 {
    grid-area: 3 / 7 / 4 / 8;
  }
  .div12 {
    grid-area: 2 / 6 / 3 / 7;
  }
  .div13 {
    grid-area: 2 / 4 / 3 / 5;
  }
  .div14 {
    grid-area: 1 / 5 / 2 / 6;
  }

  .triangle {
    width: 600px;
    height: 520px;
    display: grid;
    grid-template-columns: repeat(9, 1fr);
    grid-template-rows: repeat(5, 1fr);
    grid-column-gap: 0px;
    grid-row-gap: 0px;
  }

  .circle {
    width: 50px;
    height: 50px;
    clip-path: circle();
    text-align: center;
    color: white;
  }

  .pole {
    cursor: pointer;
    background-color: #8fbcbb;
  }

  .hole {
    cursor: default;
    background-color: #d8dee9;
  }

  .pick {
    cursor: pointer;
    background-color: #a3be8c;
  }

  .dest {
    cursor: pointer;
    background-color: #81a1c1;
  }

  :global(body) {
    background-color: #eceff4;
    margin: 0;
    padding: 15px;
  }

  span {
    display: none;
  }

  .restart {
    display: inline;
    cursor: pointer;
  }

  .gameover {
    display: inline;
  }

  .victory {
    display: inline;
  }

  .bold {
    font-weight: bold;
  }
</style>

<main>
  <div>
    <span class="restart" class:bold on:click={restart}>RESTART GAME</span>
    <span data-cy="game-over" class:gameover> | GAME OVER</span>
    <span data-cy="victory" class:victory> | VICTORY</span>
  </div>
  <div class="triangle">
    {#each circles as _, i}
      {#if circles[i] == hole}
        <div
          in:receive={{ key: i }}
          out:send={{ key: i }}
          class="circle div{i} hole"
          on:click={() => change(i)}>
          {i}
        </div>
      {/if}
      {#if circles[i] == pole}
        <div
          in:receive={{ key: i }}
          out:send={{ key: i }}
          class="circle div{i} pole"
          on:click={() => change(i)}>
          {i}
        </div>
      {/if}
      {#if circles[i] == pick}
        <div
          in:receive={{ key: i }}
          out:send={{ key: i }}
          class="circle div{i} pick"
          on:click={() => change(i)}>
          {i}
        </div>
      {/if}
      {#if circles[i] == dest}
        <div
          in:receive={{ key: i }}
          out:send={{ key: i }}
          class="circle div{i} dest"
          on:click={() => change(i)}>
          {i}
        </div>
      {/if}
    {/each}
  </div>
</main>
