#include "mn_i_pcskin"
#include "mn_i_pwfunctions"
#include "prc_inc_skin"

// void main(){}
// Used for neccessary initializations of entering PC
void handleEnteringPC(object oPC);

string generateID()
{
   int id;

   id = GetCampaignInt("mn_cids", "cid");
   id++;
   SetCampaignInt("mn_cids", "cid", id);

   return IntToString(id);
}

void setupJournal(object oPC)
{
   if (GetIsDM(oPC))
   {
      // Add instructions (dm tools) and rules references.
      AddJournalQuestEntry( "info_dm", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_guidelines", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_rules", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_spells", 1, oPC, FALSE, FALSE, TRUE );
   }
   else
   {
      // Add complete rules references and latest news.
      AddJournalQuestEntry( "info_chars", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_new", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_guidelines", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_rules", 1, oPC, FALSE, FALSE, TRUE );
      AddJournalQuestEntry( "info_spells", 1, oPC, FALSE, FALSE, TRUE );

      // Add persistant quests
      // TODO: Move to java, this is highly inefficient
      /*
      string questID;
      int i;

      questID = "quest_0001";
      i = GetQuestStatus(oPC, questID);
      if (i != QUEST_UNKNOWN)
      {
         AddJournalQuestEntry( questID, i, oPC, FALSE, FALSE, TRUE);
      }

      questID = "quest_0002";
      i = GetQuestStatus(oPC, questID);
      if (i != QUEST_UNKNOWN)
      {
         AddJournalQuestEntry( questID, i, oPC, FALSE, FALSE, TRUE);
      }

      questID = "quest_0015";
      i = GetQuestStatus(oPC, questID);
      if (i != QUEST_UNKNOWN)
      {
         AddJournalQuestEntry( questID, i, oPC, FALSE, FALSE, TRUE);
      }

         questID = "quest_0016";
      i = GetQuestStatus(oPC, questID);
      if (i != QUEST_UNKNOWN)
      {
         AddJournalQuestEntry( questID, i, oPC, FALSE, FALSE, TRUE);
      }
      */
   }
}

void giveDMTools(object oPC)
{
   if (GetIsDM(oPC))
   {
   }
}

void setupLocalVariables(object oPC)
{
   // Add teleport blocking here also

   // Setup id
   object serverToken = GetItemPossessedBy( oPC, "MN_SERVERTOKEN" );
   string charid = GetLocalString( serverToken, "cid" );
   SetLocalString( oPC, "cid", charid );

   // Class blockers
   /*
   // Tjek om Palemaster skal blokeres
   if ( GetLevelByClass( CLASS_TYPE_PALEMASTER, oPC ) >= 9 ||
        GetLevelByClass( CLASS_TYPE_PALE_MASTER, oPC ) >= 9 )
   {
      SetLocalInt( oPC, "X2_AllowPalema", 1 );
   }
   else
   {
      DeleteLocalInt( oPC, "X2_AllowPalema" );
   }
   */

   // Bloker Shifter
   SetLocalInt( oPC, "X2_AllowShiftr", 1 );

   // Bloker Palemaster
   SetLocalInt( oPC, "X2_AllowPalema", 1 );

   // Bloker Shadowdancer
   SetLocalInt( oPC, "X1_AllowShadow", 1 );
}

void assignStartingEquipment(object oPC, string chosenClothes = "")
{
    int class = GetClassByPosition(1, oPC);

    // Clothes
    if (chosenClothes == "")
    {
       chosenClothes = "nw_cloth022";
    }
    object clothes = CreateItemOnObject(chosenClothes, oPC);
    AssignCommand(oPC,ActionEquipItem(clothes,INVENTORY_SLOT_CHEST));

    // Assign class based equipment
    switch (class)
    {
       case CLASS_TYPE_BARBARIAN:
          // Weapon
          CreateItemOnObject("nw_waxhn001", oPC); // Hand Axe
          // Armor
          CreateItemOnObject("nw_aarcl008", oPC); // Hide Armor
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 20);
       break;

       case CLASS_TYPE_BARD:
          // Weapon
          CreateItemOnObject("nw_wswdg001", oPC); // Dagger
          CreateItemOnObject("nw_wbwxl001", oPC); // Light Crossbow
          CreateItemOnObject("nw_wambo001", oPC, 99); // 99 Bolts
          // Armor
          CreateItemOnObject("nw_aarcl001", oPC); // Leather Armor
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          CreateItemOnObject("nw_it_sparscr109", oPC); // Scroll Magic Missile
          CreateItemOnObject("nw_it_sparscr108", oPC); // Scroll Sleep
          CreateItemOnObject("nw_it_sparscr102", oPC); // Scroll Prot. from Alignment
          CreateItemOnObject("nw_it_sparscr105", oPC); // Scroll Summon Creature 1
          // Gold
          GiveGoldToCreature(oPC, 100);
       break;

       case CLASS_TYPE_CLERIC:
          // Weapon
          CreateItemOnObject("nw_wblml001", oPC); // Mace
          // Armor
          CreateItemOnObject("nw_aarcl002", oPC); // Studded Leather Armor
          CreateItemOnObject("nw_aarcl002", oPC); // Small Shield
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 75);
       break;

       case CLASS_TYPE_DRUID:
          // Weapon
          CreateItemOnObject("nw_wspsc001", oPC); // Sickle
          // Armor
          CreateItemOnObject("nw_aarcl001", oPC); // Leather Armor
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 50);
       break;

       case CLASS_TYPE_FIGHTER:
          // Weapon
          CreateItemOnObject("nw_wswss001", oPC); // Short Sword
          // Armor
          CreateItemOnObject("nw_aarcl002", oPC); // Studded Leather Armor
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 50);
       break;

       case CLASS_TYPE_MONK:
          // Weapon
          CreateItemOnObject("nw_wspka001", oPC); // Kama
          // Armor
          CreateItemOnObject("nw_cl0th016", oPC); // Monk's Outfit
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 20);
       break;

       case CLASS_TYPE_PALADIN:
          // Weapon
          CreateItemOnObject("nw_wswss001", oPC); // Short Sword
          // Armor
          CreateItemOnObject("nw_aarcl002", oPC); // Studded Leather Armor
          CreateItemOnObject("nw_arhe001", oPC); // Pot Helmet
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 50);
       break;

       case CLASS_TYPE_RANGER:
          // Weapon
          CreateItemOnObject("nw_wswdg001", oPC); // Dagger
          CreateItemOnObject("nw_wswls001", oPC); // Long Sword
          CreateItemOnObject("nw_wswss001", oPC); // Short Bow
          CreateItemOnObject("nw_wamar001", oPC, 99); // 99 Arrows
          // Armor
          CreateItemOnObject("nw_aarcl001", oPC); // Leather Armor
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 50);
       break;

       case CLASS_TYPE_ROGUE:
          // Weapon
          CreateItemOnObject("nw_wswss001", oPC); // Short Sword
          CreateItemOnObject("nw_wswdg001", oPC); // Dagger
          // Armor
          CreateItemOnObject("nw_aarcl001", oPC); // Leather Armor
          // Misc items
          CreateItemOnObject("nw_it_picks001", oPC); // Thieves Tools +1
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          // Gold
          GiveGoldToCreature(oPC, 100);
       break;

       case CLASS_TYPE_SORCERER:
          // Weapon
          CreateItemOnObject("nw_wswdg001", oPC); // Dagger
          CreateItemOnObject("nw_wbwxl001", oPC); // Light Crossbow
          CreateItemOnObject("nw_wambo001", oPC); // 99 Bolts
          // Armor
          CreateItemOnObject("nw_cl0th008", oPC); // Sorcerer's Robe
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          CreateItemOnObject("nw_it_sparscr109", oPC); // Scroll Magic Missile
          CreateItemOnObject("nw_it_sparscr108", oPC); // Scroll Sleep
          CreateItemOnObject("nw_it_sparscr102", oPC); // Scroll Prot. from Alignment
          CreateItemOnObject("nw_it_sparscr105", oPC); // Scroll Summon Creature 1
          // Gold
          GiveGoldToCreature(oPC, 75);
       break;

       case CLASS_TYPE_WIZARD:
          // Weapon
          CreateItemOnObject("nw_wswdg001", oPC); // Dagger
          CreateItemOnObject("nw_wbwxl001", oPC); // Light Crossbow
          CreateItemOnObject("nw_wambo001", oPC, 99); // 99 Bolts
          // Armor
          CreateItemOnObject("nw_cl0th005", oPC); // Wizard's Robe
          // Misc items
          CreateItemOnObject("nw_it_mpotion001", oPC, 3); // 3 X Pot. Cure Light Wounds
          CreateItemOnObject("nw_it_torch001", oPC); // Torch
          CreateItemOnObject("nw_it_sparscr109", oPC); // Scroll Magic Missile
          CreateItemOnObject("nw_it_sparscr108", oPC); // Scroll Sleep
          CreateItemOnObject("nw_it_sparscr102", oPC); // Scroll Prot. from Alignment
          CreateItemOnObject("nw_it_sparscr105", oPC); // Scroll Summon Creature 1
          // Gold
          GiveGoldToCreature(oPC, 75);
       break;
    }

    // Assign to all new characters
    CreateItemOnObject("mn_savechar", oPC); // Character export widget
    CreateItemOnObject("dmfi_pc_emote", oPC); // Emote Wand
    // CreateItemOnObject("intimateemotewan", oPC); // Emote crystal
}

