--[========================================================================[--

Main file for Thrust II Reloaded.

Copyright © 2015-2018 Pedro Gimeno Fortea

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

--]========================================================================]--


-- This needs to be here because some versions don't use conf.lua
love_version = love._version_major
               and love._version_major * 1000000
                 + love._version_minor * 1000
                 + love._version_revision
               or 0

if love.default_moose then
  -- Version 0.2.1 doesn't close the window - force it to.
  -- Not the cleanest way but with no docs, that's the best
  -- we could do.
  function load()
    assert(love_version >= 0009001,
      "This game requires love2d version 0.9.1 or greater")
  end
  function draw() os.exit(1) end
  return
end

assert(love_version >= 0009000,
  "This game requires love2d version 0.9.1 or greater")

-- For performance and licensing reasons, strict.lua is only run by the
-- developer, not packaged. Include it in the strict/ directory if you want
-- to develop using it.
-- Must be activated after the assert, or 0.6.x crashes during error reporting
pcall(require, 'strict.strict')

local la,le,lfs,lf,lg,li,lj,lk,lm,lmo,lp,ls,lsys,lth,lt,lw = require'ns'()

main = {}
local active, canvas

function main.activate(new)
  if active and active.deactivate then active.deactivate() end
  active = new
  if active.activate then active.activate() end
end

local DIV = love_version >= 11000000 and 255 or 1

-- screens
screens = {
  splash     = require 'splash';
  menu       = require 'menu';
  redef      = require 'redef';
  getready   = require 'getready';
  game       = require 'game';
  gameover   = require 'gameover';
  gamewon    = require 'gamewon';
  orbexplode = require 'orbexplode';
}

-- modules that are not screens
main.map,
  main.enemytypes,
  main.enemies,
  main.orbs,
  main.decoys,
  main.targets,
  main.respawn,
  main.agents = require 'layout' ()


