local instructions = {}
local index = 1
local scroll = 0
local saved = true

local function pause()
  print("Press a key or click to continue.")
  while true do
    name = os.pullEvent()
    if name == "key" or name == "mouse_click" then
      break
    end
  end
end

local function hideCursor()
  term.setCursorPos(24, index + 1 - scroll)
  term.write(" ")
end

local function showCursor()
  term.setCursorPos(24, index + 1 - scroll)
  term.write(">")
end

local function renderControls()
  term.setCursorPos(1, 1)
  print("+---------+-------+---+---------------+")
  print("|for i=1,x|while 1|end|               |")
  print("+---------+-------+---+               |")
  print("| UP      |PLACE UP   |               |")
  print("| DOWN    |PLACE DOWN |               |")
  print("| FORWARD |PLACE FRONT|               |")
  print("| BACK    |DIG UP     |               |")
  print("| LEFT    |DIG DOWN   |               |")
  print("| RIGHT   |DIG FRONT  |               |")
  print("+---------+-----------+               |")
  print("|[^][DEL][CLEAR][EXIT]|               |")
  print("|[v][COMP][LOAD][SAVE]|               |")
  term.write("+---------------------+---------------+")
end

local function renderInstructions()
  length = table.getn(instructions)
  for i = 1, 11 do
    term.setCursorPos(26, i + 1)
    term.write("             ")
    if i + scroll <= length then
      term.setCursorPos(26, i + 1)
      term.write(instructions[i + scroll])
    end
  end
  term.setCursorPos(29, 1)
  term.write("-----")
  term.setCursorPos(29, 13)
  term.write("-----")
  if scroll > 0 then
    term.setCursorPos(29, 1)
    term.write("  ^  ")
  end
  if length - 11 > scroll then
    term.setCursorPos(29, 13)
    term.write("  v  ")
  end
end

local function renderAll()
  renderControls()
  renderInstructions()
  showCursor()
end

local function update()
  len = table.getn(instructions)
  if len + 1 < index then
    index = len + 1
  end
  if index < 1 then
    index = 1
  end
  if index - 1 < scroll then
    scroll = index - 1
  end
  if index - scroll > 11 then
    scroll = index - 11
  end
end

local function insert(inst)
  length = table.getn(instructions)
  if index == length then
    table.insert(instructions, inst)
  else
    table.insert(instructions, index, inst)
  end
  hideCursor()
  index = index + 1
  update()
  renderInstructions()
  showCursor()
end

local function del()
  length = table.getn(instructions)
  if index > length then
    table.remove(instructions, length)
    index = length + 1
  else
    table.remove(instructions, index)
  end
  hideCursor()
  update()
  renderInstructions()
  showCursor()
end

local function getFileName()
  term.setCursorPos(1, 3)
  print("                                       ")
  print("=======================================")
  print("   Type name of file and press ENTER   ")
  print("---------------------------------------")
  print("                                       ")
  print("                                       ")
  print("           Type a blank line to cancel ")
  print("=======================================")
  print("                                       ")
  term.setCursorPos(1, 7)
  return read()
end

local function confirm(message)
  term.clear()
  term.setCursorPos(1, 1)
  print(message)
  print("")
  print("+-----------------+  +----------------+")
  print("|                 |  |                |")
  print("|                 |  |                |")
  print("|                 |  |                |")
  print("|                 |  |                |")
  print("|       YES       |  |       NO       |")
  print("|                 |  |                |")
  print("|                 |  |                |")
  print("|                 |  |                |")
  print("|                 |  |                |")
  term.write("+-----------------+  +----------------+")
  while true do
    name, button, x, y = os.pullEvent("mouse_click")
    if y > 2 then
      if x < 20 then
        term.clear()
        return true
      elseif x > 21 then
        term.clear()
        return false
      end
    end
  end
end

local function b_while()
  insert("while true")
  saved = false
end

