local g_new_sync = true
local g_device_settings = {}
local g_last_sync_time = 0
local g_sync_frequency = 1800
local g_disable_sync = false
local localSettingNames = {
  "eaglesUsedIn",
  "eagleUsedTime",
  "mightyEagleEnabled",
  "gfxLowQuality",
  "selectedEpisode",
  "currentZoomLevelMainMenu",
  "currentMainMenuTheme",
  "currentLevelSelectionPages",
  "audioEnabled",
  "nookUserAlreadyConnected",
  "fullscreen",
  "screenWidth",
  "screenHeight",
  "mainMenuAdLastChecked",
  "mainMenuAdTimeStamp",
  "installedApps",
  "installedAppsTTL",
  "installedAppsLastCheck",
  "iap_restore_checked",
  "shopChildlockOn",
  "playerRegistered",
  "settingsVersion",
  "license"
}
function new(self)
  return self
end
function init(self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_LEVEL_LOADING_INIT, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_CHANGE_SCENE, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_ROVIO_ID_LOGIN_ACCEPTED, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_ROVIO_ID_LOGOUT, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_GAME_DEACTIVATED, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_GAME_RESUMED, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_START_CLOUD_SYNC, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_SYNC_SUCCESS, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_SYNC_FAILURE, self)
  gamelua.eventManager:addEventListener(gamelua.events.EID_SERVICE_AVAILABILITY_CHANGED, self)
end
function update(self, dt)
  if gamelua.RovioAccount.profile.id and cloudData and g_new_sync then
    gamelua.print("<Cloud Sync> settings from cloud received\n")
    if firstCloudSync then
      gamelua.settings = combineSettings(gamelua.settings, g_device_settings.settings)
      gamelua.highscores = combineSettings(gamelua.highscores, g_device_settings.highscores)
    end
    if cloudData.settings and cloudData.settings.settingsVersion and gamelua.settingsWrapper:getSettingsVersion().id ~= cloudData.settings.settingsVersion.id then
      resolveVersionMismatch(cloudData.settings)
    end
    if cloudData then
      local combined_settings = combineSettings(gamelua.settings, cloudData.settings)
      local combined_highscores = combineSettings(gamelua.highscores, cloudData.highscores)
      saveToCloud(combined_settings, combined_highscores)
      if gamelua.menuManager and gamelua.menuManager:getRoot() ~= nil and gamelua.menuManager:getRoot().name == "mainMenuRoot" then
        replaceSettings()
        gamelua.menuManager:getRoot():refreshMiddleButtons()
      end
    end
    g_new_sync = false
  end
  if gamelua.RovioAccount.profile.id and gamelua.g_time - g_last_sync_time > g_sync_frequency then
    syncWithCloud(false)
  end
end
function eventTriggered(self, event)
  if event.id == gamelua.events.EID_LEVEL_LOADING_INIT or event.id == gamelua.events.EID_CHANGE_SCENE then
    if cloudData then
      replaceSettings()
    end
  elseif event.id == gamelua.events.EID_ROVIO_ID_LOGIN_ACCEPTED then
    gamelua.settingsWrapper:setPlayerRegistered()
    gamelua.notifyEventManager(gamelua.events.EID_ROVIO_ID_LOGIN_SUCCESS, {
      promptLogin = event.promptLogin
    })
    g_device_settings = {
      settings = gamelua.deepCopy(gamelua.settings),
      highscores = gamelua.deepCopy(gamelua.highscores)
    }
    gamelua.loadTableFromFile("settings_" .. gamelua.RovioAccount.profile.id .. ".lua", "deviceSettings")
    gamelua.settings = gamelua.deviceSettings
    if _G.next(gamelua.settings) then
      gamelua.settingsWrapper:convertSettings()
    end
    gamelua.settings = combineSettings(removeSyncableSettings(g_device_settings.settings), removeLocalSettings(gamelua.settings))
    gamelua.loadTableFromFile("highscores_" .. gamelua.RovioAccount.profile.id .. ".lua", "deviceHighscores")
    gamelua.highscores = gamelua.deviceHighscores
    syncWithCloud(true)
  elseif event.id == gamelua.events.EID_ROVIO_ID_LOGOUT then
    gamelua.loadTableFromFile("settings.lua", "deviceSettings")
    gamelua.loadTableFromFile("highscores.lua", "deviceHighscores")
    gamelua.settings = combineSettings(removeSyncableSettings(gamelua.settings), removeLocalSettings(gamelua.deviceSettings))
    gamelua.highscores = gamelua.deviceHighscores
    cloudData = nil
  elseif event.id == gamelua.events.EID_SYNC_SUCCESS then
    local sync_popup = getCurrentPopup()
    if sync_popup ~= nil then
      sync_popup:leave()
    end
    firstCloudSync = false
    gamelua.print("<Cloud Sync> settings saved\n")
  elseif event.id == gamelua.events.EID_SYNC_FAILURE then
    local sync_popup = getCurrentPopup()
    if sync_popup ~= nil then
      sync_popup:setText("Saving... Failed")
      sync_popup:leave()
    end
  elseif event.id == gamelua.events.EID_START_CLOUD_SYNC or event.id == gamelua.events.EID_GAME_DEACTIVATED or event.id == gamelua.events.EID_GAME_RESUMED then
    if gamelua.RovioAccount.profile.id then
      syncWithCloud(event.loadFirst)
    end
  elseif event.id == gamelua.events.EID_SERVICE_AVAILABILITY_CHANGED and event.serviceName == "identityLevel2" and event.available == true then
    gamelua.loginRovioId(false, "sign_in")
  end
