/* 
    BattleRPG Copyright (C) 2007-2008 Nico de Vries.

    This file is part of BattleRPG.

    BattleRPG is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/.
*/

class BattlePersistentPlayerDatabase extends Object config (BattlePersistentPlayerDatabase) dependson(BattleStaticFunctions);
// We want to keep BattleRPG settings and player data separated.

/*
  How does data get to (and from) BattlePersistentPlayerDatabase:
  - The BattleRPG mutator has a BPD variable which is initialized in InitMutator
  - ModifyPlayer calls LoadPlayerData to load data from disk in the proper PRI (player replication info) as BPRIDATA (special PRI data for RPG) 
  - LoadPlayerData always checks to see if the database is newer or older then what is in memory to prevent data loss
  - LoadPlayerData also stores a link to the BPD in the PRI
  - When WriteBPRI is called to change the BPRIDATA in the PRI it also calls SavePlayerData with this BPD
*/

struct RECORD {
  var name ID;
  var int Level; 
  var int XP;
  var int NeededXP;
};

var config array<RECORD> DATABASE;

var BattleRPG BRPG; // Keep a link to the mutator.
var BattlePersistentPlayerBuildDatabase BPBD; // Keep a link to the player build database.
var BattlePersistentPlayerBackupDatabase BAK; // Keep a link to the backup database.
var FileWriter FW;

function LogDataOp (String logline)
{
  if (FW != None) {
    `Log ("LogDataOp:"@logline);
    FW.Logf (logline);
  }
}

static function name PRI2Name (PlayerReplicationInfo PRI)
{
  return name (PRI.PlayerName);
//  return name (ID.Uid[0]$ID.Uid[1]$ID.Uid[2]$ID.Uid[3]$ID.Uid[4]$ID.Uid[5]$ID.Uid[6]$ID.Uid[7]);
} 

function Commit ()
{
  SaveConfig(); // Save the database to disk (useful if the server decides to crash)
  if (BPBD!=None) BPBD.Commit();
  if (BAK!=None) BAK.Commit();
}

function SavePlayerData (PlayerReplicationInfo PRI)
{
  local int index;
  local BPRIDATA BD;
  local RECORD R;
  local name ID;

  ID = PRI2NAME (PRI); 
  BD = class'BattleStaticFunctions'.static.ReadBPRI (PRI);
  BAK.SavePlayerLevelBackup (ID, BD.Level);
  index = DATABASE.Find ('ID', ID); // Let's hope Epic made this with hashing
  if (index==-1) { // insert new record
    LogDataOp ("SAVE INSERT ("$ID$")"@BD.Level@BD.XP);
    R.ID = ID;
    R.Level = BD.Level;
    R.XP = BD.XP;
    R.NeededXP = BD.NeededXP;
    DATABASE.AddItem (R);
  } else { // update existing record
    if (BD.Level < 1000) {
      if (BD.Level >= DATABASE[index].Level) { // Always keep the highest level, e.g. to prevent "out of order" data loss
        LogDataOp ("SAVE UPDATE ("$ID$") DB:"$DATABASE[index].Level@DATABASE[index].XP@"MEM:"$BD.Level@BD.XP);
        DATABASE[index].Level = BD.Level;
        DATABASE[index].XP = BD.XP;
        DATABASE[index].NeededXP = BD.NeededXP;
      } else { // To prevent data loss we do a LoadPlayerData now because somehow the database seems to have the newer data
        LogDataOp ("SAVE SKIP ("$ID$") DB:"$DATABASE[index].Level@DATABASE[index].XP@"MEM:"$BD.Level@BD.XP);
        LoadPlayerData (PRI);
      }
    }
  } 
}

function LoadPlayerData (PlayerReplicationInfo PRI)
{
  local int index;
  local BPRIDATA BD;
  local name ID;
  local int LevelBackup;

  ID = PRI2NAME (PRI); 
  BD = class'BattleStaticFunctions'.static.ReadBPRI (PRI);
  index = DATABASE.Find ('ID', ID); // Let's hope Epic made this with hashing
  if (index != -1) {
    if (DATABASE[index].Level >= BD.Level) { // use the latest data
      LogDataOp ("LOAD UPDATE ("$ID$") DB:"$DATABASE[index].Level@DATABASE[index].XP@"MEM:"$BD.Level@BD.XP);
      BD.Level = DATABASE[index].Level;
      BD.XP = DATABASE[index].XP;
      BD.NeededXP = DATABASE[index].NeededXP;
    } else {
      LogDataOp ("LOAD SKIP ("$ID$") DB:"$DATABASE[index].Level@DATABASE[index].XP@"MEM:"$BD.Level@BD.XP);
    }
  } else {
    LogDataOp ("LOAD NOTFOUND ("$ID$")");
  }
  BD.BPD = self; // Make sure we can always reach BPD
  LevelBackup = BAK.LoadPlayerLevelBackup (ID);
  if (LevelBackup > BD.Level) BD.Level = LevelBackup;
  class'BattleStaticFunctions'.static.WriteBPRI (PRI, BD);
}

defaultproperties
{
  Name="BattlePersistentPlayerDatabase"
}