local function getFor()
  term.setCursorPos(1, 3)
  print("                                       ")
  print("=======================================")
  print("    Type loop limit and press ENTER    ")
  print("---------------------------------------")
  print("                                       ")
  print("                                       ")
  print(" 1 < X < 100000       Type 0 to cancel ")
  print("=======================================")
  print("                                       ")
  term.setCursorPos(1, 7)
  num = tonumber(read())
  if num == nil or num == 0 then
    return nil
  end
  if num > 99999 then
    num = 99999
  end
  if num < 2 then
    num = 2
  end
  return num
end

local function b_for()
  num = getFor()
  if not (num == nil) then
    insert("for i=1," .. num)
  end
  term.clear()
  renderAll()
  saved = false
end

local function b_end()
  insert("end")
end

local function b_mUp()
  insert("MOVE_UP")
  saved = false
end

local function b_mDown()
  insert("MOVE_DOWN")
  saved = false
end

local function b_mForward()
  insert("MOVE_FORWARD")
  saved = false
end

local function b_mBack()
  insert("MOVE_BACK")
  saved = false
end

local function b_mLeft()
  insert("TURN_LEFT")
  saved = false
end

local function b_mRight()
  insert("TURN_RIGHT")
  saved = false
end

local function b_pUp()
  insert("PLACE_UP")
  saved = false
end

local function b_pDown()
  insert("PLACE_DOWN")
  saved = false
end

local function b_pFront()
  insert("PLACE_FRONT")
  saved = false
end

local function b_dUp()
  insert("DIG_UP")
  saved = false
end

local function b_dDown()
  insert("DIG_DOWN")
  saved = false
end

local function b_dFront()
  insert("DIG_FRONT")
  saved = false
end

local function b_cursorUp()
  hideCursor()
  index = index - 1
  update()
  renderInstructions()
  showCursor()
end

local function b_cursorDown()
  hideCursor()
  index = index + 1
  update()
  renderInstructions()
  showCursor()
end

local function b_del()
  del()
  saved = false
end

local function b_clear()
  if confirm("Clear the instruction list?") then
    instructions = {}
    index = 1
    scroll = 0
    saved = true
  end
  renderAll()
end

