
package.loaded.warpdriveCommons = nil 
local w = require("warpdriveCommons")

local data

----------- Reactor support

--  0000000001111111111222222222233333333334444444444
--  1234567890123456789012345678901234567890123456789
-- [Reactor stability %      Laser level k           ]
-- [ 99.9 99.9 99.9 99.9     56 123456 123456 123456 ]
-- [ 99.9 99.9 99.9 99.9     123.4 123.4 123.4 123.4 ]
-- [ 99.9 99.9 99.9 99.9     4.5 x 456 123456 123456 ]
-- [ 99.9 99.9 99.9 99.9 123456 123456 123456 123456 ]

local reactor
local reactorlasers = {}

local reactor_outputMode = "off"
local reactor_outputThreshold = 100
local reactor_targetStability = 50
local reactor_laserAmount = 10000

local reactor_output = 0

function reactor_read(parData)
  data = parData
end

function reactor_boot()
  if reactor ~= nil then
    w.writeLn("Booting Reactor...")
    reactor_outputMode, reactor_outputThreshold = reactor.outputMode()
    reactor_targetStability = 100.0 - reactor.instabilityTarget()
    reactor_laserAmount = reactor.stabilizerEnergy()
  end
end

function reactor_cycleOutputMode()
  if reactor_outputMode == "off" then
    reactor_outputMode = "unlimited"
  elseif reactor_outputMode == "unlimited" then
    reactor_outputMode = "above"
  elseif reactor_outputMode == "above" then
    reactor_outputMode = "at_rate"
  else
    reactor_outputMode = "off"
  end
end

function reactor_key(character, keycode)
  if character == 's' or character == 'S' then -- S
    reactor_start()
    return true
  elseif character == 'p' or character == 'P' then -- P
    reactor_stop()
    return true
  elseif character == 'l' or character == 'L' then -- L
    reactor_laser()
    return true
  elseif character == 'o' or character == 'O' then -- O
    reactor_cycleOutputMode()
    reactor_setMode()
    return true
  elseif character == 'g' or character == 'G' then -- G
    reactor_outputThreshold = reactor_outputThreshold - 1000
    reactor_setMode()
    return true
  elseif character == 't' or character == 'T' then -- T
    reactor_outputThreshold = reactor_outputThreshold + 1000
    reactor_setMode()
    return true
  elseif character == 'j' or character == 'J' then -- J
    reactor_laserAmount = reactor_laserAmount - 500
    reactor_setLaser()
    return true
  elseif character == 'u' or character == 'U' then -- U
    reactor_laserAmount = reactor_laserAmount + 500
    reactor_setLaser()
    return true
  elseif character == '-' then -- -
    reactor_targetStability = reactor_targetStability - 1
    reactor_setTargetStability()
    return true
  elseif character == '+' then -- +
    reactor_targetStability = reactor_targetStability + 1
    reactor_setTargetStability()
    return true
  elseif character == 'c' or character == 'C' then -- C
    reactor_config()
    return true
  end
  return false
end

function reactor_page()
  w.page_begin(w.data_getName() .. " - Reactor status")
  
  w.setCursorPos(1, 2)
  if reactor == nil or reactor.isInterfaced() == nil then
    w.setColorDisabled()
    w.write("Reactor not detected")
  else
    w.setColorNormal()
    w.write("Reactor stability (%)")
    local instabilities = { reactor.getInstabilities() }
    for key, instability in pairs(instabilities) do
      local y = (key - 1) % 4
      local x = (key - 1 - y) / 4
      w.setCursorPos(2 + 6 * x, 3 + y)
      local stability = math.floor((100.0 - instability) * 10) / 10.0
      if stability >= reactor_targetStability then
        w.setColorSuccess()
      else
        w.setColorWarning()
      end
      w.write(w.format_float(stability, 5))
    end
    
    w.setColorNormal()
    local energyStored, energyMax, energyUnits, _, energyOutputRate = reactor.getEnergyStatus()
    w.setCursorPos(1, 7)
    w.write("Energy   : ")
    w.write(w.format_integer(energyStored, 10) .. " / " .. w.format_integer(energyMax, 10) .. " " .. energyUnits .. " +" .. w.format_integer(reactor_output, 6) .. " " .. energyUnits .. "/t")
    w.setCursorPos(1, 8)
    w.write("Outputing: ")
    w.write(w.format_integer(energyOutputRate, 6) .. " " .. energyUnits .. "/t")
    
    w.setCursorPos(1, 9)
    w.setColorNormal()
    w.write("Activated: ")
    local isEnabled = reactor.enable()
    if isEnabled then w.setColorSuccess() else w.setColorNormal() end
    w.write(w.format_boolean(isEnabled, "YES", "no"))
  end
  
  w.setCursorPos(28, 2)
  if #reactorlasers == 0 then
    w.setColorDisabled()
    w.write("Lasers not detected")
  else
    w.setColorNormal()
    w.write("Lasers charge (k)")
    
    for _, reactorlaser in pairs(reactorlasers) do
      if reactorlaser.wrap ~= nil then
        local y = reactorlaser.side % 4
        local x = (reactorlaser.side - y) / 4
        w.setCursorPos(28 + 6 * x, 3 + y)
        local energyStored, _, _ = reactorlaser.wrap.getEnergyStatus()
        if energyStored == nil then
          energyStored = -1
        end
        local energy_k = math.floor(energyStored / 100) / 10.0
        if not reactorlaser.wrap.getAssemblyStatus() then
          w.setColorDisabled()
        elseif energyStored > 3 * reactor_laserAmount then
          w.setColorSuccess()
        else
          w.setColorWarning()
        end
        w.write(w.format_integer(energy_k, 5))
      end
    end
  end
  
  w.setCursorPos(1, 10)
  w.setColorNormal()
  w.write("  -----------------------------------------------")
  w.setCursorPos(1, 11)
  w.write("Output mode     : ")
  if reactor_outputMode == "off" then
    w.setColorDisabled()
    w.write("hold")
  elseif reactor_outputMode == "unlimited" then
    w.write("manual/unlimited")
  elseif reactor_outputMode == "above" then
    w.write("surplus above " .. reactor_outputThreshold .. " RF")
  else
    w.write("rated at " .. reactor_outputThreshold .. " RF")
  end
  w.setCursorPos( 1, 12)
  w.setColorNormal()
  w.write("Target stability: " .. reactor_targetStability .. "%")
  w.setCursorPos(30, 12)
  w.write("Laser amount: " .. w.format_integer(reactor_laserAmount))
  
  w.setCursorPos(1, 18)
  w.setColorControl()
  w.writeFullLine(" Start/stoP reactor (S/P), Use lasers (L)")
  w.writeFullLine(" Output mode (O), Configuration (C)")
  w.writeFullLine(" Target stability (+/-), Laser amount (U/J)")
  w.writeFullLine(" Output rate/threshold (T/G)")
