if not term.isColor() then
  print("Advanced computer required")
  exit()
end
print("loading...")

-- set alarm side if you need this
Alarm = "top"

Style = {
 CDeflt = colors.white,
 BGDeflt = colors.blue,
 CTitle = colors.black,
 BGTitle = colors.cyan,
 CWarn = colors.white,
 BGWarn = colors.red
}

function SetColorDeflt()
 term.setBackgroundColor(Style.BGDeflt)
 term.setTextColor(Style.CDeflt)
end

function SetColorTitle()
 term.setBackgroundColor(Style.BGTitle)
 term.setTextColor(Style.CTitle)
end

function SetColorWarn()
 term.setBackgroundColor(Style.BGWarn)
 term.setTextColor(Style.CWarn)
end

function Clear()
 term.clear()
 term.setCursorPos(1,1)
end

function Show(Text)
 term.write(Text)
 local xt,yt = term.getCursorPos()
 term.setCursorPos(1, yt+1)
end

function ShowTitle(Text)
 SetColorTitle()
 term.setCursorPos(12, 1)
 Show(Text)
 SetColorDeflt()
end

function ShowMenu(Text)
 term.write(Text)
 local xt, yt = term.getCursorPos()
 for i = xt, 51 do
  term.write(" ")
 end
 term.setCursorPos(1, yt+1)
end

function ShowWarning(Text)
  SetColorWarn()
  term.setCursorPos(10, 19)
  term.write(" "..Text.." ")
  SetColorDeflt()
end

function SaveData()
 local file = fs.open("shipdata.txt", "w")
 file.writeLine(textutils.serialize(SData))
 file.close()
end

function ReadData()
 local file = fs.open("shipdata.txt", "r")
 SData = textutils.unserialize(file.readAll())
 file.close()
end

