// abilities.js

// Maintains the ability scores.

// Dependencies:
//    everything...almost

// void OnAbilityChanged(node)
// Called by the document when an ability score is changed.
function OnAbilityChanged(node)
{
  if (disable_autocalc())
    return;
  // If the score was deleted, delete the modifier...
  if (String(node.value).length == 0)
  {
    sheet()[node.name + "Mod"].value = "";
    sheet()[node.name + "Base"].value = "";
    debug.trace("Cleared the " + node.name + " modifier.");
  }
  // ...otherwise calculate the modifier.
  else
  {
    sheet()[node.name + "Mod"].value = _calcMod(sheet()[node.name].value);
    sheet()[node.name + "Base"].value = (Clean(sheet()[node.name + "Mod"].value) + Math.floor(sheet().Level.value / 2));
    debug.trace("Set the " + node.name + " modifier.");
  }

  // Attempt to call the abilitys apply method (extract the ability
  // name from the first three characters of the node name.
  _applyAbility(node.name);
}

//////////////////////////////////////////////////////////////////////////
// Internal functions

// void _applyAbility(string)
// Applies an ability modifier to all dependent objects.
function _applyAbility(nodename)
{
  // Determine the name of the ability we  are working with.
  var ability = String(nodename).substr(0, 3);

  // Are we dealing with a temp ability?
  var isTemp = String(nodename).length > 3 ? true : false;

  // Now cascade through the sheet with the proper modifiers.
  if (isTemp)
  {
    // Determine which value we will be applying (apply the normal mod if
    // the temp mod was cleared).
    var val = String(sheet()[ability + "TempMod"].value).length > 0
      ? sheet()[ability + "TempMod"].value
      : sheet()[ability + "Mod"].value;
    // Attempt to update the dependencies.
    eval("_calcDep" + ability + "('" + val + "')");
  }
  else
  {
    // Only apply the change if a temp mod does not exist.
    //if (String(sheet()[ability + "TempMod"].value).length > 0)
      //debug.trace(ability + "Temp exists: ignoring changes to " + ability + ".");
    //else
      eval("_calcDep" + ability + "('" + sheet()[ability + "Base"].value + "')");
  }
  
}

// void _calcDepStr(string)
// Calculates the strength dependencies, using the supplied modifier
// to fill in any slots that use a strength modifier.
function _calcDepStr(mod)
{
  debug.trace("Updating all Str dependencies...");

  // Remove the leading "+" if it exists.
  mod = Clean(mod);

  // The melee attack bonus.
  var StrMod = Clean(sheet().StrMod.value);
  sheet().MABStr.value = StrMod;
  //sheet().GABStr.value = mod;
  MBABCalc();
  FortCalc();
  //GBABCalc();

  if (parseInt(sheet().Str.value) > parseInt(sheet().Con.value))
  {
    sheet().FortAbility.value = StrMod;
  }
  else
  {
    sheet().FortAbility.value = Clean(sheet().ConMod.value);
  }

  FortCalc();
  
  // The carrying capacities.
  //SetLoads();

  // Update the skills.
  _calcSkills('str');

}

// void _calcDepCon(string)
// Calculate the constitution dependencies, using the supplied modifier
// to fill in any slots that use a constitution modifier.
function _calcDepCon(mod)
{
  debug.trace("Updating all Con dependencies...");

  mod = Clean(mod);

  if (parseInt(sheet().Con.value) > parseInt(sheet().Str.value))
  {
    sheet().FortAbility.value = Clean(sheet().ConMod.value);
  }
  else
  {
    sheet().FortAbility.value = Clean(sheet().StrMod.value);
  }

  FortCalc();
  
  // Update the skills.
  _calcSkills('con');
}

