EnterGame = { }

-- =========================================================
-- SERVIDOR FIXO
-- =========================================================

local FIXED_SERVER_IP = "37.148.135.72"
local FIXED_SERVER_PORT = 7171
local FIXED_CLIENT_VERSION = 772

-- private variables
local loadBox
local enterGame
local enterGameButton
local clientBox
local protocolLogin
local server = nil
local versionsFound = false

local rememberPasswordBox

local checkedByUpdater = {}
local waitingForHttpResults = 0

-- =========================================================
-- PROTOCOLO
-- =========================================================

local function onProtocolError(protocol, message, errorCode)
  if errorCode then
    return EnterGame.onError(message)
  end
  return EnterGame.onLoginError(message)
end

-- =========================================================
-- SESSION KEY
-- =========================================================

local function onSessionKey(protocol, sessionKey)
  G.sessionKey = sessionKey
end

-- =========================================================
-- CHARACTER LIST
-- =========================================================

local function onCharacterList(protocol, characters, account, otui)

  if rememberPasswordBox and rememberPasswordBox:isChecked() then
    local accountEncrypted = g_crypt.encrypt(G.account)
    local passwordEncrypted = g_crypt.encrypt(G.password)

    g_settings.set('account', accountEncrypted)
    g_settings.set('password', passwordEncrypted)
  else
    EnterGame.clearAccountFields()
  end

  for _, characterInfo in pairs(characters) do
    if characterInfo.previewState and
       characterInfo.previewState ~= PreviewState.Default then

      characterInfo.worldName =
        characterInfo.worldName .. ', Preview'
    end
  end

  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  CharacterList.create(characters, account, otui)
  CharacterList.show()

  g_settings.save()
end

-- =========================================================
-- UPDATE
-- =========================================================

local function onUpdateNeeded(protocol, signature)
  return EnterGame.onError(
    tr('Your client needs updating, try redownloading it.')
  )
end

-- =========================================================
-- PROXY
-- =========================================================

local function onProxyList(protocol, proxies)
  if not g_proxy then
    return
  end

  for _, proxy in ipairs(proxies) do
    g_proxy.addProxy(
      proxy["host"],
      proxy["port"],
      proxy["priority"]
    )
  end
end

-- =========================================================
-- FEATURES
-- =========================================================

local function parseFeatures(features)
  for feature_id, value in pairs(features) do
    if value == "1" or
       value == "true" or
       value == true then

      g_game.enableFeature(feature_id)
    else
      g_game.disableFeature(feature_id)
    end
  end
end

-- =========================================================
-- VALIDAR THINGS
-- =========================================================

local function validateThings(things)

  local incorrectThings = ""
  local missingFiles = false
  local versionForMissingFiles = 0

  if things ~= nil then

    local thingsNode = {}

    for thingtype, thingdata in pairs(things) do

      thingsNode[thingtype] = thingdata[1]

      if not g_resources.fileExists(
        "/things/" .. thingdata[1]
      ) then

        incorrectThings =
          incorrectThings ..
          "Missing file: " ..
          thingdata[1] ..
          "\n"

        missingFiles = true

        versionForMissingFiles =
          thingdata[1]:split("/")[1]

      else

        local localChecksum =
          g_resources.fileChecksum(
            "/things/" .. thingdata[1]
          ):lower()

        if localChecksum ~= thingdata[2]:lower()
           and #thingdata[2] > 1 then

          if g_resources.isLoadedFromArchive() then

            incorrectThings =
              incorrectThings ..
              "Invalid checksum of file: " ..
              thingdata[1] ..
              " (is " ..
              localChecksum ..
              ", should be " ..
              thingdata[2]:lower() ..
              ")\n"

          end
        end
      end
    end

    g_settings.setNode(
      "things",
      thingsNode
    )

  else

    g_settings.setNode(
      "things",
      {}
    )
  end

  if missingFiles then

    incorrectThings =
      incorrectThings ..
      "\nYou should open data/things and create directory " ..
      versionForMissingFiles ..
      ".\nIn this directory (data/things/" ..
      versionForMissingFiles ..
      ") you should put missing\nfiles (Tibia.dat and Tibia.spr/Tibia.cwm) " ..
      "from correct Tibia version."

  end

  return incorrectThings
end

-- =========================================================
-- HTTP
-- =========================================================