function main.setVolume(vol, save)
  vol = (vol < 0) and 0 or vol
  vol = (vol > 1) and 1 or vol
  la.setVolume(vol^2)
  main.vol = vol

  if save then
    local tmp = main.vol .. (main.music and "y" or "n")
    lfs.write("vol.txt", tmp, #tmp)
  end
end

local wparamtable = {fullscreen=true,vsync=love_version >= 11000000 and 0,
                     resizable=true,fullscreentype="desktop"}

function main.saveScreen()
  local tmp = string.format("%.17g\n%s\n%d\n%d\n",
      main.pixelsize,
      main.fullscreen and "y" or "n",
      main.wwNFS, main.whNFS)
  lfs.write("screen.txt", tmp, #tmp)
end

function main.setMode()
  wparamtable.fullscreen = main.fullscreen
  lw.setMode(main.wwNFS, main.whNFS, wparamtable)
  lw.setTitle("Thrust II reloaded")
  main.saveScreen()
end

function main.tomenu()
  main.activate(screens.menu)
end

-- deepcopy from http://lua-users.org/wiki/CopyTable
-- minus the parts that we don't need
function main.deepcopy(orig)
  local orig_type = type(orig)
  local copy
  if orig_type == 'table' then
    copy = {}
    for orig_key, orig_value in next, orig, nil do
--      copy[deepcopy(orig_key)] = deepcopy(orig_value)
      copy[orig_key] = main.deepcopy(orig_value)
    end
    -- setmetatable(copy, deepcopy(getmetatable(orig)))
  else -- number, string, boolean, etc
    copy = orig
  end
  return copy
end

if love_version >= 11000000 then
  local info = {type = false, size = false, modtime = false}
  function main.isFile(f)
    return lfs.getInfo(f, info) ~= nil
      and (info.type == "file" or info.type == "symlink")
  end
else
  function main.isFile(f)
    return lfs.isFile(f)
  end
end

function main.drawaligned(alignment, drawable, x, y, angle, xzoom, yzoom)
  -- alignment's value works as a numeric keypad ("normal" is 7):
  --
  --    7 8 9
  --    4 5 6
  --    1 2 3
  if type(drawable) == "userdata" and drawable.typeOf and drawable:typeOf("Drawable") then
    -- it's a drawable
    local iw, ih = drawable:getDimensions()
    local ox, oy = 0, 0
    if alignment <= 3 then
      oy = ih
    elseif alignment <= 6 then
      oy = ih / 2
    end
    if alignment % 3 == 0 then
      ox = iw
    elseif alignment % 3 == 2 then
      ox = iw / 2
    end

    return lg.draw(drawable, math.floor(x), math.floor(y), angle, xzoom, yzoom, ox, oy)
  end
end


function love.load(args)
  lfs.setIdentity("ThrustIIreloaded")

  main.argv = args

  main.pixelsize = 1
  main.fullscreen = false
  main.wwNFS = 640 -- non-fullscreen width
  main.whNFS = 480 -- non-fullscreen height
  if main.isFile("screen.txt") then
    local lines = lfs.read("screen.txt")
    local pix, fs, ww, wh = lines:match("^(%d+%.?%d*[Ee]?%d*)\n([yn]?)\n(%d+)\n(%d+)\n")
    main.pixelsize = tonumber(pix)
    main.fullscreen = fs == "y"
    main.wwNFS = tonumber(ww)
    main.whNFS = tonumber(wh)
  else
    main.saveScreen()
  end
  main.setMode()

  lg.setBackgroundColor(0,0,0,0)
  lg.setDefaultFilter("nearest", "nearest")
  main.ww = math.ceil(lg.getWidth()/main.pixelsize)
  main.wh = math.ceil(lg.getHeight()/main.pixelsize)
  main.wcx, main.wcy = main.ww/2, main.wh/2
  canvas = lg.newCanvas(main.ww, main.wh)

  main.font = lg.newImageFont("img/Font16x16.png",
      " !\"#$%&'()*+,-./0123456789:;<=>?\127ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~@",
      1) -- required for 0.10 compatibility, ignored by 0.9
  lg.setFont(main.font)

  local space = love._version_major == 0 and love._version_minor < 10 and " " or "space"
  main.keys =
    { left = "q", right = "w", thrust = "p", pickup = "l", fire = space }
  if main.isFile("keys.txt") then
    local line = 0
    for s in lfs.lines("keys.txt") do
      if s == " " or s == "space" then s = space end -- 0.10 compat
      line = line + 1
      if line == 1 then
        main.keys.left = s
      elseif line == 2 then
        main.keys.right = s
      elseif line == 3 then
        main.keys.thrust = s
      elseif line == 4 then
        main.keys.pickup = s
      elseif line == 5 then
        main.keys.fire = s
      else
        break
      end
    end
  else
    local s = main.keys.left   .. "\n"
           .. main.keys.right  .. "\n"
           .. main.keys.thrust .. "\n"
           .. main.keys.pickup .. "\n"
           .. main.keys.fire
    lfs.write("keys.txt", s, #s)
  end

  main.vol = 1
  main.music = true

  if main.isFile("vol.txt") then
    local line = lfs.read("vol.txt")
    local vol, music = line:match("^(%d+%.?%d*[Ee]?%d*)([yn]?)$")
    main.setVolume(tonumber(vol))
    main.music = music == "y"
  else
    main.setVolume(main.vol, true)
  end

  for k, mod in next, screens do
    if mod and mod.load then mod.load(arg) end
  end

  -- Bootstrap activation
  main.activate(screens.splash)

  -- Allow volume change with auto-repeat
  lk.setKeyRepeat(true)

  main.singlestep = false
  main.savedelay = false
  main.quitaccepted = false

  -- Dialog data
  main.dialogdata = {}
  main.dialogdata.waitrelease = false
  main.dialogactive = false
end

-- HACK: rather than altering love.run, we alter love.timer.sleep.
-- This guarantees that it's run between lg.present and fetching
-- events, giving maximum responsivity.
main.sleep = lt.sleep
main.lastTime = love.timer.getTime()
function lt.sleep(t)
  main.sleep(main.lastTime + 0.02 - love.timer.getTime())
  main.lastTime = love.timer.getTime()
end

function love.update(dt)
  if main.savedelay then
    main.savedelay = main.savedelay - dt
    if main.savedelay <= 0 then
      main.savedelay = false
    end
  end
  if main.paused and not main.singlestep or main.dialogactive then return end
  if active.update then active.update(main.singlestep and 0.03125 or dt) end
end

local function roundrect(x1, y1, w, h, r)
  local nsegments = 8
  local x2, y2 = x1+w, y1+h
  local npoints = nsegments + 1 -- points per corner
  local poly = {}
  poly[1] = x2
  poly[2] = y2 - r
  poly[npoints*2-1] = x2 - r
  poly[npoints*2] = y2
  poly[npoints*2+1] = x1 + r
  poly[npoints*2+2] = y2
  poly[npoints*4-1] = x1
  poly[npoints*4] = y2 - r
  poly[npoints*4+1] = x1
  poly[npoints*4+2] = y1 + r
  poly[npoints*6-1] = x1 + r
  poly[npoints*6] = y1
  poly[npoints*6+1] = x2 - r
  poly[npoints*6+2] = y1
  poly[npoints*8-1] = x2
  poly[npoints*8] = y1 + r
  local angle, x, y

  for i = 2, (nsegments-1)*2, 2 do
    angle = math.pi * 0.25 * (i / nsegments)
    x = math.cos(angle) * r
    y = math.sin(angle) * r
    poly[i+1] = x2 - r + x
    poly[i+2] = y2 - r + y
    poly[npoints*4-i-1] = x1 + r - x
    poly[npoints*4-i] = y2 - r + y
    poly[npoints*4+i+1] = x1 + r - x
    poly[npoints*4+i+2] = y1 + r - y
    poly[npoints*8-i-1] = x2 - r + x
    poly[npoints*8-i] = y1 + r - y
  end
  lg.polygon("fill", poly)
end


function main.centertext(text, x, y)
  lg.print(text, math.floor(x-main.font:getWidth(text)/2), math.floor(y-main.font:getHeight()/2))
end

local function dummyfunction()
end

-- Show a modal yes/no dialog
function main.dialog(text, cbyes, cbno)
  main.dialogdata.text = text
  main.dialogdata.cbyes = cbyes or dummyfunction
  main.dialogdata.cbno = cbno or dummyfunction
  main.dialogdata.waitrelease = false
  main.dialogactive = true
  -- If we're not in pause mode, pause now anyway since we're modal.
  if not main.paused and active.pause then active.pause(true) end
end

local function drawActive()
  -- HACK: Allow scroll-out in get ready screen, by not clearing the canvas
  if active ~= screens.getready then
    lg.clear()
  end
  active.draw()
end

function love.draw()
  lg.scale(main.pixelsize)
  if main.dialogactive then
    lg.setColor(85/DIV, 85/DIV, 85/DIV, 255/DIV)
    lg.rectangle("fill", 0, 0, main.ww, main.wh)
    lg.setColor(255/DIV, 255/DIV, 255/DIV, 120/DIV)
    lg.draw(canvas)
    lg.setColor(0/DIV, 0/DIV, 0/DIV, 200/DIV)

    local rectw = main.font:getWidth(main.dialogdata.text) + 68
    roundrect(main.wcx-rectw*0.5, main.wcy-50, rectw, 100, 7)
    lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
    main.centertext(main.dialogdata.text, main.wcx, main.wcy - 20)

    lg.setColor(160/DIV,160/DIV,160/DIV)
    roundrect(main.wcx-70, main.wcy+4, 60, 24, 4)
    roundrect(main.wcx+10, main.wcy+4, 60, 24, 4)
    lg.setColor(0/DIV, 0/DIV, 0/DIV, 255/DIV)
    main.centertext("YES", main.wcx-40, main.wcy+16)
    main.centertext("NO", main.wcx+40, main.wcy+16)
    lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
  else
    lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
    if main.paused and not main.singlestep then
      lg.draw(canvas)
      lg.setColor(64/DIV, 64/DIV, 64/DIV, 150/DIV)
      roundrect(main.wcx-60, main.wh-44, 120, 40, 6)
      lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
      main.centertext("PAUSED", main.wcx, main.wh - 24)
    else
      main.canvas = canvas
      if active.draw then
        lg.origin()
        canvas:renderTo(drawActive)
        lg.scale(main.pixelsize)
      end
      lg.draw(canvas)
    end
    if main.savedelay then
      lg.setColor(64/DIV, 64/DIV, 64/DIV, 200/DIV)
      roundrect(main.wcx-60, main.wh-44, 120, 40, 6)
      lg.setColor(255/DIV, 255/DIV, 255/DIV, 255/DIV)
      main.centertext("SAVED", main.wcx, main.wh - 24)
    end
  end

  if main.singlestep then main.singlestep = false end

  lg.origin()
end

local function quit_yes()
  main.quitaccepted = true
  le.quit()
end

function love.quit()
  if not main.quitaccepted then
    main.dialog("REALLY QUIT?", quit_yes)
    return true -- don't quit
  end
end

function love.mousepressed(x, y, b)
  x = x / main.pixelsize
  y = y / main.pixelsize
  if main.dialogactive then
    if x >= main.wcx-70 and x <= main.wcx-10
       and y >= main.wcy+4 and y <= main.wcy+28
    then
      main.dialogdata.cbyes()
      main.dialogactive = false
    elseif x >= main.wcx+10 and x <= main.wcx+70
       and y >= main.wcy+4 and y <= main.wcy+28
    then
      main.dialogdata.cbno()
      main.dialogactive = false
    end
    -- If we were not in pause mode then we paused because of the menu.
    -- If that's the case and we're closing the dialog, unpause now.
    if not main.dialogactive and not main.paused and active.pause then
      active.pause(false)
    end
  end
end

function love.keypressed(k, r, rr)
  -- 0.10+ compat
  if rr ~= nil then r = rr end

  if k == "kp-" or k == "kp+" then
    main.setVolume(main.vol + (k == "kp+" and 0.0625 or -0.0625), true)
  elseif r then
    -- nothing
  elseif main.dialogactive then
    if k == "y" then
      main.dialogdata.cbyes()
      main.dialogdata.waitrelease = true
    elseif k == "n" or k == "escape" then
      main.dialogdata.cbno()
      main.dialogdata.waitrelease = true
    end
  elseif k == "pause" and not main.dialogactive then
    main.paused = not main.paused
    if active.pause then active.pause(main.paused) end
  elseif active.keypressed and not main.paused then
    active.keypressed(k, r)
  end
  --[[ debug ]]
  --if k == "1" and main.paused then main.singlestep = true end
  --]]
end

function love.textinput(c)
  if not main.paused then
    if active.textinput then active.textinput(c) end
  end
end

function love.keyreleased(k)
  if main.dialogactive then
    if main.dialogdata.waitrelease then
      main.dialogdata.waitrelease = false
      main.dialogactive = false
      if not main.paused and active.pause then active.pause(false) end
    end
  elseif k ~= "pause" and not main.paused then
    if active.keyreleased then active.keyreleased(k) end
  end
end

function love.resize(neww, newh)
  main.ww = math.ceil(neww/main.pixelsize)
  main.wh = math.ceil(newh/main.pixelsize)
  main.wcx = main.ww/2
  main.wcy = main.wh/2
  local newcanvas = lg.newCanvas(main.ww, main.wh)
  local saveCanvas = lg.getCanvas()
  lg.setCanvas(newcanvas)
  main.drawaligned(5, canvas, main.wcx, main.wcy)
  lg.setCanvas(saveCanvas)
  canvas = newcanvas
  if not main.fullscreen then
    main.wwNFS = neww
    main.whNFS = newh
    main.saveScreen()
  end
  for k, mod in next, screens do
    if mod.resize then
      mod.resize(neww, newh)
    end
  end
end