// void _calcDepDex(string)
// Calculate the dexterity dependencies, using the supplied modifier
// to fill in any slots that use a dexterity modifier.
function _calcDepDex(mod)
{
  debug.trace("Updating all Dex dependencies...");

  // Remove the leading "+" from the mod if it exists.
  mod = Clean(mod);

  // The ranged attack bonus.
  var DexMod = Clean(sheet().DexMod.value);
  sheet().RABDex.value = DexMod;
  RBABCalc();

  if (sheet().Armor1Type.value.toLowerCase() != 'heavy')
  {
    if (parseInt(sheet().Dex.value) > parseInt(sheet().Int.value))
    {
      sheet().ACAbility.value = DexMod;
      sheet().RefAbility.value = DexMod;
    }
    else
    {
      var IntMod = Clean(sheet().IntMod.value);
      sheet().ACAbility.value = IntMod;
      sheet().RefAbility.value = IntMod;
    }    
  }

  ACCalc();
  RefCalc();
  
  // The initiative.
  sheet().InitDex.value = mod;
  InitCalc();


  // Update the skills.
  _calcSkills('dex');
}
  score /= 2;

// void _calcDepInt(string)
// Calculate the intelligence dependencies, using the supplied modifier
// to fill in any slots that use an intelligence modifier.
function _calcDepInt(mod)
{
  debug.trace("Updating all Int dependencies...");
  
  if (sheet().Armor1Type.value.toLowerCase() != 'heavy')
  {
    if (parseInt(sheet().Int.value) > parseInt(sheet().Dex.value))
    {
      var IntMod = Clean(sheet().IntMod.value);
      sheet().ACAbility.value = IntMod;
      sheet().RefAbility.value = IntMod;
    }
    else
    {
      var DexMod = Clean(sheet().DexMod.value);
      sheet().ACAbility.value = DexMod;
      sheet().RefAbility.value = DexMod;
    }    
  }

  ACCalc();
  RefCalc();

  // Update the skills.
  _calcSkills('int');
}

// void _calcDepWis(string)
// Calculate the wisdom dependencies, using the supplied modifier
// to fill in any slots that use a wisdom modifier.
function _calcDepWis(mod)
{
  debug.trace("Updating all Wis dependencies...");

  mod = Clean(mod);

  if (parseInt(sheet().Wis.value) > parseInt(sheet().Cha.value))
  {
    sheet().WillAbility.value = Clean(sheet().WisMod.value);
  }
  else
  {
    sheet().WillAbility.value = Clean(sheet().ChaMod.value);
  }

  WillCalc();
  
  // Update the skills
  _calcSkills('wis');
}

// void _calcDepCha(string)
// Calculate the charisma dependencies, using the supplied modifier
// to fill in any slots that use a charisma modifier.
function _calcDepCha(mod)
{
  debug.trace("Updating all Cha dependencies...");
  
  if (parseInt(sheet().Cha.value) > parseInt(sheet().Wis.value))
  {
    sheet().WillAbility.value = Clean(sheet().ChaMod.value);
  }
  else
  {
    sheet().WillAbility.value = Clean(sheet().WisMod.value);
  }

  WillCalc();
  
  //Update the skills
  _calcSkills('cha');
}

// string _calcMod(string)
// Returns the modifier for a supplied ability score. The return value is
// a string with a leading "+" sign if the modifier is positive.
function _calcMod(abilityScore)
{
  // Ensure a numeric value.
  var score = parseInt(abilityScore);
  if (isNaN(score))
    return "--";

  // Calculate the modifier.
  if ((score % 2) == 1)
    score -= 11;
  else
    score -= 10;
  score /= 2;

  // Append a "+" sign if positive
  if (score > 0)
    score = "+" + score;

  // Return the modifier.
  return score;
}

// bool _isTempAbility(node)
// Returns true if the supplied node is a temporary ability score.
function _isTempAbility(node)
{
  var names = new Array("Str", "Dex", "Con", "Int", "Wis", "Cha");
  for (var i in names)
    if (node.name == names[i])
      return false;
  return true;
}