local function b_compile()
  name = getFileName()
  if name == "" then
    renderAll()
    return
  end
  term.clear()
  term.setCursorPos(1, 1)
  print("Compiling to " .. name)
  file = fs.open(name, "w")
  movement = {movement = false, up = false, down = false, forward = false, back = false, left = false, right = false}
  place = {up = false, down = false, front = false}
  print("Scanning for movement...")
  for i = table.getn(instructions), 1, -1 do
    sub1 = string.sub(instructions[i], 1, 4)
    sub2 = nil
    if sub1 == "MOVE" then
      movement.movement = true
      sub2 = string.sub(instructions[i], 6, 6)
      if sub2 == "U" then
        movement.up = true
      elseif sub2 == "D" then
        movement.down = true
      elseif sub2 == "F" then
        movement.forward = true
      elseif sub2 == "B" then
        movement.back = true
      end
    end
    if sub1 == "TURN" then
      movement.movement = true
      sub2 = string.sub(instructions[i], 6, 6)
      if sub2 == "L" then
        movement.left = true
      elseif sub2 == "R" then
        movement.right = true
      end
    end
  end
  print("Scanning for block placing...")
  for i = table.getn(instructions), 1, -1 do
    sub1 = string.sub(instructions[i], 1, 5)
    sub2 = nil
    if sub1 == "PLACE" then
      sub2 = string.sub(instructions[i], 7, 7)
      if sub2 == "U" then
        place.up = true
      elseif sub2 == "D" then
        place.down = true
      elseif sub2 == "F" then
        place.front = true
      end
    end
  end
  print("Generating required functions...")
  if movement.movement then
    file.writeLine("local function tryMove()")
    file.writeLine("  while turtle.getFuelLevel() == 0 do")
    file.writeLine("    for i = 1, 16 do")
    file.writeLine("      turtle.select(i)")
    file.writeLine("      if turtle.refuel(1) then")
    file.writeLine("        break")
    file.writeLine("      end")
    file.writeLine("    end")
    file.writeLine("  end")
    file.writeLine("end")
    file.writeLine("")
    if movement.up then
      file.writeLine("local function tryMoveUp()")
      file.writeLine("  tryMove()")
      file.writeLine("  turtle.up()")
      file.writeLine("end")
      file.writeLine("")
    end
    if movement.down then
      file.writeLine("local function tryMoveDown()")
      file.writeLine("  tryMove()")
      file.writeLine("  turtle.down()")
      file.writeLine("end")
      file.writeLine("")
    end
    if movement.forward then
      file.writeLine("local function tryMoveForward()")
      file.writeLine("  tryMove()")
      file.writeLine("  turtle.forward()")
      file.writeLine("end")
      file.writeLine("")
    end
    if movement.back then
      file.writeLine("local function tryMoveBack()")
      file.writeLine("  tryMove()")
      file.writeLine("  turtle.back()")
      file.writeLine("end")
      file.writeLine("")
    end
    if movement.left then
      file.writeLine("local function tryMoveLeft()")
      file.writeLine("  turtle.turnLeft()")
      file.writeLine("end")
      file.writeLine("")
    end
    if movement.right then
      file.writeLine("local function tryMoveRight()")
      file.writeLine("  turtle.turnRight()")
      file.writeLine("end")
      file.writeLine("")
    end
  end
  if place.up then
    file.writeLine("local function tryPlaceUp()")
    file.writeLine("  while not turtle.placeUp() do")
    file.writeLine("    local i = turtle.getSelectedSlot()")
    file.writeLine("    i = i + 1")
    file.writeLine("    if i > 16 then")
    file.writeLine("      i = 1")
    file.writeLine("    end")
    file.writeLine("    turtle.select(i)")
    file.writeLine("  end")
    file.writeLine("end")
    file.writeLine("")
  end
  if place.down then
    file.writeLine("local function tryPlaceDown()")
    file.writeLine("  while not turtle.placeDown() do")
    file.writeLine("    local i = turtle.getSelectedSlot()")
    file.writeLine("    i = i + 1")
    file.writeLine("    if i > 16 then")
    file.writeLine("      i = 1")
    file.writeLine("    end")
    file.writeLine("    turtle.select(i)")
    file.writeLine("  end")
    file.writeLine("end")
    file.writeLine("")
  end
  if place.front then
    file.writeLine("local function tryPlace()")
    file.writeLine("  while not turtle.place() do")
    file.writeLine("    local i = turtle.getSelectedSlot()")
    file.writeLine("    i = i + 1")
    file.writeLine("    if i > 16 then")
    file.writeLine("      i = 1")
    file.writeLine("    end")
    file.writeLine("    turtle.select(i)")
    file.writeLine("  end")
    file.writeLine("end")
    file.writeLine("")
  end
  print("Parsing instruction list...")
  indent = 0
  length = table.getn(instructions)
  mapping = {MOVE_UP = "tryMoveUp()", MOVE_DOWN = "tryMoveDown()", MOVE_FORWARD = "tryMoveForward()", MOVE_BACK = "tryMoveBack()", TURN_LEFT = "tryMoveLeft()", TURN_RIGHT = "tryMoveRight()", PLACE_UP = "tryPlaceUp()", PLACE_DOWN = "tryPlaceDown()", PLACE_FRONT = "tryPlace()", DIG_UP = "turtle.digUp()", DIG_DOWN = "turtle.digDown()", DIG_FRONT = "turtle.dig()"}
  varnames = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
  mapped = nil
  for i = 1, length do
    mapped = mapping[instructions[i]]
    if mapped == nil then
      if instructions[i] == "while true" then
        file.writeLine(string.format("%" .. indent .. "s%s", "", "while true do"))
        indent = indent + 2
      elseif instructions[i] == "end" then
        indent = indent - 2
        if indent < 0 then
          break
        end
        file.writeLine(string.format("%" .. indent .. "s%s", "", "end"))
      else
        indent = indent + 2
        file.writeLine(string.format("%" .. indent .. "s%s", "", "for " .. varnames[indent / 2] .. " = 1, " .. string.sub(instructions[i], 9) .. " do"))
      end
    else
      file.writeLine(string.format("%" .. indent .. "s%s", "", mapped))
    end
  end
  file.close()
  if indent > 0 then
    print("Error: Unclosed loop.")
  elseif indent < 0 then
    print("Error: end without loop.")
  else
    print("Finished compiling without errors.")
  end
  pause()
  renderAll()