void stripCharacterOfItems(object oPC)
{
    string carryOnClothes = "";
    object clothing = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC);
    if(GetIsObjectValid(clothing))
    {
       string resrefOfClothes = GetResRef(clothing);
       if (GetStringLeft(resrefOfClothes, 8) == "nw_cloth")
       {
          carryOnClothes = resrefOfClothes;
       }
    }

    // Delete equipment
    ExecuteScript("mn_removeitems", oPC);

    // Init new equipment
    DelayCommand(1.0f,assignStartingEquipment(oPC, carryOnClothes));

}

void feedback( string message, object oPC )
{
    FloatingTextStringOnCreature(message,oPC,FALSE);
//      SendMessageToPC( oPC, message );
}

void handleInvalidChar( object oPC, string reason = "")
{
/*    feedback("Your character failed validation: "+reason+". Please contact a DM", oPC);
   SetLocalInt(oPC, "BLOCK_TELEPORT_PC", 15); // Block all teleportation
   SetCommandable( FALSE, oPC ); */
}

int validateStartingAbilities(object oPC)
{
   return TRUE;
}

int validateStartingSkills(object oPC)
{
   return TRUE;
}

int validateStartingFeats(object oPC)
{
   return TRUE;
}

int validateStartingHP(object oPC)
{
   return TRUE;
}

