using Blink
using Colors

# === utilities ===

append_to_head!(w, type, content) = @js w begin
  @var element = document.createElement($type)
  element.appendChild(document.createTextNode($content))
  document.head.appendChild(element)
end

style!(w, stylesheet) = append_to_head!(w, "style", stylesheet)

script!(w, code) = append_to_head!(w, "script", code)

function add_element!(vec)
  # add element
  full_vec = [0; vec; fill(0, 26)]
  n = @js win elements.push(@new CGA3($full_vec))
  
  # generate palette. this is Gadfly's `default_discrete_colors` palette,
  # available under the MIT license
  palette = distinguishable_colors(
    n,
    [LCHab(70, 60, 240)],
    transform = c -> deuteranopic(c, 0.5),
    lchoices = Float64[65, 70, 75, 80],
    cchoices = Float64[0, 50, 60, 70],
    hchoices = range(0, stop=330, length=24)
  )
  palette_packed = [RGB24(c).color for c in palette]
  @js win palette = $palette_packed
end

# === build page ===

# create window and open developer console
win = Window()
opentools(win)

# set stylesheet
style!(win, """
  body {
    background-color: #ffe0f0;
  }

  /* needed to keep Ganja canvas from blowing up */
  canvas {
    min-width: 600px;
    max-width: 600px;
    min-height: 600px;
    max-height: 600px;
  }
""")

# load Ganja.js
loadjs!(win, "https://unpkg.com/ganja.js")

# create global functions and variables
script!(win, """
  // create algebra
  var CGA3 = Algebra(4, 1);
  
  // initialize element list and palette
  var elements = [];
  var palette = [];
  
  // declare visualization handle
  var graph;
  
  // create scene function
  function scene() {
    commands = [];
    for (let n = 0; n < elements.length; ++n) {
      commands.push(palette[n], elements[n]);
    }
    return commands;
  }
  
  function flip() {
    let last = elements.length - 1;
    for (let n = 0; n < last; ++n) {
      // reflect
      elements[n] = CGA3.Mul(CGA3.Mul(elements[last], elements[n]), elements[last]);
      
      // de-noise
      for (let k = 6; k < elements[n].length; ++k) {
        elements[n][k] = 0;
      }
    }
    requestAnimationFrame(graph.update.bind(graph, scene));
  }
""")

# set up controls
body!(win, """
  <p><button id="flip-button" onclick="flip()">Flip</button></p>
""", async = false)

# === set up visualization ===

# list elements. in the default view, e4 + e5 is the point at infinity
elements = sqrt(0.5) * BigFloat[
  1  1 -1 -1  0;
  1 -1  1 -1  0;
  1 -1 -1  1  0;
  0  0  0  0 -sqrt(6);
  1  1  1  1  2
]

# load elements
for vec in eachcol(elements)
  add_element!(vec)
end

# initialize visualization
@js win begin
  graph = CGA3.graph(
    scene,
    Dict(
      "conformal" => true,
      "gl" => true,
      "grid" => true
    )
  )
  document.body.appendChild(graph)
end