function Explode(d, p)
 local t, ll
 t = {}
 ll = 0
 if(#p == 1) then return {p} end
 while true do
  l = string.find(p ,d, ll, true)
  if l ~= nil then 
   table.insert(t, string.sub(p, ll, l-1))
   ll = l+1
  else
   table.insert(t, string.sub(p, ll))
   break
  end
 end
 return t
end

function ShowDirection()
 if SData.Direction == 1 then
  Show(" Direction        = Up")
 elseif SData.Direction == 2 then
  Show(" Direction        = Down")
 elseif SData.Direction == 0 then
  Show(" Direction        = Front")
 elseif SData.Direction == 180 then
  Show(" Direction        = Back")
 elseif SData.Direction == 90 then
  Show(" Direction        = Left")
 elseif SData.Direction == 255 then
  Show(" Direction        = Right")
 end
end

function CalcRealDistance()
 if IsInHyper then
  RealDistance = SData.Distance * 100
  MinimumDistance = 1
  JumpCost = (1000 * Weight) + (1000 * SData.Distance)
 else
  if SData.Direction == 1 or SData.Direction == 2 then
   MinimumDistance = GUp + GDown
   RealDistance = SData.Distance + MinimumDistance
  elseif SData.Direction == 0 or SData.Direction == 180 then
   MinimumDistance = GFront + GBack
   RealDistance = SData.Distance + MinimumDistance
  elseif SData.Direction == 90 or SData.Direction == 255 then
   MinimumDistance = GLeft + GRight
   RealDistance = SData.Distance + MinimumDistance
  end
  MinimumDistance = MinimumDistance + 1
  JumpCost = (10 * Weight) + (100 * SData.Distance)
 end
end

function CalcNewCoords(cx, cy, cz)
 local res = {x=cx, y=cy, z=cz}
 if SData.Direction == 1 then
  res.y = res.y + RealDistance
 elseif SData.Direction == 2 then
  res.y = res.y - RealDistance
 end
 local dx, dy, dz = warp.getOrientation()
 if dx ~= 0 then
  if SData.Direction == 0 then
   res.x = res.x + (RealDistance * dx)
  elseif SData.Direction == 180 then
   res.x = res.x - (RealDistance * dx)
  elseif SData.Direction == 90 then
   res.z = res.z + (RealDistance * dx)
  elseif SData.Direction == 255 then
   res.z = res.z - (RealDistance * dx)
  end
 else
  if SData.Direction == 0 then
   res.z = res.z + (RealDistance * dz)
  elseif SData.Direction == 180 then
   res.z = res.z - (RealDistance * dz)
  elseif SData.Direction == 90 then
   res.x = res.x + (RealDistance * dz)
  elseif SData.Direction == 255 then
   res.x = res.x - (RealDistance * dz)
  end
 end
 return res
end

function ShowInfo()
 ShowTitle(Title)
 Show("Core:")
 Show(" x, y, z          = "..X..", "..Y..", "..Z)
 local energy, energyMax = warp.getEnergyLevel()
 Show(" Energy           = " .. math.floor(100 * energy / energyMax) .. " % (" .. energy .. "EU)")
 local playersString, playersArray = warp.getAttachedPlayers()
 Show(" Attached players = " .. playersString)
 Show("Dimensions:")
 Show(" Front, Right, Up = "..GFront..", "..GRight..", "..GUp)
 Show(" Back, Left, Down = "..GBack..", "..GLeft..", "..GDown)
 Show(" Size             = "..Weight.." blocks")
 Show("Warp data:")
 ShowDirection()
 local dest = CalcNewCoords(X, Y, Z)
 Show(" Distance         = "..RealDistance.." ("..JumpCost.."EU, "..math.floor(energy/JumpCost).." jumps)")
 Show(" Dest.coordinates = "..dest.x..", "..dest.y..", "..dest.z)
 if SData.Summon then
  Show(" Summon after     = Yes")
 else
  Show(" Summon after     = No")
 end
end

function Confirm()
 ShowWarning("Are you sure? (y/n)")
 local event, keycode = os.pullEvent("key")
 if keycode == 21 then
  return true
 else
  return false
 end
end

function Warp()
 rs.setOutput(Alarm, false)
 sleep(1)
 warp.direction(SData.Direction)
 if IsInHyper then
  warp.mode(2)
 else
  warp.mode(1)
 end
 warp.jump()
end

function SetDistance()
 Clear()
 ShowTitle("<====  Set distance  ====>")
 SData.Distance = 0
 CalcRealDistance()
 MaximumDistance = MinimumDistance + 127
 if IsInHyper then
  term.write("Distance * 100 (min "..MinimumDistance..", max "..MaximumDistance.."): ")
 else
  term.write("Distance (min "..MinimumDistance..", max "..MaximumDistance.."): ")
 end
 sleep(0.3)
 SData.Distance = tonumber(read())
 if SData.Distance == nil then SData.Distance = 1 end
 if SData.Distance < MinimumDistance or SData.Distance > MaximumDistance then
  SData.Distance = 1
  ShowWarning("Wrong distance. Try again.")
  os.pullEvent("key")
  CalcRealDistance()
 else
  if not IsInHyper then
   SData.Distance = SData.Distance - RealDistance
  end
  warp.distance(SData.Distance)
  CalcRealDistance()
 end
end

function SetDirection()
 local drun = true
 while(drun) do
  Clear()
  ShowTitle("<==== Set direction ====>")
  ShowDirection()
  term.setCursorPos(1, 16)
  SetColorTitle()
  ShowMenu("Use directional keys")
  ShowMenu("W/S keys for Up/Down")
  ShowMenu("Enter - confirm")
  SetColorDeflt()
  local event, keycode = os.pullEvent("key")
  if keycode == 200 then
   SData.Direction = 0
  elseif keycode == 17 then
   SData.Direction = 1
  elseif keycode == 203 then
   SData.Direction = 90
  elseif keycode == 205 then
   SData.Direction = 255
  elseif keycode == 208 then
   SData.Direction = 180
  elseif keycode == 31 then
   SData.Direction = 2
  elseif keycode == 28 then
   drun = false
  end
 end
end

function SetDimensions()
 Clear()
 sleep(0.3)
 ShowTitle("<==== Set dimensions ====>")
 term.write(" Front ("..GFront..") : ")
 GFront = tonumber(read())
 term.write(" Right ("..GRight..") : ")
 GRight = tonumber(read())
 term.write(" Up    ("..GUp..") : ")
 GUp = tonumber(read())
 term.write(" Back  ("..GBack..") : ")
 GBack = tonumber(read())
 term.write(" Left  ("..GLeft..") : ")
 GLeft = tonumber(read())
 term.write(" Down  ("..GDown..") : ")
 GDown = tonumber(read())
 term.write("Setting dimensions...")
 warp.dim_positive(GFront, GRight, GUp)
 warp.dim_negative(GBack, GLeft, GDown)
 Weight = warp.getShipSize()
end

function Summon()
 Clear()
 ShowTitle("<==== Summon players ====>")
 local playersString, playersArray = warp.getAttachedPlayers()
 for i = 1, #playersArray do
  Show(i..". "..playersArray[i])
 end
 SetColorTitle()
 ShowMenu("Enter player number")
 ShowMenu("or press enter to summon everyone")
 SetColorDeflt()
 sleep(0.3)
 term.write(":")
 local input = read()
 if input == "" then
  warp.summonAll()
 else
  input = tonumber(input)
  warp.summon(input - 1)
 end
end

function JumpToBeacon()
 Clear()
 ShowTitle("<==== Jump to beacon ====>")
 sleep(0.3)
 term.write("Enter beacon frequency: ")
 local freq = tostring(read())
 rs.setOutput(Alarm, true)
 if Confirm() then
  rs.setOutput(Alarm, false)
  warp.mode(4)
  warp.beaconFrequency(freq)
  warp.jump()
 end
 rs.setOutput(Alarm, false)
end

function JumpToGate()
 Clear()
 ShowTitle("<==== Jump to JumpGate ====>")
 sleep(0.3)
 term.write("Enter jumpgate name: ")
 local name = tostring(read())
 rs.setOutput(Alarm, true)
 if Confirm() then
  rs.setOutput(Alarm, false)
  warp.mode(6)
  warp.targetJumpgate(name)
  warp.jump()
 end
 rs.setOutput(Alarm, false)
end

function SetShipName()
 Clear()
 ShowTitle("<==== Set ship name ====>")
 sleep(0.3)
 term.write("Enter ship name: ")
 SData.Shipname = tostring(read())
 os.setComputerLabel(SData.Shipname)
 warp.coreFrequency(SData.Shipname)
 SaveData()
 os.reboot()
end

if fs.exists("shipdata.txt") then
 ReadData()
else
 SData = {
  Summon = false,
  Distance = 1,
  Direction = 0,
  Shipname = ""
 }
end

SetColorDeflt()

Side = { "bottom", "top", "back", "left", "right" }
for i = 1,5 do
 if peripheral.getType(Side[i]) == "warpcore" then
  warp = peripheral.wrap(Side[i])
  break
  else
    warp = nil
  end
end

if warp == nil then
 ShowWarning("No warpcore controller detected")
 os.pullEvent("key")
 os.reboot()
end

if SData.Shipname == "" then
 SetShipName()
end

Title = "<JumpShip \""..SData.Shipname.."\">"

if SData.Summon then
 warp.summonAll()
end

GFront, GRight, GUp = warp.dim_positive()
GBack, GLeft, GDown = warp.dim_negative()
IsInHyper = warp.isInHyperspace()
repeat
 pos = warp.pos()
 sleep(0.3)
until pos ~= nil
X, Y, Z = warp.pos()
repeat
 isAttached = warp.isAttached()
 sleep(0.3)
until isAttached ~= false

repeat
 Weight = warp.getShipSize()
 sleep(0.3)
until Weight ~= nil 

CalcRealDistance()

warp.mode(1)

mainloop = true
while(mainloop) do
 Clear()
 ShowInfo()
 term.setCursorPos(1, 15)
 SetColorTitle()
 ShowMenu("D - Dimensions, M - Toggle summon, N - Ship name")
 ShowMenu("S - Set Warp Data, J - Jump, G - Jump to JumpGate")
 ShowMenu("B - Jump to Beacon, H - Jump to Hyperspace")
 ShowMenu("C - Summon crew, X - Shutdown WarpCore and Exit")
 SetColorDeflt()
 local event, keycode = os.pullEvent("key")
 if keycode == 31 then
  SetDirection()
  SetDistance()
  SaveData()
 elseif keycode == 50 then
  if SData.Summon then
   SData.Summon = false
  else
   SData.Summon = true
  end
  SaveData()
 elseif keycode == 32 then
  SetDimensions()
  SaveData()
 elseif keycode == 36 then
  rs.setOutput(Alarm, true)
  if Confirm() then
   Warp()
  end
  rs.setOutput(Alarm, false)
 elseif keycode == 46 then
  Summon()
 elseif keycode == 48 then
  JumpToBeacon()
 elseif keycode == 34 then
  JumpToGate()
 elseif keycode == 35 then
  rs.setOutput(Alarm, true)
  if Confirm() then
   rs.setOutput(Alarm, false)
   warp.mode(5)
   warp.jump()
  end
  rs.setOutput(Alarm, false)
 elseif keycode == 45 then
  mainloop = false
 elseif keycode == 49 then
  SetShipName()
 end
end

if SData.Summon then
 SData.Summon = false
 SaveData()
end
Clear()
print("wish you good")
warp.mode(0)
sleep(0.5)
os.shutdown()