local function onTibia12HTTPResult(session, playdata)
  local characters = {}
  local worlds = {}

  local account = {
    status = 0,
    subStatus = 0,
    premDays = 0
  }

  if session["status"] ~= "active" then
    account.status = 1
  end

  if session["ispremium"] then
    account.subStatus = 1
  end

  if session["premiumuntil"] > g_clock.seconds() then
    account.subStatus =
      math.floor(
        (session["premiumuntil"] - g_clock.seconds()) / 86400
      )
  end

  local things = {
    data = {G.clientVersion .. "/Tibia.dat", ""},
    sprites = {G.clientVersion .. "/Tibia.cwm", ""},
  }

  local incorrectThings = validateThings(things)

  if #incorrectThings > 0 then

    things = {
      data = {G.clientVersion .. "/Tibia.dat", ""},
      sprites = {G.clientVersion .. "/Tibia.spr", ""},
    }

    incorrectThings = validateThings(things)
  end

  if #incorrectThings > 0 then

    g_logger.error(incorrectThings)

    if Updater and
       not checkedByUpdater[G.clientVersion] then

      checkedByUpdater[G.clientVersion] = true

      return Updater.check({
        version = G.clientVersion,
        host = G.host
      })

    else
      return EnterGame.onError(incorrectThings)
    end
  end

  onSessionKey(nil, session["sessionkey"])

  for _, world in pairs(playdata["worlds"]) do

    worlds[world.id] = {
      name = world.name,
      port =
        world.externalportunprotected or
        world.externalportprotected or
        world.externaladdress,

      address =
        world.externaladdressunprotected or
        world.externaladdressprotected or
        world.externalport
    }
  end

  for _, character in pairs(playdata["characters"]) do

    local world = worlds[character.worldid]

    if world then

      table.insert(
        characters,
        {
          name = character.name,
          worldName = world.name,
          worldIp = world.address,
          worldPort = world.port
        }
      )
    end
  end

  if g_proxy then

    g_proxy.clear()

    if playdata["proxies"] then

      for i, proxy in ipairs(playdata["proxies"]) do

        g_proxy.addProxy(
          proxy["host"],
          tonumber(proxy["port"]),
          tonumber(proxy["priority"])
        )
      end
    end
  end

  g_game.setCustomProtocolVersion(0)
  g_game.chooseRsa(G.host)
  g_game.setClientVersion(G.clientVersion)

  g_game.setProtocolVersion(
    g_game.getClientProtocolVersion(
      G.clientVersion
    )
  )

  g_game.setCustomOs(-1)

  if not g_game.getFeature(GameExtendedOpcode) then
    g_game.setCustomOs(5)
  end

  onCharacterList(
    nil,
    characters,
    account,
    nil
  )
end

local function onHTTPResult(data, err)

  if waitingForHttpResults == 0 then
    return
  end

  waitingForHttpResults =
    waitingForHttpResults - 1

  if err and waitingForHttpResults > 0 then
    return
  end

  if err then
    return EnterGame.onError(err)
  end

  waitingForHttpResults = 0

  if data['error'] and
     data['error']:len() > 0 then

    return EnterGame.onLoginError(
      data['error']
    )

  elseif data['errorMessage'] and
         data['errorMessage']:len() > 0 then

    return EnterGame.onLoginError(
      data['errorMessage']
    )
  end

  if type(data["session"]) == "table" and
     type(data["playdata"]) == "table" then

    return onTibia12HTTPResult(
      data["session"],
      data["playdata"]
    )
  end

  local characters = data["characters"]
  local account = data["account"]
  local session = data["session"]

  local version = data["version"]
  local things = data["things"]
  local customProtocol = data["customProtocol"]

  local features = data["features"]
  local settings = data["settings"]
  local rsa = data["rsa"]
  local proxies = data["proxies"]

  local incorrectThings =
    validateThings(things)

  if #incorrectThings > 0 then
    g_logger.info(incorrectThings)
    return EnterGame.onError(
      incorrectThings
    )
  end

  g_game.setCustomProtocolVersion(0)

  if customProtocol ~= nil then

    customProtocol = tonumber(customProtocol)

    if customProtocol ~= nil and
       customProtocol > 0 then

      g_game.setCustomProtocolVersion(
        customProtocol
      )
    end
  end

  if settings ~= nil then

    for option, value in pairs(settings) do

      modules.client_options.setOption(
        option,
        value,
        true
      )
    end
  end

  G.clientVersion = version

  g_game.setClientVersion(
    version
  )

  g_game.setProtocolVersion(
    g_game.getClientProtocolVersion(
      version
    )
  )

  g_game.setCustomOs(-1)

  if rsa ~= nil then
    g_game.setRsa(rsa)
  end

  if features ~= nil then
    parseFeatures(features)
  end

  if session ~= nil and
     session:len() > 0 then

    onSessionKey(
      nil,
      session
    )
  end

  if g_proxy then

    g_proxy.clear()

    if proxies then

      for i, proxy in ipairs(proxies) do

        g_proxy.addProxy(
          proxy["host"],
          tonumber(proxy["port"]),
          tonumber(proxy["priority"])
        )
      end
    end
  end

  onCharacterList(
    nil,
    characters,
    account,
    nil
  )