end
function combineSettings(localSettings, externalSettings)
  if _G.type(externalSettings) ~= "table" then
    gamelua.print("<Cloud Sync> external settings in unsuitable format\n")
    return localSettings
  end
  gamelua.print("<Cloud Sync> Combining settings\n")
  local combined = gamelua.deepCopy(localSettings) or {}
  combined = handleTableCombine(localSettings, externalSettings)
  combined.firstTimeUseLocation = localSettings.firstTimeUseLocation and externalSettings.firstTimeUseLocation
  if externalSettings.tutorials and localSettings.tutorials then
    for k, v in _G.pairs(externalSettings.tutorials) do
      if localSettings.tutorials[k] and (v.showHelp == false or localSettings.tutorials[k].showHelp == false) then
        combined.tutorials[k].showHelp = false
      end
    end
  end
  for item, amounts in _G.pairs(gamelua.settingsWrapper:getLocalInventory()) do
    if gamelua.settingsWrapper:getItemAmount(item) < 0 then
      local positiveDifference = _G.math.abs(gamelua.settingsWrapper:getItemAmount(item))
      local totalPowerups = amounts.totalCount + positiveDifference
      gamelua.settingsWrapper:setItemAmount(item, totalPowerups, amounts.usedCount)
      gamelua.notifyEventManager(gamelua.events.EID_POWERUPS_DUPLICATED, {product = item, amount = positiveDifference})
    end
  end
  gamelua.print("<Cloud Sync> settings combined\n")
  return combined
end
function handleTableCombine(localSettings, externalSettings)
  local combined = gamelua.deepCopy(localSettings) or {}
  for k, v in _G.pairs(externalSettings) do
    if localSettings == nil or localSettings[k] == nil then
      combined[k] = v
    elseif _G.type(v) == "boolean" then
      combined[k] = localSettings[k] or v
    elseif _G.type(v) == "number" then
      combined[k] = _G.math.max(localSettings[k], v)
    elseif _G.type(v) == "table" then
      if isDate(v) and isDate(localSettings[k]) then
        if gamelua.getTimeDifferenceInSeconds(localSettings[k], v) > 0 then
          combined[k] = localSettings[k]
        else
          combined[k] = v
        end
      else
        combined[k] = handleTableCombine(localSettings[k], v)
      end
    end
  end
  return combined
end
function isDate(targetTable)
  if _G.type(targetTable) == "table" and targetTable.year and targetTable.month and targetTable.day and targetTable.hour and targetTable.minutes and targetTable.seconds then
    return true
  end
  return false
end
function removeLocalSettings(targetSettings)
  local filteredSettings = gamelua.deepCopy(targetSettings) or {}
  for i, v in _G.ipairs(localSettingNames) do
    filteredSettings[v] = nil
  end
  return filteredSettings
end
function filterHighscores(targetHighscores)
  local filteredScores = gamelua.deepCopy(targetHighscores) or {}
  for k, v in _G.pairs(filteredScores) do
    if gamelua.settingsWrapper:isMightyEagleEnabled() == false then
      v.eagleScoreMax = nil
      v.eagleScore = nil
      v.eagleScoreMin = nil
      v.freeEagleUsed = nil
    end
  end
  return filteredScores
end
function removeSyncableSettings(targetSettings)
  local filteredSettings = {}
  for i, v in _G.ipairs(localSettingNames) do
    filteredSettings[v] = targetSettings[v]
  end
  return filteredSettings
end
function syncWithCloud(loadFirst)
  g_last_sync_time = gamelua.g_time
  if g_disable_sync then
    gamelua.print("<Cloud Sync> sync cancelled\n")
    return
  end
  if loadFirst then
    local loadStarted = gamelua.loadCloudSettings()
    if loadStarted == true then
      showSyncPopup("Saving...")
      g_new_sync = true
      gamelua.print("<Cloud Sync> sync download started\n")
    else
      gamelua.print("<Cloud Sync> sync download failed: sync already in progress...\n")
    end
  else
    showSyncPopup("Saving...")
    saveToCloud(gamelua.settings, gamelua.highscores)
  end
end
function saveToCloud(newSettings, newHighscores)
  gamelua.syncReadySettings = {settings = newSettings, highscores = newHighscores}
  gamelua.saveCloudSettings("syncReadySettings")
end
function replaceSettings()
  gamelua.settings = combineSettings(gamelua.settings, removeLocalSettings(cloudData.settings))
  gamelua.highscores = combineSettings(gamelua.highscores, filterHighscores(cloudData.highscores))
  gamelua.saveLuaFileWrapper("highscores.lua", "highscores", true)
  gamelua.saveLuaFileWrapper("settings.lua", "settings", true)
  cloudData = nil