end

local function b_load()
  name = getFileName()
  if name == "" then
    renderAll()
    return
  end
  term.clear()
  term.setCursorPos(1, 1)
  print("Loading " .. name .. "...")
  if not fs.exists(name) then
    print("Error: File " .. name .. " does not exist!")
    pause()
    renderAll()
    return
  end
  file = fs.open(name, "r")
  instructions = {}
  line = file.readLine()
  i = 0
  
  while not (line == nil) do
    i = i + 1
    instructions[i] = line
    line = file.readLine()
  end
  file.close()
  print("Loaded.")
  pause()
  renderAll()
  saved = true
end

local function b_save()
  name = getFileName()
  if name == "" then
    renderAll()
    return
  end
  term.clear()
  term.setCursorPos(1, 1)
  print("Saving to " .. name .. "...")
  file = fs.open(name, "w")
  length = table.getn(instructions)
  for i = 1, length do
    file.writeLine(instructions[i])
  end
  file.close()
  print("File saved.")
  pause()
  renderAll()
  saved = true
end

local function b_exit()
  if confirm("Are you sure you want to exit?") then
    if not saved and confirm("Save instruction list before quitting?") then
      b_save()
    end
    return false
  end
  renderAll()
  return true
end

local function handleEvents()
  name, button, x, y = os.pullEvent("mouse_click")
  if y == 2 then
    if x >= 2 and x <= 10 then
      b_for()
    elseif x >= 12 and x <= 18 then
      b_while()
    elseif x >= 20 and x <= 22 then
      b_end()
    end
  elseif y == 4 then
    if x >= 2 and x <= 10 then
      b_mUp()
    elseif x >= 12 and x <= 22 then
      b_pUp()
    end
  elseif y == 5 then
    if x >= 2 and x <= 10 then
      b_mDown()
    elseif x >= 12 and x <= 22 then
      b_pDown()
    end
  elseif y == 6 then
    if x >= 2 and x <= 10 then
      b_mForward()
    elseif x >= 12 and x <= 22 then
      b_pFront()
    end
  elseif y == 7 then
    if x >= 2 and x <= 10 then
      b_mBack()
    elseif x >= 12 and x <= 22 then
      b_dUp()
    end
  elseif y == 8 then
    if x >= 2 and x <= 10 then
      b_mLeft()
    elseif x >= 12 and x <= 22 then
      b_dDown()
    end
  elseif y == 9 then
    if x >= 2 and x <= 10 then
      b_mRight()
    elseif x >= 12 and x <= 22 then
      b_dFront()
    end
  elseif y == 11 then
    if x >= 2 and x <= 4 then
      b_cursorUp()
    elseif x >= 5 and x <= 9 then
      b_del()
    elseif x >= 10 and x <= 16 then
      b_clear()
    elseif x >= 17 and x <= 22 then
      return b_exit()
    end
  elseif y == 12 then
    if x >= 2 and x <= 4 then
      b_cursorDown()
    elseif x >= 5 and x <= 10 then
      b_compile()
    elseif x >= 11 and x <= 16 then
      b_load()
    elseif x >= 17 and x <= 22 then
      b_save()
    end
  end
  return true
end

term.setCursorBlink(false)
renderAll()
while handleEvents() do
end
term.clear()
term.setCursorPos(1, 1)