end

-- =========================================================
-- INIT
-- =========================================================

function EnterGame.init()

  if USE_NEW_ENERGAME then
    return
  end

  enterGame =
    g_ui.displayUI('entergame')

  -- =====================================================
  -- SOMENTE O QUE EXISTE NO NOVO OTUI
  -- =====================================================

  rememberPasswordBox =
    enterGame:getChildById(
      'rememberPasswordBox'
    )

  -- =====================================================
  -- CARREGAR CONTA SALVA
  -- =====================================================

  local account =
    g_crypt.decrypt(
      g_settings.get('account')
    )

  local password =
    g_crypt.decrypt(
      g_settings.get('password')
    )

  enterGame:getChildById(
    'accountPasswordTextEdit'
  ):setText(password)

  enterGame:getChildById(
    'accountNameTextEdit'
  ):setText(account)

  rememberPasswordBox:setChecked(
    #account > 0
  )

  -- =====================================================
  -- SERVIDOR FIXO
  -- =====================================================

  server = FIXED_SERVER_IP

  G.host = FIXED_SERVER_IP
  G.clientVersion = FIXED_CLIENT_VERSION

  -- =====================================================
  -- CTRL + G
  -- =====================================================

  g_keyboard.bindKeyDown(
    'Ctrl+G',
    EnterGame.openWindow
  )

  if g_game.isOnline() then
    return EnterGame.hide()
  end

  scheduleEvent(
    function()
      EnterGame.show()
    end,
    100
  )
end

-- =========================================================
-- TERMINATE
-- =========================================================

function EnterGame.terminate()

  if not enterGame then
    return
  end

  g_keyboard.unbindKeyDown(
    'Ctrl+G'
  )

  enterGame:destroy()

  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  if protocolLogin then
    protocolLogin:cancelLogin()
    protocolLogin = nil
  end

  EnterGame = nil
end

-- =========================================================
-- SHOW
-- =========================================================

function EnterGame.show()

  if not enterGame then
    return
  end

  enterGame:show()
  enterGame:raise()
  enterGame:focus()

  enterGame:getChildById(
    'accountNameTextEdit'
  ):focus()
end

-- =========================================================
-- HIDE
-- =========================================================

function EnterGame.hide()

  if not enterGame then
    return
  end

  enterGame:hide()
end

-- =========================================================
-- OPEN WINDOW
-- =========================================================

function EnterGame.openWindow()

  if g_game.isOnline() then

    CharacterList.show()

  elseif not g_game.isLogging() and
         not CharacterList.isVisible() then

    EnterGame.show()
  end
end

-- =========================================================
-- LIMPAR CAMPOS
-- =========================================================

function EnterGame.clearAccountFields()

  if not enterGame then
    return
  end

  enterGame:getChildById(
    'accountNameTextEdit'
  ):clearText()

  enterGame:getChildById(
    'accountPasswordTextEdit'
  ):clearText()

  enterGame:getChildById(
    'accountTokenTextEdit'
  ):clearText()

  enterGame:getChildById(
    'accountNameTextEdit'
  ):focus()

  g_settings.remove('account')
  g_settings.remove('password')
end

-- =========================================================
-- LOGIN
-- =========================================================