end
function resolveVersionMismatch(externalSettings)
  if externalSettings.settingsVersion.id > gamelua.settingsWrapper:getSettingsVersion().id then
    gamelua.print("<Cloud Sync> Version mismatch, cloud version is higher, can't merge!\n")
    gamelua.notifyEventManager(gamelua.events.EID_SYNC_FAILURE)
    g_disable_sync = true
    cloudData = nil
  else
    gamelua.print("<Cloud Sync> Version mismatch, converting cloud settings..")
    local localSettings = gamelua.deepCopy(gamelua.settings)
    gamelua.settings = externalSettings
    gamelua.settingsWrapper:convertSettings()
    gamelua.settings = localSettings
  end
end
function showSyncPopup(message, maxShowTime)
  if not getCurrentPopup() then
    gamelua.notificationsFrame:addChild(SyncPopup:new(message, maxShowTime))
  end
end
function getCurrentPopup()
  return gamelua.notificationsFrame:getChild("sync_popup")
end
ui = gamelua.ui
SyncPopup = ui.BGBox:inherit()
function SyncPopup:new(description, maxShowTime, o)
  local o = o or {}
  o.desc_text = description
  o.maxShowTime = maxShowTime or 30
  o.name = "sync_popup"
  return ui.BGBox.new(self, o)
end
function SyncPopup:init()
  ui.BGBox.init(self)
  self.components = {
    left = "ACHIEVEMENT_BG_LEFT",
    center = "ACHIEVEMENT_BG_MIDDLE",
    right = "ACHIEVEMENT_BG_RIGHT"
  }
  self.hanchor = "RIGHT"
  self.vanchor = "TOP"
  local spinner = ui.Image:new()
  spinner.name = "spinner"
  spinner:setImage("IN_APP_LOADING")
  self:addChild(spinner)
  self.spinner_angle = 0
  local description = ui.Text:new()
  description.name = "description"
  description.text = self.desc_text
  description:setFont(gamelua.getFontBasic)
  description.scaleX = 0.7
  description.scaleY = 0.7
  description.hanchor = "LEFT"
  description.group = ""
  self:addChild(description)
  local title = ui.Text:new()
  title.name = "title"
  title.text = "Saving to Rovio"
  title:setFont(gamelua.getFontBasic)
  title.scaleX = 0.7
  title.scaleY = 0.7
  self:addChild(title)
end
function SyncPopup:onEntry()
  ui.BGBox.onEntry(self)
  self.timer = 0
  self.state = "APPEAR"
end
function SyncPopup:leave()
  self.leaving = true
end
function SyncPopup:setText(text)
  local description = self:getChild("description")
  description.text = text
  description:clip()
  self:layout()
end
function SyncPopup:layout()
  ui.BGBox.layout(self)
  local title = self:getChild("title")
  local description = self:getChild("description")
  gamelua.setFont(description.font)
  local desc_h = _G.res.getFontHeight() * description.scaleY
  local desc_w = _G.res.getStringWidth(_G.res.getString("TEXTS_BASIC", description.text)) * description.scaleX
  gamelua.setFont(title.font)
  local title_w = _G.res.getStringWidth(_G.res.getString("TEXTS_BASIC", title.text)) * title.scaleX
  local bg_w, bg_h = _G.res.getSpriteBounds(self.components.right)
  local spinner = self:getChild("spinner")
  local spinner_w, spinner_h = _G.res.getSpriteBounds(spinner.image)
  self.width = _G.math.max(desc_w + spinner_w * 1.5, title_w)
  self.height = bg_h
  self.x = gamelua.screenWidth - bg_w
  self.y = gamelua.screenHeight + bg_h * 0.5
  title.x = -self.width * 0.5
  title.y = self.height * 0.23
  title:clip()
  description.x = self.width * -0.98
  description.y = self.height * 0.65
  description:clip()
  spinner.x = description.x + desc_w + spinner_w
  spinner.y = description.y
end
function SyncPopup:update(dt, time)
  ui.BGBox.update(self, dt, time)
  local _, box_h = _G.res.getSpriteBounds(self.components.right)
  self.timer = self.timer + dt
  if self.state == "APPEAR" then
    self.y = self.y - box_h * dt
    if self.y <= gamelua.screenHeight - box_h then
      self.y = gamelua.screenHeight - box_h
      self.state = "IDLE"
    end
  elseif self.state == "IDLE" then
    if self.timer >= 3 and self.leaving or self.timer > self.maxShowTime then
      self.state = "LEAVE"
    end
  elseif self.state == "LEAVE" then
    self.y = self.y + box_h * dt
    if self.y > gamelua.screenHeight + box_h then
      gamelua.notificationsFrame:removeChild(self)
    end
  end
  local spinner = self:getChild("spinner")
  self.spinner_angle = self.spinner_angle + dt * 10
  spinner.angle = _G.math.floor(self.spinner_angle / 0.5235983) * 0.5235983
end
filename = "CloudSync.lua"