int validateStartingLevels(object oPC)
{
   return TRUE;
}

void removeSpecialMarkers(object oPC)
{
   SetPlotFlag(oPC, FALSE);
   SetImmortal(oPC, FALSE);
}

void removeTemporaryItemProperties(object obj)
{
   if (GetIsObjectValid(obj))
   {
      itemproperty property = GetFirstItemProperty(obj);
      while (GetIsItemPropertyValid(property))
      {
         if (GetItemPropertyDurationType(property) == DURATION_TYPE_TEMPORARY)
         {
            RemoveItemProperty( obj, property );
         }

         property = GetNextItemProperty(obj);
      }
   }
}

void clearItemTemporaryProperties(object oPC)
{
   object obj = GetFirstItemInInventory(oPC);
   while (GetIsObjectValid(obj))
   {
      removeTemporaryItemProperties(obj);
      obj = GetNextItemInInventory(oPC);
   }

   //Clear equipped items.
   int i;
   for(i = 0; i < NUM_INVENTORY_SLOTS; i++)
   {
       object oItem = GetItemInSlot(i, oPC);
       if(GetIsObjectValid(oItem))
       {
           removeTemporaryItemProperties(oItem);
       }
   }
}

int performBeginningValidations(object oPC)
{
   int success = TRUE;
   if (!validateStartingAbilities(oPC))
   {
      success = FALSE;
      handleInvalidChar(oPC, "Invalid Abilities");
   } else if (!validateStartingSkills(oPC))
   {
      success = FALSE;
      handleInvalidChar(oPC, "Invalid Skills");
   }
   else if (!validateStartingFeats(oPC))
   {
      success = FALSE;
      handleInvalidChar(oPC, "Invalid Feats");
   }
   else if (!validateStartingHP(oPC))
   {
      success = FALSE;
      handleInvalidChar(oPC, "Invalid HP");
   }
   else if (!validateStartingLevels(oPC))
   {
      success = FALSE;
      handleInvalidChar(oPC, "Invalid Levels");
   }
   return success;
}

void giveAlangaraStone(object oPC, string passkey = "")
{
   object serverToken = CreateItemOnObject( "mn_servertoken", oPC );
   if(passkey != "")
   {
      SetLocalString(serverToken, "passkey", passkey);
   }
   SetLocalString(serverToken, "cid", generateID());
}

string initCharInDB(object oPC)
{
   string passkey = "";

   return passkey;
}

void handleEnteringDM(object oPC)
{
   stripCharacterOfItems(oPC);
   giveDMTools(oPC);
   setupJournal(oPC);
}