end

function reactor_setMode()
  if reactor_outputThreshold < 1 then
    reactor_outputThreshold = 1
  elseif reactor_outputThreshold > 100000 then
    reactor_outputThreshold = 100000
  end
  if reactor ~= nil then
    reactor.outputMode(reactor_outputMode, reactor_outputThreshold)
  end
end

function reactor_setLaser()
  if reactor_laserAmount < 1 then
    reactor_laserAmount = 1
  elseif reactor_laserAmount > 100000 then
    reactor_laserAmount = 100000
  end
  if reactor ~= nil then
    reactor_laserAmount = reactor.stabilizerEnergy(reactor_laserAmount)
  end
end

function reactor_setTargetStability()
  if reactor_targetStability < 1.0 then
    reactor_targetStability = 1.0
  elseif reactor_targetStability > 100.0 then
    reactor_targetStability = 100.0
  end
  if reactor ~= nil then
    reactor_targetStability = 100.0 - reactor.instabilityTarget(100.0 - reactor_targetStability)
  end
end

function reactor_start()
  if reactor ~= nil then
    reactor_setMode()
    reactor.enable(true)
  end
end

function reactor_stop()
  if reactor ~= nil then
    reactor.enable(false)
  end
end

function reactor_laser(side)
  for key, reactorlaser in pairs(reactorlasers) do
    if (side == nil) or (reactorlaser.side == side) then
      reactorlaser.wrap.stabilize(reactor_laserAmount)
    end
  end
end

local reactor_configPageLoaded = false
local reactor_pulseStep = 0
function reactor_pulse(output)
  reactor_output = output
  if reactor == nil or reactor.isInterfaced() == nil then
    w.reboot()
  end
  local instabilities = { reactor.getInstabilities() }
  reactor_pulseStep = (reactor_pulseStep + 1) % 16
  if w.page_getCallbackDisplay() == reactor_page and (not reactor_configPageLoaded) then
    for key, instability in pairs(instabilities) do
      local y = (key - 1) % 4
      local x = (key - 1 - y) / 4
      w.setCursorPos(2 + 6 * x, 3 + y)
      local stability = math.floor((100.0 - instability) * 10) / 10
      if stability >= reactor_targetStability then
        w.setColorSuccess()
      else
        w.setColorWarning()
      end
      w.write(w.format_float(stability, 5))
    end
    
    if reactor_pulseStep % 6 == 1 then
      local energyStored, _, _, _, energyOutputRate = reactor.getEnergyStatus()
      w.setCursorPos(12, 7)
      w.setColorNormal()
      w.write(w.format_integer(energyStored, 10))
      w.setCursorPos(40, 7)
      w.write(w.format_integer(reactor_output, 6))
      w.setCursorPos(12, 8)
      w.write(w.format_integer(energyOutputRate, 6))
    end
    
    if #reactorlasers ~= 0 then
      for _, reactorlaser in pairs(reactorlasers) do
        if reactorlaser.wrap ~= nil and reactor_pulseStep == key then
          local y = reactorlaser.side % 4
          local x = (reactorlaser.side - y) / 4
          w.setCursorPos(28 + 6 * x, 3 + y)
          local energyStored, _, _ = reactorlaser.wrap.getEnergyStatus()
          if energyStored == nil then
            energyStored = -1
          end
          local energy_k = math.floor(energyStored / 100) / 10.0
          if not reactorlaser.wrap.getAssemblyStatus() then
            w.setColorDisabled()
          elseif energyStored > 3 * reactor_laserAmount then
            w.setColorSuccess()
          else
            w.setColorWarning()
          end
          w.write(w.format_integer(energy_k, 5))
        end
      end
    end
  end