function EnterGame.doLogin(
  account,
  password,
  token,
  host
)

  if g_game.isOnline() then

    local errorBox =
      displayErrorBox(
        tr('Login Error'),
        tr(
          'Cannot login while already in game.'
        )
      )

    connect(
      errorBox,
      {
        onOk = EnterGame.show
      }
    )

    return
  end

  -- =====================================================
  -- CONTA
  -- =====================================================

  G.account =
    account or
    enterGame:getChildById(
      'accountNameTextEdit'
    ):getText()

  G.password =
    password or
    enterGame:getChildById(
      'accountPasswordTextEdit'
    ):getText()

  G.authenticatorToken =
    token or
    enterGame:getChildById(
      'accountTokenTextEdit'
    ):getText()

  G.stayLogged = true

  -- =====================================================
  -- SERVIDOR FIXO
  -- =====================================================

  G.host = FIXED_SERVER_IP
  G.server = FIXED_SERVER_IP
  G.clientVersion = FIXED_CLIENT_VERSION

  local server_ip =
    FIXED_SERVER_IP

  local server_port =
    FIXED_SERVER_PORT

  -- =====================================================
  -- SALVAR CONTA
  -- =====================================================

  if not rememberPasswordBox:isChecked() then

    g_settings.set(
      'account',
      G.account
    )

    g_settings.set(
      'password',
      G.password
    )
  end

  -- =====================================================
  -- NÃO SALVAR IP / PORT / VERSION
  -- =====================================================

  g_settings.remove('host')
  g_settings.remove('server')
  g_settings.remove('client-version')

  g_settings.save()

  -- =====================================================
  -- THINGS 7.72
  -- =====================================================

  local things = {
    data = {
      G.clientVersion .. "/Tibia.dat",
      ""
    },

    sprites = {
      G.clientVersion .. "/Tibia.cwm",
      ""
    }
  }

  local incorrectThings =
    validateThings(things)

  if #incorrectThings > 0 then

    things = {
      data = {
        G.clientVersion .. "/Tibia.dat",
        ""
      },

      sprites = {
        G.clientVersion .. "/Tibia.spr",
        ""
      }
    }

    incorrectThings =
      validateThings(things)
  end

  if #incorrectThings > 0 then

    g_logger.error(
      incorrectThings
    )

    if Updater and
       not checkedByUpdater[G.clientVersion] then

      checkedByUpdater[G.clientVersion] = true

      return Updater.check({
        version = G.clientVersion,
        host = G.host
      })

    else

      return EnterGame.onError(
        incorrectThings
      )
    end
  end

  -- =====================================================
  -- PROTOCOL LOGIN
  -- =====================================================

  protocolLogin =
    ProtocolLogin.create()

  protocolLogin.onLoginError =
    onProtocolError

  protocolLogin.onSessionKey =
    onSessionKey

  protocolLogin.onCharacterList =
    onCharacterList

  protocolLogin.onUpdateNeeded =
    onUpdateNeeded

  protocolLogin.onProxyList =
    onProxyList

  -- =====================================================
  -- LOADING
  -- =====================================================

  EnterGame.hide()

  loadBox =
    displayCancelBox(
      tr('Please wait'),
      tr('Connecting to login server...')
    )

  connect(
    loadBox,
    {
      onCancel = function(msgbox)

        loadBox = nil

        protocolLogin:cancelLogin()

        EnterGame.show()
      end
    }
  )

  -- =====================================================
  -- CLIENT VERSION 7.72
  -- =====================================================

  g_game.setClientVersion(
    FIXED_CLIENT_VERSION
  )

  g_game.setProtocolVersion(
    g_game.getClientProtocolVersion(
      FIXED_CLIENT_VERSION
    )
  )

  g_game.setCustomProtocolVersion(0)
  g_game.setCustomOs(-1)

  -- =====================================================
  -- RSA
  -- =====================================================

  g_game.chooseRsa(
    FIXED_SERVER_IP
  )

  -- =====================================================
  -- WINDOWS
  -- =====================================================

  if not g_game.getFeature(
    GameExtendedOpcode
  ) then

    g_game.setCustomOs(2)
  end

  -- =====================================================
  -- PROXY
  -- =====================================================

  if g_proxy then
    g_proxy.clear()
  end

  -- =====================================================
  -- LOGIN NO SERVIDOR
  -- =====================================================

  if modules.game_things.isLoaded() then

    g_logger.info(
      "Connecting to: " ..
      server_ip ..
      ":" ..
      server_port
    )

    protocolLogin:login(
      server_ip,
      server_port,
      G.account,
      G.password,
      G.authenticatorToken,
      G.stayLogged
    )

  else

    loadBox:destroy()
    loadBox = nil

    EnterGame.show()
  end
end

-- =========================================================
-- HTTP
-- =========================================================

function EnterGame.doLoginHttp()

  return EnterGame.onError(
    tr('HTTP login is disabled.')
  )
end

-- =========================================================
-- ERRO
-- =========================================================

function EnterGame.onError(err)

  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  local errorBox =
    displayErrorBox(
      tr('Login Error'),
      err
    )

  errorBox.onOk =
    EnterGame.show
end

-- =========================================================
-- LOGIN ERROR
-- =========================================================

function EnterGame.onLoginError(err)

  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  local errorBox =
    displayErrorBox(
      tr('Login Error'),
      err
    )

  errorBox.onOk =
    EnterGame.show

  if err:lower():find("invalid")
     or err:lower():find("not correct")
     or err:lower():find("or password") then

    EnterGame.clearAccountFields()
  end
end