void handleEnteringExistingPC(object oPC)
{
   removeSpecialMarkers(oPC);
   clearItemTemporaryProperties(oPC);
   updateSkin(oPC);
   setupLocalVariables(oPC);
   setupSpecialEffects(oPC);
   setupJournal(oPC);
}

void handleEnteringNewPC(object oPC)
{
   //stripCharacterOfItems(oPC);
   removeSpecialMarkers(oPC);
   removeEffects(oPC);
   if (performBeginningValidations(oPC))
   {
      // Character is validated correctly
      updateSkin(oPC);
      string passkey = initCharInDB(oPC);
      giveAlangaraStone(oPC, passkey);
      setupLocalVariables(oPC);
      setupSpecialEffects(oPC);
      SetXP(oPC, 1);
      ExportSingleCharacter( oPC );
      setupJournal(oPC);
   }
}

void handleTransitionExistingPC(object oPC)
{
   updateSkin(oPC);
   handleEnteringExistingPC(oPC);
}


int validatePCinDB (object oPC)
{
   if (!GetIsDM(oPC))
   {
      object token = GetItemPossessedBy( oPC, "MN_SERVERTOKEN" );
      if (GetIsObjectValid(token))
      {
         string localPasskey = GetLocalString(token, "passkey");
      }
      else
      {
         return FALSE;
      }
   }
   return TRUE;
}

void removeObsoleteItems(object oPC)
{
    // Remove rogue power item
    object power = GetItemPossessedBy(oPC, "mn_rogueboost");
    if (GetIsObjectValid(power))
    {
       SetPlotFlag(power, FALSE);
       DestroyObject(power);
    }

    power = GetItemPossessedBy(oPC, "mn_monkboost");
    if (GetIsObjectValid(power))
    {
       SetPlotFlag(power, FALSE);
       DestroyObject(power);
    }

}

int authenticateKey(object oPC)
{
   string sPName = GetStringLowerCase(GetPCPlayerName(oPC));
   string sKey = GetPCPublicCDKey(oPC);
   string sStoredKey = FetchPString("playerkeys", sPName, "key01");
   if (sStoredKey == "")
   {
      StorePString("playerkeys", sPName, "key01", sKey);
      feedback("Your account has been linked with your CD key", oPC);
      return TRUE;
   } else
   {
      if (sStoredKey==sKey)
      {
         return TRUE; // Key validated
      }
      else
      {
         string sLogmsg = "INCORRECT CD KEY DETECTED! Account: " + sPName + "; Charname: " +
                GetName(oPC) + "; CD Key: " + sKey + "; IP: " + GetPCIPAddress(oPC);

         WriteTimestampedLogEntry(sLogmsg);
         SendMessageToAllDMs(sLogmsg);
         return FALSE;
      }
   }

}

void handleEnteringPC(object oPC)
{
   if (GetIsPC(oPC) && GetIsObjectValid(oPC))
   {
      if (authenticateKey(oPC) == FALSE)
      {
         BootPC(oPC);
      }

      removeObsoleteItems(oPC);
      object serverToken = GetItemPossessedBy( oPC, "MN_SERVERTOKEN" );
      object pcSkin = GetPCSkin(oPC);
      if (GetIsDM(oPC) && validatePCinDB(oPC))
      {
         // Player is DM
         handleEnteringDM(oPC);
      } else if (GetXP(oPC) == 0 && !GetIsObjectValid(serverToken) && !GetIsObjectValid(pcSkin))
      {
         // New player - throrough validations and initialization
//         feedback("New PC", oPC);
         handleEnteringNewPC(oPC);
      }
      else if (GetXP(oPC) == 0)
      {
         // New character, but with items. This should not be possible (remember that min XP after XP loss should be 1, fix this in death script)
         handleInvalidChar(oPC, "Starting character had special items");
      }
      else
      {
         // Check for correct items
         if (GetIsObjectValid( serverToken ) && GetIsObjectValid( pcSkin ) && validatePCinDB(oPC))
         {
            // Player has correct items
//            feedback("Existing", oPC);
            handleEnteringExistingPC( oPC );
         }
         else if ( GetIsObjectValid( serverToken ) && !GetIsObjectValid( pcSkin ) )
         {
            // During beta only. Tweak to include new test-systems in condition above.
            // Right now, skin was added during development, so might be missing.
//            feedback("Transition", oPC);
            handleTransitionExistingPC( oPC );
         }
         else
         {
            // Player lacks the neccessary items for being a correct char, or
            // failed validation in other ways.
            handleInvalidChar(oPC, "Invalid Character Composition");
         }
      }

   }
}