end

function reactor_config()
  reactor_configPageLoaded = true
  w.page_begin(w.data_getName() .. " - Reactor configuration")
  
  w.setCursorPos(1, 2)
  if reactor == nil or reactor.isInterfaced() == nil then
    w.setColorDisabled()
    w.write("Reactor not detected")
  else
    -- reactor output rate
    w.setCursorPos(1, 6)
    w.setColorHelp()
    w.writeFullLine(" Enter a positive number.")
    
    w.setCursorPos(1, 4)
    w.setColorNormal()
    w.write("Reactor output rate (" .. w.format_integer(reactor_outputThreshold) .. " RF): ")
    reactor_outputThreshold = w.input_readInteger(reactor_outputThreshold)
    reactor_setMode()
    w.setCursorPos(1, 5)
    w.write("Reactor output rate set")
    w.setCursorPos(1, 6)
    w.writeFullLine(" ")
    
    -- laser amount
    w.setCursorPos(1, 9)
    w.setColorHelp()
    w.writeFullLine(" Enter a positive number.")
    
    w.setCursorPos(1, 7)
    w.setColorNormal()
    w.write("Laser energy level (" .. w.format_integer(reactor_laserAmount) .. "): ")
    reactor_laserAmount = w.input_readInteger(reactor_laserAmount)
    reactor_setLaser()
    w.setCursorPos(1, 8)
    w.write("Laser energy level set")
    w.setCursorPos(1, 9)
    w.writeFullLine(" ")
    
    -- target stability
    w.setCursorPos(1, 12)
    w.setColorHelp()
    w.writeFullLine(" Enter a positive number.")
    
    w.setCursorPos(1, 10)
    w.setColorNormal()
    w.write("Reactor target stability (" .. w.format_integer(reactor_targetStability) .. "%): ")
    reactor_targetStability = w.input_readInteger(reactor_targetStability)
    reactor_setTargetStability()
    w.setCursorPos(1, 11)
    w.write("Reactor target stability set")
    w.setCursorPos(1, 12)
    w.writeFullLine(" ")
  end
  reactor_configPageLoaded = false
end

function reactor_register()
  w.device_register("warpdriveEnanReactorCore",
      function(deviceType, address, wrap) reactor = wrap end,
      function() end)
  w.device_register("warpdriveEnanReactorLaser",
      function(deviceType, address, wrap) table.insert(reactorlasers, { side = wrap.side(), wrap = wrap }) end,
      function() end)
  w.event_register("reactorPulse"       , function(eventName, param) reactor_pulse(param)                        return false end )
  w.event_register("reactorDeactivation", function(                ) w.status_showWarning("Reactor deactivated") return false end )
  w.event_register("reactorActivation"  , function(                ) w.status_showWarning("Reactor activated")   return false end )
  w.data_register("reactor", reactor_read, nil, nil)
end

----------- connections status

function connections_page(isBooting)
  w.page_begin(w.data_getName() .. " - Connections")
  
  w.writeLn("")
  
  if reactor == nil or reactor.isInterfaced() == nil then
    w.setColorDisabled()
    w.writeLn("No Enantiomorphic reactor detected")
  else
    w.setColorSuccess()
    w.writeLn("Enantiomorphic reactor detected")
    if isBooting then
      reactor_boot()
    end
  end
  
  if #reactorlasers == 0 then
    w.setColorDisabled()
    w.writeLn("No reactor stabilisation laser detected")
  elseif #reactorlasers == 1 then
    w.setColorSuccess()
    w.writeLn("1 reactor stabilisation laser detected")
  else
    w.setColorSuccess()
    w.writeLn(#reactorlasers .. " reactor stabilisation lasers detected")
  end
  
  w.writeLn("")
  w.setColorNormal()
  w.writeLn("This is a keyboard controlled user interface.")
  w.write("Key controls are written like so: ")
  w.setColorControl()
  w.write("Action (key)")
  w.setColorNormal()
  w.writeLn(".")
  w.write("For example, typing ")
  w.setColorControl()
  w.write(" 1 ")
  w.setColorNormal()
  w.writeLn(" will open Reactor controls.")
end

----------- Boot sequence

w.page_setEndText(" Home (0), Reactor controls (1)")
w.page_register('0', connections_page, nil)
w.page_register('1', reactor_page, reactor_key)
reactor_register()

w.boot()
local success, message = pcall(w.run)
if not success then
  print("failed with message")
  print(message)
  w.sleep(3.0)
  print("rebooting...")
  w.reboot()
else
  w.close()
end