// Our army class
// Holds information for our army list
function GameLevelClass(name, casters, points)
{
    this.name = name;
    this.numCasters = casters;
    this.points = points;
}

GameLevelClass.prototype.PointsString = function()
{
    return this.points + " pts.";
}

GameLevelClass.prototype.FullTitle = function()
{
    return this.name + " " + this.points + " pts. (" + this.numCasters + " Caster)";
}

GameLevelClass.prototype.GroupAndNumCastersString = function()
{
    return this.name + " (" + this.numCasters + " Caster)";
}

GameLevelClass.prototype.ToXML = function()
{
    var xmlStr = "<game><gameType>" + this.name + "</gameType>";
    xmlStr += "<casters>" + this.numCasters + "</casters>";
    xmlStr += "<points>" + this.points + "</points></game>";
    return xmlStr;
}

function FactionClass( name, filename, iconFilename )
{
    this.name = name;
    this.filename = filename;
    this.iconFilename = iconFilename;
}

FactionClass.prototype.ToXML = function()
{
    var xmlStr = "<faction><factionName>" + this.name + "</factionName>";
    xmlStr += "<factionFile>" + this.filename + "</factionFile>";
    xmlStr += "<iconFile>" + this.iconFilename + "</iconFile></faction>";
    return xmlStr;
}

function ContractClass(name)
{
    this.name = name;

}

ContractClass.prototype.ToXML = function()
{
    var xmlStr = "<contract><contractName>" + this.name + "</contractName></contract>";
    return xmlStr;
}

function TierClass(name, level)
{
    this.name = name;
    this.level = level;
}

TierClass.prototype.ToXML = function()
{
    var xmlStr = "<tier><tierName>" + this.name + "</tierName>";
    xmlStr += "<tierLevel>" + this.filename + "</tierLevel></tier>";
    return xmlStr;
}

function ModelGroup(name, collapse)
{
    this.name = name;
    this.collapse = collapse;
}

function ArmyInfoClass()
{
    this.title = "";
    this.faction = new FactionClass();
    this.contract = new ContractClass();
    this.gameLevel = new GameLevelClass();
    this.availableModels = new Array();
    this.modelGroups = new Array(); //warcaster, warjack, unit, etc...
    this.armyList = new Array();
    this.tier = new TierClass();
}

ArmyInfoClass.prototype.ToXML = function()
{
    xmlStr = "<gizmoList><version>1.0</version>";
    xmlStr += "<title>" + this.title + "</title>";
    xmlStr += this.faction.ToXML();
    if (this.contract.name)
    {
        xmlStr += this.contract.ToXML();
    }
    xmlStr += this.gameLevel.ToXML();
    if (this.tier.name)
    {
        xmlStr += this.tier.ToXML();
    }
    ///loop over army list and add models
    for (var i = 0; i < this.armyList.length; i++)
    {
        xmlStr += this.armyList[i].ToXML();
    }
    xmlStr += "</gizmoList>";
    return xmlStr;
}

ArmyInfoClass.prototype.ArmyPoints = function()
{
    var pointTotal = 0;
    var haveWarjackOrBeast = false;
    for (var i = 0; i < this.armyList.length; i++)
    {
        if (this.armyList[i].type === "warbeast" || this.armyList[i].type === "warjack")
        {
            haveWarjackOrBeast = true;
            break;
        }
    }
    for (var i = 0; i < this.armyList.length; i++)
    {
        if (this.armyList[i].type === "warcaster" || this.armyList[i].type === "warlock")
        {
            if (haveWarjackOrBeast)
            {
                pointTotal -= Number(this.armyList[i].pointCost);
            }
        }
        else
        {
            if (this.armyList[i].isUnit && this.armyList[i].isMax)
            {
                pointTotal += Number(this.armyList[i].maxCost);
            }
            else
            {
                pointTotal += Number(this.armyList[i].pointCost);
            }
        }
    }
    return pointTotal;
}

ArmyInfoClass.prototype.CasterCount = function()
{
    var casterCount = 0;
    for (var i = 0; i < this.armyList.length; i++)
    {
        if (this.armyList[i].type === "warcaster" || this.armyList[i].type === "warlock")
        {
            //alert(oArmyInfo.armyList[i].type);
            casterCount++;
        }
    }
    return casterCount;
}

ArmyInfoClass.prototype.HaveMercCaster = function()
{
    for (var i = 0; i < this.armyList.length; i++)
    {
        //alert(this.armyList[i].faction);
        if (this.armyList[i].type === "warcaster" && this.armyList[i].faction === "Mercenary")
        {
            return true;
        }
    }
    return false;
}

ArmyInfoClass.prototype.IsWarmachine = function()
{
    return (
    this.faction.name === "Cryx" ||
    this.faction.name === "Cygnar" ||
    this.faction.name === "Khador" ||
    this.faction.name === "Protectorate of Menoth" ||
    this.faction.name === "Retribution of Scyrah");
}

ArmyInfoClass.prototype.IsHordes = function()
{
    return (
    this.faction.name === "Circle Orboros" ||
    this.faction.name === "Legion of Everblight" ||
    this.faction.name === "Skorne" ||
    this.faction.name === "Trollbloods");
}

ArmyInfoClass.prototype.IsGroupCollapsed = function(faction, group)
{
    var modelGroups = this.modelGroups[faction];
    for (var i = 0; i < modelGroups.length; i++)
    {
        //
        if (modelGroups[i].name === group)
        {
            //alert(modelGroups[i].name);
            return modelGroups[i].collapse;
        }
    }
    return false;
}

//////////////////////////////////////////////////////
//Our view class - responsible for:
//
//      -displaying dialogs
//      -updating dialogs
//      -presenting the input data
//      -presenting the calculated data
//
//////////////////////////////////////////////////////
//our html ids
var ARMY_TITLE_INPUT = "army-title-input";
var FACTION_SELECT = "faction-select";
var CONTRACT_AREA = "contract-area";
var CONTRACT_SELECT = "contract-select";
var GAME_LEVEL_SELECT = "game-level-select";
var TIER_SELECT = "tier-select";

function ArmyInfoViewClass()
{
    var m_htmlStr = "";
    var collapseGroups = new Array();
}

//should be renamed....only creating contact and tier select boxes here
ArmyInfoViewClass.prototype.CreateDialog = function(divID, factions, contracts, gameLevels, oArmyInfo)
{
    var htmlStr = "";
    htmlStr += '<table class="contract-select" style="border:none">';
    htmlStr += '<tr>';
    //contract drop down
    htmlStr += '<td ><select style="width:160px" id="' + CONTRACT_SELECT + '" onchange="UpdateArmyInfo();">';
    for (var i = 0; i < contracts.length; i++)
    {
        if (contracts[i].name === oArmyInfo.contract.name)
        {
            htmlStr += '<option selected="yes">' + contracts[i].name + '</option>';
        }
        else
        {
            htmlStr += '<option>' + contracts[i].name + '</option>';
        }
    }
    htmlStr += '</select>';
    document.getElementById(CONTRACT_AREA).innerHTML = htmlStr; 
    
    this.UpdateContractAndTierView(oArmyInfo);
    this.UpdateFactionSelect("faction", factions, oArmyInfo);
    this.UpdateGameLevelSelect("game-level", gameLevels, oArmyInfo );
}

ArmyInfoViewClass.prototype.UpdateContractAndTierView = function(oArmyInfo)
{
    var contractList = document.getElementById(CONTRACT_AREA);
    //var tierList = document.getElementById(TIER_SELECT);
    if (oArmyInfo.faction.name !== "Mercenaries")
    {
        //contractList.disabled = true;
        contractList.style.display = 'none';
        //tierList.style.display = 'none';
    }
    else
    {
        //contractList.disabled = false;
        contractList.style.display = 'block';
        //tierList.style.display = 'block';
    }
}

ArmyInfoViewClass.prototype.UpdateFactionSelect = function(divID, factions, oArmyInfo)
{
    //fill the faction select table
    var htmlStr = "";
    htmlStr += '<table class="faction-select">';
    htmlStr += '<tr class="model-header">';
    var userName = getCookie("GizmoUserName");
    if (userName !== "")
    {
        htmlStr += "<th style='align:left;' colspan='3'><b>" + userName + "</b> | ";
        htmlStr += "<a href=GizmoSignOut.php?destination=Gizmo.html>Sign Out</a></th>";
    }
    else
    {
        htmlStr += '<th colspan="3"></th> ';
    }
    htmlStr += '<th colspan="9"></th>';
    htmlStr += '</tr>';
    htmlStr += '<tr>';
    for (var i = 0; i < factions.length; i++)
    {
        if (factions[i].name === oArmyInfo.faction.name)
        {
            //alert("selected");
            htmlStr += '<td class="faction-select-selected" ><img src="factions/' + factions[i].iconFilename + '" /></td>';
        }
        else
        {
            htmlStr += "<td onclick='javascript:UpdateFaction(\"" + factions[i].name + "\");'><img src='factions/" + factions[i].iconFilename + "' /></td>";
        }
    }
    htmlStr += '</tr>';
    htmlStr += '<tr>';
    for (var i = 0; i < factions.length; i++)
    {
        if (factions[i].name === oArmyInfo.faction.name)
        {
            htmlStr += '<td class="faction-select-selected" >' + factions[i].name + '</td>';
        }
        else
        {
            htmlStr += "<td onclick='javascript:UpdateFaction(\"" + factions[i].name + "\");'>" + factions[i].name + "</td>";
        }
    }
    htmlStr += '</tr>';
    htmlStr += '</table>';
    document.getElementById(divID).innerHTML = htmlStr;
}

ArmyInfoViewClass.prototype.UpdateGameLevelSelect = function(divID, gameLevels, oArmyInfo)
{
    //fill the faction select table
    var htmlStr = "";
    htmlStr += '<table class="game-level-select">';
    htmlStr += '<tr class"model-header">';
    if (gameLevels.length <= 0)
    {
        return;
    }
    var currentNameGroup = "";
    for (var i = 0; i < gameLevels.length; i++)
    {
        //add a new column group
        if (currentNameGroup !== gameLevels[i].name)
        {
            currentNameGroup = gameLevels[i].name;
            var colspan = 0;
            for (var j = 0; j < gameLevels.length; j++)
            {
                if (gameLevels[j].name === currentNameGroup)
                {
                    colspan += 1;
                }
            }
            htmlStr += '<th colspan="' + Number(colspan) + '" >' + gameLevels[i].GroupAndNumCastersString() + '</th>';
        }

    }
    htmlStr += '</tr>';
    htmlStr += '<tr>';
    currentNameGroup = "";
    for (var i = 0; i < gameLevels.length; i++)
    {
        htmlStr += "<td ";
        if (currentNameGroup !== gameLevels[i].name)
        {
            currentNameGroup = gameLevels[i].name;
            htmlStr += " style='border-left:solid thin;' "; 
        }
        if( oArmyInfo.gameLevel === gameLevels[i] )
        {
            htmlStr += " class='game-level-select-selected' "
        }
        htmlStr += " onclick='javascript:UpdateGameLevel(\"" + i + "\");'>" + gameLevels[i].points + "</td>";
    }
    htmlStr += '</tr>';
    htmlStr += '</table>';
    document.getElementById(divID).innerHTML = htmlStr;
}

ArmyInfoViewClass.prototype.UpdateAvailableModelList = function(divID, oArmyInfo)
{
    var currentFaction = oArmyInfo.faction.name;
    var currentContract = oArmyInfo.contract.name;
    var availableModels = oArmyInfo.availableModels[currentFaction];
    var casterCount = oArmyInfo.CasterCount();
    if (availableModels == null)
    {
        alert("Faction not found");
        return;
    }

    this.m_htmlStr = "<table class='model-list'>";
    this.m_htmlStr += "<tbody>";

    var currentType = "";
    for (var i = 0; i < availableModels.length; i++)
    {
        var bIsCaster = (availableModels[i].type === "warcaster" || availableModels[i].type === "warlock");
        if (casterCount <= 0 && !bIsCaster)
        {
            continue;
        }
        if (casterCount === oArmyInfo.gameLevel.numCasters && bIsCaster)
        {
            continue;
        }
        //if( casterCount > 0 && isDual && (availableModels[i].type !== "Warjack" && availableModels[i].type !== "Warbeast"))
        //{
        //    continue;
        //}
        var groupCollapsed = oArmyInfo.IsGroupCollapsed(currentFaction, currentType);
        if (currentType !== availableModels[i].type)
        {
            //starting a new model type so add in a new model group row
            var niceTypeName = availableModels[i].NiceTypeName();
            currentType = availableModels[i].type;
            groupCollapsed = oArmyInfo.IsGroupCollapsed(currentFaction, currentType);
            if (bIsCaster)
            {
                if (casterCount <= 0)
                {
                    this.m_htmlStr += "<tr class'model-header'><th class='type'>Choose Your Caster</th>";
                }
                else
                {
                    this.m_htmlStr += "<tr class'model-header'><th class='type'>" + niceTypeName + "</th>";
                }
            }
            else
            {
                this.m_htmlStr += "<tr class'model-header' ondblclick=\"ToggleCollapse('" + currentFaction + "','" + currentType + "');\"><th class='type'>" + niceTypeName + "</th>";
            }
            this.m_htmlStr += "<th class='points'>Points</th>";
            this.m_htmlStr += "<th class='fa'>FA</th>";
            //this.m_htmlStr += "<th class='add'></th>";
            if (bIsCaster)
            {
                this.m_htmlStr += "<th class='add'></th>";
            }
            else if (groupCollapsed === true)
            {
                this.m_htmlStr += "<th class='add'><a href='javascript:ToggleCollapse(\"" + currentFaction + "\",\"" + currentType + "\");'><img src='images/expand.png'></a></th>";
            }
            else
            {
                this.m_htmlStr += "<th class='add'><a href='javascript:ToggleCollapse(\"" + currentFaction + "\",\"" + currentType + "\");'><img src='images/collapse.png'></a></th>";
            }
            this.m_htmlStr += "</tr>";
        }
        var excluded = false;
        if (groupCollapsed)
        {
            excluded = true;
        }
        var excludeFaction = availableModels[i].excludeFaction;
        if (excluded === false && currentFaction === "Mercenaries")
        {
            for (var j = 0; j < excludeFaction.length; j++)
            {
                if (excludeFaction[j] === currentContract)
                {
                    excluded = true;
                }
            }
        }
        if (!excluded)
        {
            this.FilterAvailableModels(availableModels[i], currentFaction, i);
        }
    }
    //filter merc list if necessary
    var bShowMercOrMinionCastersAndJacks = (oArmyInfo.gameLevel.numCasters > 1);
    if (oArmyInfo.IsWarmachine() && casterCount > 0)
    {
        var mercenaries = oArmyInfo.availableModels["Mercenaries"];
        var groupCollapsed = oArmyInfo.IsGroupCollapsed("Mercenaries", currentType);
        if (mercenaries != null)
        {
            for (var i = 0; i < mercenaries.length; i++)
            {
                if (!bShowMercOrMinionCastersAndJacks &&
                (mercenaries[i].type === "warcaster" || mercenaries[i].type === "warlock" ||
                mercenaries[i].type === "warjack" || mercenaries[i].type === "warbeast"))
                {
                    continue;
                }
                var bIsCaster = (mercenaries[i].type === "warcaster" || mercenaries[i].type === "warlock");
                if (casterCount === oArmyInfo.gameLevel.numCasters && bIsCaster)
                {
                    continue;
                }
                if (currentType !== mercenaries[i].type)
                {
                    //starting a new model type so add in a new model group row
                    var niceTypeName = mercenaries[i].NiceTypeName();
                    currentType = mercenaries[i].type;
                    groupCollapsed = oArmyInfo.IsGroupCollapsed("Mercenaries", currentType);
                    if (bIsCaster)
                    {
                        this.m_htmlStr += "<tr class'model-header'><th class='type'>Mercenary " + niceTypeName + "</th>";
                    }
                    else
                    {
                        this.m_htmlStr += "<tr class'model-header' ondblclick=\"ToggleCollapse('Mercenaries','" + currentType + "');\"><th class='type'>Mercenary " + niceTypeName + "</th>";
                    }
                    //this.m_htmlStr += "<tr><th class='type'>Mercenary " + currentType + "</th>";
                    this.m_htmlStr += "<th class='points'>Points</th>";
                    this.m_htmlStr += "<th class='fa'>FA</th>";
                    //this.m_htmlStr += "<th class='add'></th>";
                    if (bIsCaster)
                    {
                        this.m_htmlStr += "<th class='add'></th>";
                    }
                    else if (groupCollapsed === true)
                    {
                        this.m_htmlStr += "<th class='add'><a href='javascript:ToggleCollapse(\"Mercenaries\",\"" + currentType + "\");'><img src='images/expand.png'></a></th>";
                    }
                    else
                    {
                        this.m_htmlStr += "<th class='add'><a href='javascript:ToggleCollapse(\"Mercenaries\",\"" + currentType + "\");'><img src='images/collapse.png'></a></th>";
                    }
                    this.m_htmlStr += "</tr>";
                }
                var excluded = false;
                if (groupCollapsed)
                {
                    excluded = true;
                }
                var excludeFaction = mercenaries[i].excludeFaction;
                for (var j = 0; j < excludeFaction.length; j++)
                {
                    if (excludeFaction[j] === currentFaction)
                    {
                        excluded = true;
                        break;
                    }
                }
                //ignore merc casters and jacks if it's a 1 caster game 
                if (!excluded)
                {
                    this.FilterAvailableModels(mercenaries[i], "Mercenaries", i);
                }
            }
        }
    }
    //now filter minion list if necessary
    if (oArmyInfo.IsHordes() && casterCount > 0)
    {
        var minions = oArmyInfo.availableModels["Minions"];
        var groupCollapsed = oArmyInfo.IsGroupCollapsed("Minions", currentType);
        if (minions != null)
        {
            for (var i = 0; i < minions.length; i++)
            {
                //ignore merc casters and jacks if it's a 1 caster game
                if (!bShowMercOrMinionCastersAndJacks &&
                    (minions[i].type === "warcaster" || minions[i].type === "warlock" ||
                    minions[i].type === "warjack" || minions[i].type === "warbeast"))
                {
                    continue;
                }
                var bIsCaster = (minions[i].type === "warcaster" || minions[i].type === "warlock");
                if (casterCount === oArmyInfo.gameLevel.numCasters && bIsCaster)
                {
                    continue;
                }
                if (currentType !== minions[i].type)
                {
                    //starting a new model type so add in a new model group row
                    var niceTypeName = minions[i].NiceTypeName();
                    currentType = minions[i].type;
                    groupCollapsed = oArmyInfo.IsGroupCollapsed("Minions", currentType);
                    if (bIsCaster)
                    {
                        this.m_htmlStr += "<tr class'model-header'><th class='type'>Minion " + niceTypeName + "</th>";
                    }
                    else
                    {
                        this.m_htmlStr += "<tr class'model-header' ondblclick=\"ToggleCollapse('Minions','" + currentType + "');\"><th class='type'>Minion " + niceTypeName + "</th>";
                    }
                    //this.m_htmlStr += "<tr><th class='type'>Minion " + currentType + "</th>";
                    this.m_htmlStr += "<th class='points'>Points</th>";
                    this.m_htmlStr += "<th class='fa'>FA</th>";
                    //this.m_htmlStr += "<th class='add'></th>";
                    if (bIsCaster)
                    {
                        this.m_htmlStr += "<th class='add'></th>";
                    }
                    else if (groupCollapsed === true)
                    {
                        this.m_htmlStr += "<th class='add'><a href='javascript:ToggleCollapse(\"Minions\",\"" + currentType + "\");'><img src='images/expand.png'></a></th>";
                    }
                    else
                    {
                        this.m_htmlStr += "<th class='add'><a href='javascript:ToggleCollapse(\"Minions\",\"" + currentType + "\");'><img src='images/collapse.png'></a></th>";
                    }
                    this.m_htmlStr += "</tr>";
                }
                var excluded = false;
                if (groupCollapsed)
                {
                    excluded = true;
                }
                var excludeFaction = minions[i].excludeFaction;
                for (var j = 0; j < excludeFaction.length; j++)
                {
                    if (excludeFaction[j] === currentFaction)
                    {
                        excluded = true;
                        break;
                    }
                }
                if (!excluded)
                {
                    this.FilterAvailableModels(minions[i], "Minions", i);
                }
            }
        }
    }

    this.m_htmlStr += "</tbody></table>";
    document.getElementById(divID).innerHTML = this.m_htmlStr;
}

ArmyInfoViewClass.prototype.UpdateArmyList = function(divID, oArmyInfo)
{
    var armyPoints = Number(oArmyInfo.ArmyPoints());
    var gamePoints = Number(oArmyInfo.gameLevel.points);
    var armyList = oArmyInfo.armyList;

    if (armyList === null)
    {
        alert("ArmyInfoViewClass.UpdateArmyList: Army List Empty");
        return;
    }

    //if (armyList.length <= 0)
    //{
    //    document.getElementById(divID).innerHTML = "";
    //    return;
    //}
    var htmlStr = "<table class='model-list'><thead><tr>";
    //    if (armyList.length <= 0)
    //    {
    //        if (oArmyInfo.IsWarmachine() || oArmyInfo.faction.name === "Mercenaries")
    //        {
    //            htmlStr += "<th class='army-points'>Choose your Warcaster</th>";
    //        }
    //        else
    //        {
    //            htmlStr += "<th class='army-points'>Choose your Warlock</th>";
    //        }
    //        htmlStr += "<th class='points'></th>"; //points
    //        htmlStr += "<th class='checkbox'></th>"; //checkbox holder
    //        htmlStr += "<th class='add'></th>"; //delete button
    //        htmlStr += "</tr></thead><tbody></tbody></table>";
    //        document.getElementById(divID).innerHTML = htmlStr;
    //        return;
    //    }

    htmlStr += "<th class='army-points'>" + oArmyInfo.faction.name + " - " + oArmyInfo.gameLevel.name + "</td>";
    htmlStr += "<th colspan='3' class='points'>Points: " + armyPoints + "/" + oArmyInfo.gameLevel.points + "</th>"; //points
    //htmlStr += "<th class='checkbox'></th>"; //checkbox holder
    //htmlStr += "<th class='add'></th>"; //delete button
    htmlStr += "</tr></thead><tbody>";

    //title
    /*if (armyList.length > 0)
    {
        
    }
    */
    //alert("Update mercs: view class");
    for (var i = 0; i < armyList.length; i++)
    {
        htmlStr += "<tr>";
        //htmlStr += "<td>" + armyList[i].type + "</td>";

        if (armyList[i].isUnit)
        {
            //alert(armyList[i].maxCost);
            if (armyList[i].maxCost === "0")
            {
                htmlStr += "<td class='army-points'>" + armyList[i].name + " <span class='note'>(" + armyList[i].minSize + ")</span></td>";
                htmlStr += "<td class='points'>" + armyList[i].minCost + "</td>";
                htmlStr += "<td class='checkbox'></td>"; //checkbox holder
            }
            else
            {
                if (armyList[i].isMax)
                {
                    var numberOfGrunts = Number(armyList[i].maxSize) - 1;
                    htmlStr += "<td class='army-points'>" + armyList[i].name + " (Leader and " + numberOfGrunts + ")</td>";
                    htmlStr += "<td class='points'>" + armyList[i].maxCost + "</td>";
                    //can we add a max button (points are okay)
                    htmlStr += "<td class='checkbox'><input style='vertical-align:middle' type='checkbox' name='maxUnit' value='Full' checked onClick=MinUnit(\"" + i + "\")>" + armyList[i].maxSize + "</td>";
                }
                else
                {
                    var numberOfGrunts = Number(armyList[i].minSize) - 1;
                    htmlStr += "<td class='army-points'>" + armyList[i].name + " (Leader and " + numberOfGrunts + ")</td>";
                    htmlStr += "<td class='points'>" + armyList[i].minCost + "</td>";
                    //can we add a max button (points are okay)
                    if (armyPoints + Number(armyList[i].maxCost) - Number(armyList[i].minCost) <= gamePoints)
                    {
                        htmlStr += "<td class='checkbox'><input style='vertical-align:middle' type='checkbox' name='maxUnit' value='Full' onClick=MaxUnit(\"" + i + "\")>" + armyList[i].maxSize + "</td>";
                    }
                    else
                    {
                        htmlStr += "<td class='checkbox'></td>"; //checkbox holder
                    }
                }
            }
        }
        else
        {
            if (armyList[i].type === "warjack" || armyList[i].type === "warbeast")
            {
                htmlStr += "<td style='padding: 0px 0px 0px 20px;' class='army-points'>" + armyList[i].name + "</td>";
            }
            else
            {
                htmlStr += "<td class='army-points'>" + armyList[i].name + "</td>";
            }
            htmlStr += "<td class='points'>" + armyList[i].pointCost + "</td>";
            htmlStr += "<td class='checkbox'></td>"; //checkbox holder
        }
        //AddModel defined in Gizmo.js
        htmlStr += "<td class='remove' onclick='javascript:RemoveModel(\"" + i + "\");'></td>";
        htmlStr += "</tr>";
    }
    htmlStr += "<tr class='army-point-total-header'>";
    htmlStr += "<td width=400>Points:</td>";
    htmlStr += "<td class='points'>" + armyPoints + "/" + oArmyInfo.gameLevel.points + "</td>"; //points
    htmlStr += "<td style='width:80px'></td>"; //checkbox holder
    htmlStr += "<td class='remove' onclick='javascript:ClearArmyList();'></td>";
    htmlStr += "</tr>";


    htmlStr += "</tr>";

    htmlStr += "</tbody></table>";
    //alert(divID);
    document.getElementById(divID).innerHTML = htmlStr;

    //save and load support
    //
    var bSignedIn = false;
    var userName = getCookie("GizmoUserName");
    if (userName !== "")
    {
        bSignedIn = true;
    }

    if (armyList.length > 0)
    {
        if (bSignedIn)
        {
            var boxStr = oArmyInfo.faction.name + " - " + oArmyInfo.gameLevel.FullTitle();
            htmlStr = '<input id="' + ARMY_TITLE_INPUT + '" type="text"  style="width:270px" onchange="UpdateArmyInfo();" value="' + boxStr + '"/>';
            htmlStr += '<a href="#" onclick="javascript:SaveArmyList();" class="save-load-button">Save List</a>'; //save button
            document.getElementById("save-load").innerHTML = htmlStr;
        }
    }
    else
    {
        if (bSignedIn)
        {
            htmlStr = '<a href="#" onclick="javascript:ShowLoadDialog();" class="save-load-button">Load a List</a>'; //load button
            document.getElementById("save-load").innerHTML = htmlStr;
        }
    }
}

ArmyInfoViewClass.prototype.FilterAvailableModels = function(model, faction, index)
{
    if (model.currentlyExcluded)
    {
        this.m_htmlStr += "<tr class='disable-row'>";
    }
    else
    {
        this.m_htmlStr += "<tr class='unit-row'>";
    }
    if (model.isUnit)
    {
        if (model.maxCost === "0")
        {
            this.m_htmlStr += "<td class='name'>" + model.name + " (" + model.minSize + ")</td>";
            this.m_htmlStr += "<td class='points'>" + model.minCost + "</td>";
        }
        else
        {
            this.m_htmlStr += "<td class='name'>" + model.name + " (" + model.minSize + "/" + model.maxSize + ")</td>";
            this.m_htmlStr += "<td class='points'>" + model.minCost + "/" + model.maxCost + "</td>";
        }
    }
    else
    {
        this.m_htmlStr += "<td class='name'>" + model.name + "</td>";
        this.m_htmlStr += "<td class='points'>" + model.pointCost + "</td>";
    }
    var faStr = model.fa;
    if (Number(model.fa) === 999)
        faStr = "U";
    this.m_htmlStr += "<td class='fa'>" + faStr + "</td>";
    //AddModel defined in Gizmo.js
    if (!model.currentlyExcluded)
    {
        this.m_htmlStr += "<td class='add' onclick='javascript:AddModel(\"" + faction + "\",\"" + index + "\");'></td>";
    }
    else
    {
        this.m_htmlStr += "<td class='disable-add'></td>";
    }
    this.m_htmlStr += "</tr>";
}
//Global
var ARMY_INFO_DIALOG = "army-info-dialog"
//var FACTION_LIST = "faction-list";
//var GAME_LEVEL_LIST = "game-level-list";
var ARMY_LIST = "army-list";
var AVAILABLE_LIST = "available-list";

//cookies
var FACTION_COOKIE = "GizmoFaction";
var GAME_LEVEL_COOKIE = "GizmoGameLevel";
var USER_NAME_COOKIE = "GizmoUserName";

//var html_root = "http%3A%2F%2Flocalhost%2FGizmo%2F";
//var html_root = "http%3A%2F%2Fwww.engineeringcalculator.net%2FArmyBuilderTest%2F";
//var HTML_ROOT = "http%3A%2F%2Fwww.engineeringcalculator.net%2FGizmo%2F";

var factions = new Array();
var requiredFactions = new Array();
var contracts = new Array();
var gameLevels = new Array();
var tiers = new Array();
tiers.push(new TierClass("No Tier", 0));
tiers.push(new TierClass("Tier I", 1));
tiers.push(new TierClass("Tier II", 2));
tiers.push(new TierClass("Tier III", 3));
tiers.push(new TierClass("Tier VI", 4));

var ArmyInfo = new ArmyInfoClass();
var ArmyInfoView = new ArmyInfoViewClass();
var GizmoController = new GizmoControllerClass();

function setCookie(c_name, value, expiredays)
{
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + expiredays);
    document.cookie = c_name + "=" + escape(value) + ((expiredays == null) ? "" : ";expires=" + exdate.toGMTString());
}

function getCookie(c_name)
{
    if (document.cookie.length > 0)
    {
        c_start = document.cookie.indexOf(c_name + "=");
        if (c_start != -1)
        {
            c_start = c_start + c_name.length + 1;
            c_end = document.cookie.indexOf(";", c_start);
            if (c_end == -1)
            {
                c_end = document.cookie.length;
            }
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }
    return "";
}

if (window.ActiveXObject)
{
    isIE = true;
    XMLHttpRequestObject = new ActiveXObject("Microsoft.XMLHTTP");
    XMLDoc = new ActiveXObject("Microsoft.XMLDOM");
    XMLLoadedList = new ActiveXObject("Microsoft.XMLDOM");
} 
else if (window.XMLHttpRequest)
{
    XMLHttpRequestObject = new XMLHttpRequest();
    XMLDoc = document.implementation.createDocument("", "", null);
    XMLLoadedList = document.implementation.createDocument("", "", null);
}
else
{
    alert("We're sorry, your browser is currently not supported by this app (no xmlhttp support).");
}

var xmlhttp;
function AsynchXMLRequest(url, stateChangeFunction)
{
    xmlhttp = null;
    // code for Mozilla, etc.
    if (window.XMLHttpRequest)
    {
        xmlhttp = new XMLHttpRequest();
    }
    // code for IE
    else if (window.ActiveXObject)
    {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    if (xmlhttp != null)
    {
        xmlhttp.onreadystatechange = stateChangeFunction;
        xmlhttp.open("GET", url, true);
        xmlhttp.send(null);
    }
    else
    {
        alert("We're sorry, your browser is currently not supported by this app (no xmlhttp support).");
    }
}

function InitializeAppStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            XMLDoc = xmlhttp.responseXML;
            InitializeBasicArmyInfo();
        }
        else
        {
            alert("Problem retrieving XML data");
        }
    }
}

function LoadModelsStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            XMLDoc = xmlhttp.responseXML;
            HandleLoadModelsStateChange();
        }
        else
        {
            alert("Problem retrieving XML data");
        }
    }
}

function LoadRequiredModelsStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            XMLDoc = xmlhttp.responseXML;
            HandleLoadRequiredModelsStateChange();
        }
        else
        {
            alert("Problem retrieving XML data");
        }
    }
}

function SaveListStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            var responseText = xmlhttp.responseText;
            alert(responseText);
        }
        else
        {
            alert("There was a problem saving your list. Try signing out and signing back in.");
        }
    }
}

function TrashListStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            var responseText = xmlhttp.responseText;
            alert(responseText);
            ShowLoadDialog();     
        }
        else
        {
            alert("There was a problem deleting your list. Try signing out and signing back in.");
        }
    }
}

function GetArmyListByIDStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            //XMLDoc = xmlhttp.responseXML;
            var xmlList = xmlhttp.responseText;
            //alert(xmlList);
            try //Internet Explorer
            {
                XMLLoadedList = new ActiveXObject("Microsoft.XMLDOM");
                XMLLoaded.async = "false";
                XMLLoaded.loadXML(xmlList);
            }
            catch (e)
            {
                try
                {
                    var parser = new DOMParser();
                    XMLLoadedList = parser.parseFromString(xmlList, "text/xml");
                }
                catch (e) { alert(e.message); };
            }
            LoadArmyList();
            loadDialog.hide();
        }
        else
        {
            alert("There was a problem retrieving your list. Try signing out and signing back in.");
        }
    }
}

function GetUserArmyListsStateChange()
{
    if (xmlhttp.readyState == 4)
    {
        // if "OK"
        if (xmlhttp.status == 200)
        {
            var responseText = xmlhttp.responseText;
            loadDialog.setBody(responseText);
            loadDialog.show();
        }
        else
        {
            alert("There was a problem retrieving your lists. Try signing out and signing back in."); 
        }
    }
}

window.onload = function()
{
    Init();
}

function Init()
{
    //read in the factions and game levels
    AsynchXMLRequest("factions/Factions.xml", InitializeAppStateChange);
}

function sortByName(a, b)
{
    var x = a.name.toLowerCase();
    var y = b.name.toLowerCase();
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}

function sortByCost(a, b)
{
    var x = a.cost;
    var y = b.cost;
    return x - y;
}
var bByName = true;

function sortPredicate(a, b)
{
    if (bByName == "true")
    {
        return sortByName(a, b);
    }
    else
    {
        return sortByCost(a, b);
    }
}

function InitializeBasicArmyInfo()
{
    //load factions
    var factionNodes = XMLDoc.getElementsByTagName("faction");
    for (var i = 0; i < factionNodes.length; i++)
    {
        var name = factionNodes[i].getElementsByTagName("factionName")[0].firstChild.nodeValue;
        var filename = factionNodes[i].getElementsByTagName("factionFile")[0].firstChild.nodeValue;
        var iconFilename = factionNodes[i].getElementsByTagName("iconFile")[0].firstChild.nodeValue;
        factions.push(new FactionClass(name, filename, iconFilename));
    }

    if (factions.length > 0)
    {
        ArmyInfo.faction = factions[0];
        var savedFaction = getCookie(FACTION_COOKIE);
        if (savedFaction !== null)
        {
            for (var i = 0; i < factions.length; i++)
            {
                if (savedFaction === factions[i].name)
                {
                    ArmyInfo.faction = factions[i]; 
                }
            }
        }
    }

    //load required factions
    var requiredFactionNodes = XMLDoc.getElementsByTagName("requiredFaction");
    for (var i = 0; i < requiredFactionNodes.length; i++)
    {
        var name = requiredFactionNodes[i].getElementsByTagName("factionName")[0].firstChild.nodeValue;
        var filename = requiredFactionNodes[i].getElementsByTagName("factionFile")[0].firstChild.nodeValue;
        var iconFilename = factionNodes[i].getElementsByTagName("iconFile")[0].firstChild.nodeValue;
        requiredFactions.push(new FactionClass(name, filename));
    }

    //load contracts
    var contractNodes = XMLDoc.getElementsByTagName("contract");
    for (var i = 0; i < contractNodes.length; i++)
    {
        var name = contractNodes[i].getElementsByTagName("contractName")[0].firstChild.nodeValue;
        contracts.push(new ContractClass(name));
    }
    
    //load game levels
    var gameLevelNodes = XMLDoc.getElementsByTagName("game");
    for (var i = 0; i < gameLevelNodes.length; i++)
    {
        var name = gameLevelNodes[i].getElementsByTagName("gameType")[0].firstChild.nodeValue;
        var casters = gameLevelNodes[i].getElementsByTagName("casters")[0].firstChild.nodeValue;
        var points = gameLevelNodes[i].getElementsByTagName("points")[0].firstChild.nodeValue;
        gameLevels.push(new GameLevelClass(name, Number(casters), Number(points)));
    }

    if (gameLevels.length > 0)
    {
        ArmyInfo.gameLevel = gameLevels[0];
        var savedGameLevel = getCookie(GAME_LEVEL_COOKIE);
        if (savedGameLevel !== null)
        {
            for (var i = 0; i < gameLevels.length; i++)
            {
                if (savedGameLevel === gameLevels[i].FullTitle())
                {
                    ArmyInfo.gameLevel = gameLevels[i];
                }
            }
        }
    }

    ArmyInfoView.CreateDialog(ARMY_INFO_DIALOG, factions, contracts, gameLevels, ArmyInfo);
    UpdateArmyInfo();   
}

function LoadModelsFromXMLNodes(modelType, modelNodes, modelObjectArray, bIsUnit)
{
    for (i = 0; i < modelNodes.length; i++)
    {
        var name = modelNodes[i].getElementsByTagName("name")[0].firstChild.nodeValue;
        var pointCost = modelNodes[i].getElementsByTagName("mkiicost")[0].firstChild.nodeValue;
        var faction = modelNodes[i].getElementsByTagName("baseFaction")[0].firstChild.nodeValue;
        var faNodes = modelNodes[i].getElementsByTagName("basicFA");
        var fa = "1";
        if (faNodes.length > 0)
        {
            fa = faNodes[0].firstChild.nodeValue;
        }
        if (bIsUnit)
        {
            minSize = modelNodes[i].getElementsByTagName("baseSize")[0].firstChild.nodeValue;
            maxSize = modelNodes[i].getElementsByTagName("maxSize")[0].firstChild.nodeValue;
            expandCostNodes = modelNodes[i].getElementsByTagName("mkiiexpand");
            expandCost = 0;
            if (expandCostNodes.length > 0)
            {
                expandCost = expandCostNodes[0].firstChild.nodeValue;
            }
        }
        modelObjectArray.push(new ModelClass());
        var lastElement = modelObjectArray.length - 1;
        modelObjectArray[lastElement].SetupStandardInfo(faction, modelType, name, pointCost, fa);
        if (bIsUnit)
        {
            modelObjectArray[lastElement].SetupUnitInfo(pointCost, expandCost, minSize, maxSize);
        }
        //unit and weapon attachments...
        var attachedToNodes = modelNodes[i].getElementsByTagName("baseUnit");
        if (attachedToNodes.length > 0)
        {
            var attachedTo = attachedToNodes[0].firstChild.nodeValue;
            modelObjectArray[lastElement].attachedTo = attachedTo;
        }
        //assignable solo
        var dependsOnNodes = modelNodes[i].getElementsByTagName("dependsOn");
        if (dependsOnNodes.length > 0)
        {
            var dependsOn = dependsOnNodes[0].firstChild.nodeValue;
            modelObjectArray[lastElement].attachedTo = dependsOn;
        }
        //for mercs
        var excludeNodes = modelNodes[i].getElementsByTagName("excludeFaction");
        if (excludeNodes.length > 0)
        {
            modelObjectArray[lastElement].excludeFaction = new Array();
            for (j = 0; j < excludeNodes.length; j++)
            {
                var exclude = excludeNodes[j].firstChild.nodeValue;
                modelObjectArray[lastElement].excludeFaction.push(exclude);
            }
        } 
    }
}

function LoadFactionModelObjects( modelsArray, modelGroupArray )
{
    //warcasters and warlocks
    var modelNodes = XMLDoc.getElementsByTagName( "warcaster" );
    var type = "warcaster";
    if( modelNodes.length <= 0 ){
        modelNodes = XMLDoc.getElementsByTagName( "warlock" );
        type = "warlock";
    }
    modelGroupArray.push(new ModelGroup(type,false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);

    //warcasters attachments and warlocks attachments
    modelNodes = XMLDoc.getElementsByTagName("warcasterAttachment");
    type = "warcasterAttachment";
    if (modelNodes.length <= 0)
    {
        modelNodes = XMLDoc.getElementsByTagName("warlockAttachment");
        type = "warlockAttachment";
    }
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);
    
    //warjacks and warbeasts
    modelNodes = XMLDoc.getElementsByTagName("warjack");
    type = "warjack";
    if (modelNodes.length <= 0)
    {
        modelNodes = XMLDoc.getElementsByTagName("warbeast");
        type = "warbeast";
    }
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);

    //units
    modelNodes = XMLDoc.getElementsByTagName("unit");
    type = "unit";
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, true);

    //assignable solos
    modelNodes = XMLDoc.getElementsByTagName("assignableSolo");
    type = "assignableSolo";
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);

    //solos
    modelNodes = XMLDoc.getElementsByTagName("solo");
    type = "solo";
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);

    //unit attachment
    modelNodes = XMLDoc.getElementsByTagName("unitAttachment");
    type = "unitAttachment";
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);

    //weapon attachment
    modelNodes = XMLDoc.getElementsByTagName("specWpnAttachment");
    type = "specWpnAttachment";
    modelGroupArray.push(new ModelGroup(type, false));
    LoadModelsFromXMLNodes(type, modelNodes, modelsArray, false);
}

var currentRequiredFactionName = "";
function HandleLoadModelsStateChange()
{
    ArmyInfo.availableModels[ArmyInfo.faction.name] = new Array();
    ArmyInfo.modelGroups[ArmyInfo.faction.name] = new Array();
    LoadFactionModelObjects(ArmyInfo.availableModels[ArmyInfo.faction.name], ArmyInfo.modelGroups[ArmyInfo.faction.name]);
    GizmoController.UpdateFaction(factions, ArmyInfo.faction.name, ArmyInfo, ArmyInfoView);
    //make sure our required factions are loaded!
    for (var i = 0; i < requiredFactions.length; i++)
    {
        if (ArmyInfo.availableModels[requiredFactions[i].name] == null)
        {
            //alert("Loading " + requiredFactions[i].filename + " from file."); 
            currentRequiredFactionName = requiredFactions[i].name;
            AsynchXMLRequest("factions/" + requiredFactions[i].filename, LoadRequiredModelsStateChange);
            return;
        }
    }
    if (isLoading)
    {
        LoadModelsIntoArmyList();
    }     
}

function HandleLoadRequiredModelsStateChange()
{
    ArmyInfo.availableModels[currentRequiredFactionName] = new Array();
    ArmyInfo.modelGroups[currentRequiredFactionName] = new Array();
    LoadFactionModelObjects(ArmyInfo.availableModels[currentRequiredFactionName], ArmyInfo.modelGroups[currentRequiredFactionName]);
    GizmoController.UpdateFaction(factions, ArmyInfo.faction.name, ArmyInfo, ArmyInfoView);
    for (var i = 0; i < requiredFactions.length; i++)
    {
        if (ArmyInfo.availableModels[requiredFactions[i].name] == null)
        {
            //alert("Loading " + requiredFactions[i].filename + " from file."); 
            currentRequiredFactionName = requiredFactions[i].name;
            AsynchXMLRequest("factions/" + requiredFactions[i].filename, LoadRequiredModelsStateChange);
            return;
        }
    }
    if (isLoading)
    {
        LoadModelsIntoArmyList();
    }
}

function UpdateArmyInfo()
{
    GizmoController.UpdateFaction(factions, ArmyInfo.faction.name, ArmyInfo, ArmyInfoView);
    GizmoController.UpdateContract(contracts, ArmyInfo, ArmyInfoView);
}

function UpdateFaction(selectedFactionName)
{
    GizmoController.UpdateFaction(factions, selectedFactionName, ArmyInfo, ArmyInfoView)
}

function UpdateGameLevel(index)
{
    GizmoController.UpdateGameLevel(index, gameLevels, ArmyInfo, ArmyInfoView);
}

function ToggleCollapse(faction, currentGroup)
{
    GizmoController.ToggleCollapse(faction, currentGroup, ArmyInfo, ArmyInfoView);
}

function AddModel(faction, index)
{
    GizmoController.AddModelToArmyList(faction, index, ArmyInfo, ArmyInfoView);   
}

function RemoveModel(index)
{
    var currentFaction = ArmyInfo.faction.name;
    GizmoController.RemoveModelFromArmyList(currentFaction, index, ArmyInfo, ArmyInfoView);
}

function MinUnit(index)
{
    var currentFaction = ArmyInfo.faction.name;
    GizmoController.MinUnit(currentFaction, index, ArmyInfo, ArmyInfoView);
}

function MaxUnit(index)
{
    var currentFaction = ArmyInfo.faction.name;
    GizmoController.MaxUnit(currentFaction, index, ArmyInfo, ArmyInfoView);
}

function SaveArmyList()
{
    var xmlList = ArmyInfo.ToXML();
    var filename = "test";
    var pointLevel = ArmyInfo.gameLevel.PointsString();
    var phpURL = "GizmoSaveList.php?filename=" + filename + "&pointLevel=" + pointLevel + "&xmlDoc=" + xmlList;
    //alert(xmlList);
    AsynchXMLRequest(phpURL, SaveListStateChange);
}

function ClearArmyList()
{
    GizmoController.ClearArmyList(ArmyInfo, ArmyInfoView);}

var isLoading = false;

function onLoadLinkClicked(fileID)
{
    //alert("load " + fileID);
    var getListURL = "GizmoLoadList.php?listID=" + fileID;
    //now add this info to the saveDialog...
    //document.location = getListURL;
    AsynchXMLRequest(getListURL, GetArmyListByIDStateChange);
    loadDialog.hide();
    //waitContainer.setHeader("Retrieving list...");
    //waitContainer.show();
}

function ShowLoadDialog()
{
    var userName = getCookie(USER_NAME_COOKIE);
    //validate the user cookie here...
    if (userName === "")
    {
        alert("Please sign in first!");
        return;
    }
    //set up the php call to be run on the server, this file will handle some additional
    //user authentication. 
    var getListURL = "GizmoGetUserLists.php";
    //now add this info to the saveDialog...
    //document.location = getListURL;
    AsynchXMLRequest(getListURL, GetUserArmyListsStateChange);
}

function LoadArmyList()
{
    isLoading = true;
    
    var titleNodes = XMLLoadedList.getElementsByTagName("title");
    if (titleNodes[0].firstChild)
    {
        ArmyInfo.title = titleNodes[0].firstChild.nodeValue;
    }

    //gameLevel
    var gameLevelNodes = XMLLoadedList.getElementsByTagName("game");
    var gameType = gameLevelNodes[0].getElementsByTagName("gameType")[0].firstChild.nodeValue;
    var gamePoints = gameLevelNodes[0].getElementsByTagName("points")[0].firstChild.nodeValue;
    for (var i = 0; i < gameLevels.length; i++)
    {
        if (gameLevels[i].name === gameType && gameLevels[i].points.toString() === gamePoints.toString())
        {
            GizmoController.UpdateGameLevel(i, gameLevels, ArmyInfo, ArmyInfoView);
            break;
        }
    }

    //contract
    var contractNodes = XMLLoadedList.getElementsByTagName("contract");
    if (contractNodes[0])
    {
        var contractName = contractNodes[0].getElementsByTagName("contractName")[0].firstChild.nodeValue;
        GizmoController.UpdateContract(contracts, ArmyInfo, ArmyInfoView);
    }

    //tier

    //army list
    var factionNodes = XMLLoadedList.getElementsByTagName("faction");
    if (factionNodes)
    {
        var factionName = factionNodes[0].getElementsByTagName("factionName")[0].firstChild.nodeValue;
        if (ArmyInfo.faction.name !== factionName)
        {
            for (var i = 0; i < factions.length; i++)
            {
                if (factions[i].name === factionName)
                {
                    //alert(factions[i].name);
                    ArmyInfo.faction = factions[i];
                    ArmyInfo.armyList = new Array();
                }
            }
        }
        ArmyInfoView.UpdateArmyList(ARMY_LIST, ArmyInfo);
        //At this point we may or may not have the faction loaded in the available models list.
        //If it's not loaded we send an asynch request to load the faction and we wait for 
        //the request to return back from the server
        if (ArmyInfo.availableModels[ArmyInfo.faction.name] == null)
        {
            //alert("Loading " + ArmyInfo.faction.filename + " from file."); 
            AsynchXMLRequest("factions/" + ArmyInfo.faction.filename, LoadModelsStateChange);
            return;
        }
        //we'll get here if the faction models have been loaded, otherwise, we call LoadModelsIntoArmyList() 
        //from within the LoadModelsStateChange, which waits for the server response.
        if (isLoading)
        {
            LoadModelsIntoArmyList();
        } 

        ArmyInfoView.UpdateFactionSelect("faction", factions, ArmyInfo);
        ArmyInfoView.UpdateContractAndTierView(ArmyInfo);
        ArmyInfoView.UpdateArmyList(ARMY_LIST, ArmyInfo);
        GizmoController.UpdateVisibleAvailableModels(ArmyInfo, ArmyInfoView);
    }
}

function LoadModelsIntoArmyList()
{
    //alert("Loading models into list."); 
    //assume XMLDoc has a valid amry list
    var modelNodes = XMLLoadedList.getElementsByTagName("model");
    if (modelNodes)
    {
        for (var i = 0; i < modelNodes.length; i++)
        {
            var modelName = modelNodes[i].getElementsByTagName("name")[0].firstChild.nodeValue;
            var baseFaction = modelNodes[i].getElementsByTagName("baseFaction")[0].firstChild.nodeValue;
            var battleGroup = "";
            if (modelNodes[i].getElementsByTagName("battleGroup").length > 0)
            {
                battleGroup = modelNodes[i].getElementsByTagName("battleGroup")[0].firstChild.nodeValue;
            }
            var unitSize = "";
            if (modelNodes[i].getElementsByTagName("unitSize").length > 0)
            {
                unitSize = modelNodes[i].getElementsByTagName("unitSize")[0].firstChild.nodeValue; 
            }
            GizmoController.AddModelFromSavedList(modelName, baseFaction, battleGroup, unitSize, ArmyInfo, ArmyInfoView)
        }
    }
}

function handleSaveButtonLoginReponse(login_args)
{
    //do our asynch php login here.
    //if everything is ok, save the list
    //alert("Custom login here");
}

function onTrashLinkClicked(fileID)
{
    //alert("load " + fileID);

    var trashListURL = "GizmoTrashList.php?listID=" + fileID;
    //alert( trashListURL );
    //now add this info to the saveDialog...
    //document.location = getListURL;
    AsynchXMLRequest(trashListURL, TrashListStateChange);
    //waitContainer.setHeader("Tossing list...");
    //waitContainer.show();
}

//our modal load dialog that allows us to load or delete lists
var loadDialog =
    new YAHOO.widget.SimpleDialog("loadDialog",
	{
	    width: "400px",
	    fixedcenter: true,
	    visible: false,
	    draggable: false,
	    close: true,
	    modal: true,
	    zindex: 4,
	    text: "Yout lists here...",
	    icon: YAHOO.widget.SimpleDialog.ICON_HELP,
	    constraintoviewport: true,
	    buttons: [{ text: "Cancel", handler: handleLoadDialogCancel}]
	}
);

// Render the Dialog
loadDialog.render();

function handleLoadDialogCancel()
{
    this.cancel();
}

//////////////////////////////////////////////////////
//Our controller class - responsible for:
//
//      -responding to user inupt
//      -updating data (after it is validated by the model)
//      -putting up error messages and warnings
//
//////////////////////////////////////////////////////
function GizmoControllerClass()
{
}

GizmoControllerClass.prototype.ClearArmyList = function(oArmy, oArmyView)
{
    oArmy.armyList = new Array();
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);
}

GizmoControllerClass.prototype.UpdateFaction = function(factions, selectedFactionName, oArmy, oArmyView)
{
    if (oArmy.faction.name !== selectedFactionName)
    {
        for (var i = 0; i < factions.length; i++)
        {
            if (factions[i].name === selectedFactionName)
            {
                //alert(factions[i].name);
                oArmy.faction = factions[i];
                oArmy.armyList = new Array();
                setCookie(FACTION_COOKIE, factions[i].name, 9999);
            }
        }
    }
    //declared in Gizmo.js
    if (oArmy.availableModels[oArmy.faction.name] == null)
    {
        AsynchXMLRequest("factions/" + oArmy.faction.filename, LoadModelsStateChange);
        return;
    }

    oArmyView.UpdateFactionSelect("faction", factions, oArmy);
    oArmyView.UpdateContractAndTierView(oArmy);
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);
}

GizmoControllerClass.prototype.UpdateGameLevel = function(index, gameLevels, oArmy, oArmyView)
{
    oArmy.gameLevel = gameLevels[index];
    setCookie(GAME_LEVEL_COOKIE, gameLevels[index].FullTitle(), 9999);
    oArmyView.UpdateGameLevelSelect("game-level", gameLevels, oArmy);
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);
}

GizmoControllerClass.prototype.UpdateContract = function(contracts, oArmy, oArmyView)
{
    if (oArmy.faction.name === "Mercenaries")
    {
        var contractList = document.getElementById(CONTRACT_SELECT);
        oArmy.contract = contracts[contractList.selectedIndex];
        oArmyView.UpdateContractAndTierView(oArmy);
        oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
        this.UpdateVisibleAvailableModels(oArmy, oArmyView);
    }
}

GizmoControllerClass.prototype.AddModelToArmyList = function(faction, index, oArmy, oArmyView)
{
    //make sure we can add this one...
    var modelAdded = false;
    if (oArmy.availableModels[faction][index].type === "warjack")
    {
        var firstCasterIndex = -1;
        var battleGroup = "";
        for (var i = 0; i < oArmy.armyList.length; i++)
        {
            if (oArmy.armyList[i].type === "warcaster")
            {
                firstCasterIndex = i;
                battleGroup = oArmy.armyList[i].name;
                break;
            }
        }
        if (firstCasterIndex > -1)
        {
            //insert this model under the caster
            oArmy.armyList.splice(firstCasterIndex + 1, 0, new ModelClass());
            oArmy.armyList[firstCasterIndex + 1].Copy(oArmy.availableModels[faction][index]);
            oArmy.armyList[firstCasterIndex + 1].battleGroup = battleGroup;
            modelAdded = true;
        }
    }
    else if (oArmy.availableModels[faction][index].type === "warbeast")
    {
        var firstCasterIndex = -1;
        var battleGroup = "";
        for (var i = 0; i < oArmy.armyList.length; i++)
        {
            if (oArmy.armyList[i].type === "warlock")
            {
                firstCasterIndex = i;
                battleGroup = oArmy.armyList[i].name;
                break;
            }
        }
        if (firstCasterIndex > -1)
        {
            //insert this model under the caster
            oArmy.armyList.splice(firstCasterIndex + 1, 0, new ModelClass());
            oArmy.armyList[firstCasterIndex + 1].Copy(oArmy.availableModels[faction][index]);
            oArmy.armyList[firstCasterIndex + 1].battleGroup = battleGroup;
            modelAdded = true;
        }
    }
    if (!modelAdded)
    {
        oArmy.armyList.push(new ModelClass());
        var lastElement = oArmy.armyList.length - 1;
        oArmy.armyList[lastElement].Copy(oArmy.availableModels[faction][index]);
    }
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);

}

GizmoControllerClass.prototype.AddModelFromSavedList = function(modelName, baseFaction, battleGroup, unitSize, oArmy, oArmyView)
{
    //1. find the model
    //2. if it has a battle group find the warcaster and add it to that warcaster
    //3. if it's a unit set the unit size
    ///////////////////////////////////////
    // for now let's just use the AddModelToArmyList function from above...
    var index = -1;
    for (var i = 0;  i < oArmy.availableModels[baseFaction].length; i++)
    {
        if (oArmy.availableModels[baseFaction][i].name === modelName)
        {
            index = i;
            this.AddModelToArmyList(baseFaction, index, oArmy, oArmyView);
            break;
        }
    }
    if (index > 0 && unitSize !== "")
    {
        //if it's a unit then set the unit size
        if (unitSize === "max")
        {
            var lastElement = oArmy.armyList.length - 1;
            oArmy.armyList[lastElement].SetMaxUnit();
            oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
            this.UpdateVisibleAvailableModels(oArmy, oArmyView);
        }
    }
}

GizmoControllerClass.prototype.RemoveModelFromArmyList = function(faction, index, oArmy, oArmyView)
{
    oArmy.armyList.splice(index, 1);
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);
}

GizmoControllerClass.prototype.MinUnit = function(faction, index, oArmy, oArmyView)
{
    oArmy.armyList[index].SetMinUnit();
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);
}

GizmoControllerClass.prototype.MaxUnit = function(faction, index, oArmy, oArmyView)
{
    oArmy.armyList[index].SetMaxUnit();
    oArmyView.UpdateArmyList(ARMY_LIST, oArmy);
    this.UpdateVisibleAvailableModels(oArmy, oArmyView);
}

GizmoControllerClass.prototype.UpdateVisibleAvailableModels = function(oArmy, oArmyView)
{
    var htmlStr = "<table>";
    var currentFaction = oArmy.faction.name;
    var currentContract = oArmy.contract.name;
    var availableModels = oArmy.availableModels[currentFaction];
    var armyList = oArmy.armyList;

    var casterCount = oArmy.CasterCount();
    var pointTotal = oArmy.ArmyPoints();

    //check caster count
    var ignoreCasters = false;
    if (casterCount === oArmy.gameLevel.numCasters)
    {
        ignoreCasters = true;
    }

    if (availableModels == null)
    {
        alert("Problem in Controller.UpdateVisibleAvailableModels: Faction not found");
        return;
    }

    for (var i = 0; i < availableModels.length; i++)
    {
        availableModels[i].currentlyExcluded = false;
        //should this model be excluded from available model list...

        //1. if we don't have caster then disable all other models
        if (casterCount <= 0 && availableModels[i].type !== "warcaster" && availableModels[i].type !== "warlock")
        {
            availableModels[i].currentlyExcluded = true;
        }

        //2. Warcaster/Warlock Check
        if (ignoreCasters === true && (availableModels[i].type === "warcaster" || availableModels[i].type === "warlock"))
        {
            availableModels[i].currentlyExcluded = true;
        }
        
        //3. FA Check
        this.CheckFA(availableModels[i], armyList);
        //4. Points Check
        this.PointsCheck(availableModels[i], pointTotal, Number(oArmy.gameLevel.points));
    }

    //check merc list if necessary
    if (oArmy.IsWarmachine() )
    {
        var mercenaries = oArmy.availableModels["Mercenaries"];
        var haveMercCaster = oArmy.HaveMercCaster();
        //alert(haveMercCaster);
        if (mercenaries != null)
        {
            for (var i = 0; i < mercenaries.length; i++)
            {
                mercenaries[i].currentlyExcluded = false;
                //should this model be excluded from available model list...               
                //1. only show merc caster if we have a regular caster
                if (casterCount <= 0 && mercenaries[i].type === "warcaster")
                {
                    mercenaries[i].currentlyExcluded = true;
                }
                //2. if we don't have a merc caster then don't show merc jacks
                if (!haveMercCaster && mercenaries[i].type === "Warjack")
                {
                    mercenaries[i].currentlyExcluded = true;
                }
                //3. if we don't have a caster then disable all other models
                if (casterCount <= 0 && mercenaries[i].type !== "warcaster" && mercenaries[i].type !== "warlock")
                {
                    mercenaries[i].currentlyExcluded = true;
                }
                //4. Warcaster/Warlock Check
                if (ignoreCasters === true && mercenaries[i].type === "warcaster")
                {
                    mercenaries[i].currentlyExcluded = true;
                }
                //5. FA Check
                this.CheckFA(mercenaries[i], armyList);
                //6. Points Check
                this.PointsCheck(mercenaries[i], pointTotal, Number(oArmy.gameLevel.points));
            }
        }
    }
    //check minion list if necessary
    if (oArmy.IsHordes() )
    {
        var minions = oArmy.availableModels["Minions"];
        var haveMercCaster = oArmy.HaveMercCaster();
        //alert(haveMercCaster);
        if (minions != null)
        {
            for (var i = 0; i < minions.length; i++)
            {
                minions[i].currentlyExcluded = false;
                //should this model be excluded from available model list...
                //1. only show merc caster if we have a regular caster
                if (casterCount <= 0 && minions[i].type === "warcaster")
                {
                    minions[i].currentlyExcluded = true;
                }
                //2. if we don't have a merc caster then don't show merc jacks
                if (!haveMercCaster && minions[i].type === "warjack")
                {
                    minions[i].currentlyExcluded = true;
                }
                //3. if we don't have a caster then disable all other models
                if (casterCount <= 0 && minions[i].type !== "warcaster" && minions[i].type !== "warlock")
                {
                    minions[i].currentlyExcluded = true;
                }
                //4. Warcaster/Warlock Check
                if (ignoreCasters === true && minions[i].type === "warcaster")
                {
                    minions[i].currentlyExcluded = true;
                }
                //5. FA Check
                this.CheckFA(minions[i], armyList);
                //6. Points Check
                this.PointsCheck(minions[i], pointTotal, Number(oArmy.gameLevel.points));
            }
        }
    }
    oArmyView.UpdateAvailableModelList(AVAILABLE_LIST, oArmy);
}

GizmoControllerClass.prototype.CheckFA = function(model, armyList)
{
    var numFielded = 0;
    for (var j = 0; j < armyList.length; j++)
    {
        if (model.name === armyList[j].name)
        {
            numFielded++;
        }
    }
    var numberAllowed = 1;
    //if it's an attachment
    if (model.attachedTo !== "")
    {
        numberAllowed = 0;
        for (var j = 0; j < armyList.length; j++)
        {
            var containIndex = armyList[j].name.indexOf(model.attachedTo);
            if (containIndex !== -1)
            {
                numberAllowed++;
                //if it's a Weapon attachment we can add FA per base unit
                if (model.type !== "specWpnAttachment")
                {
                    break;
                }
            }
        }
    }
    if (numFielded >= numberAllowed * Number(model.fa))
    {
        model.currentlyExcluded = true;
    }
}

GizmoControllerClass.prototype.PointsCheck = function(model, currentPointTotal, allowablePoints)
{
    if (model.type !== "warcaster" && model.type !== "warlock")
    {
        if (Number(model.pointCost) + currentPointTotal > allowablePoints)
        {
            model.currentlyExcluded = true;
        }
    }
}

GizmoControllerClass.prototype.ToggleCollapse = function(faction, currentGroup, oArmy, oArmyView)
{
    var groups = oArmy.modelGroups[faction];
    for (var j = 0; j < groups.length; j++)
    {
        if (groups[j].name === currentGroup)
        {
            groups[j].collapse = !groups[j].collapse;
            break;
        }
    }
    oArmyView.UpdateAvailableModelList(AVAILABLE_LIST, oArmy);
}

GizmoControllerClass.prototype.SaveArmyList = function(oArmy)
{
    //1. faction
    //2. gameLevel
    //3. contract
    //4. tier
    //5. models
//    var signedIn = false;
//    if (!signedIn)
//    {
//        var signInStr = 'https://warmachine--hordes-army-builder.rpxnow.com/openid/v2/signin?token_url=http%3A%2F%2Flocalhost%2FWarmachineGizmo%2FGizmoSignIn.php?destination=Gizmo.html';
//        window.location = signInStr;
//    }
//    else
//    {
//        var xmlList = oArmy.ToXML();
//        var filename = "test";
//        var phpURL = "GizmoSaveList.php?username=" + userName + "&filename=" + data.filename + "&xmlDoc=" + xmlList;
//        alert(phpURL);
//    }
}

GizmoControllerClass.prototype.LoadArmyList = function(oArmy)
{
    alert("Coming Soon!");
    //1. faction
    //2. gameLevel
    //3. contract
    //4. tier
    //5. models     
}
// Our model class
// Holds information for each model
function ModelClass()
{
    this.faction = "";
    this.type = "";
    this.name = "";
    this.pointCost = 0;
    this.fa = 0;
    this.attachedTo = "";
    this.isUnit = false;
    this.minCost = 0;
    this.maxCost = 0;
    this.minSize = 0;
    this.maxSize = 0;
    this.canMax = true;
    this.isMax = false;
    this.excludeFaction = new Array();
    this.tierList = new Array();
    this.currentlyExcluded = false;
    this.battleGroup = "";
}

function ModelClass( faction, type, name, pointCost, fa )
{
    this.faction = faction;
    this.type = type;
    this.name = name;
    this.pointCost = pointCost;
    this.fa = fa;
    this.attachedTo = "";
    this.isUnit = false;
    this.minCost = 0;
    this.maxCost = 0;
    this.minSize = 0;
    this.maxSize = 0;
    this.canMax = true;
    this.isMax = false;
    this.excludeFaction = new Array();
    this.tierList = new Array();
    this.currentlyExcluded = false;
    this.battleGroup = "";
}

function ModelClass(faction, type, name, pointCost, fa, minCost, maxCost, minSize, maxSize)
{
    this.faction = faction;
    this.type = type;
    this.name = name;
    this.pointCost = pointCost;
    this.fa = fa;
    this.attachedTo = "";
    this.isUnit = true;
    this.minCost = minCost;
    this.maxCost = maxCost;
    this.minSize = minSize;
    this.maxSize = maxSize;
    this.canMax = true;
    this.isMax = false;
    this.excludeFaction = new Array();
    this.tierList = new Array();
    this.currentlyExcluded = false;
    this.battleGroup = "";
}

ModelClass.prototype.ToXML = function()
{
    var xmlStr = "<model>";
    xmlStr += "<type>" + this.type + "</type>";
    xmlStr += "<baseFaction>" + this.faction + "</baseFaction>";
    xmlStr += "<name>" + this.name + "</name>";
    xmlStr += "<mkiicost>" + this.pointCost + "</mkiicost>";
    if (this.isUnit)
    {
        var maxStr = this.isMax ? "max" : "min";
        xmlStr += "<unitSize>" + maxStr + "</unitSize>";
    }
    if (this.battleGroup !== "")
    {
        xmlStr += "<battleGroup>" + this.battleGroup + "</battleGroup>";
    }
    xmlStr += "</model>";
    return xmlStr;
}

ModelClass.prototype.Copy = function(model)
{
    this.faction = model.faction; 
    this.type = model.type;
    this.name = model.name;
    this.pointCost = model.pointCost;
    this.fa = model.fa;
    this.attachedTo = model.attachedTo;
    this.isUnit = model.isUnit;
    this.minCost = model.minCost;
    this.maxCost = model.maxCost;
    this.minSize = model.minSize;
    this.maxSize = model.maxSize;
    this.canMax = model.canMax;
    this.isMax = model.isMax;
    this.excludeFaction = new Array();
    for (var i = 0; i < model.excludeFaction.length; i++)
    {
        this.excludeFaction.push(model.excludeFaction[i]);
    }
    this.tierList = new Array();
    for (var i = 0; i < model.tierList.length; i++)
    {
        this.tierList.push(model.tierList[i]);
    }
    this.currentlyExcluded = model.currentlyExcluded;
    this.battleGroup = model.battleGroup;
}

ModelClass.prototype.SetupStandardInfo = function(faction, type, name, pointCost, fa)
{
    this.faction = faction;
    this.type = type;
    this.name = name;
    this.pointCost = pointCost;
    this.fa = fa;
    this.attachedTo = "";
    this.isUnit = false;
    this.minCost = 0;
    this.maxCost = 0;
    this.minSize = 0;
    this.maxSize = 0;
    this.canMax = true;
    this.isMax = false;
    this.battleGroup = "";
}

ModelClass.prototype.SetupUnitInfo = function(minCost, maxCost, minSize, maxSize)
{
    this.isUnit = true;
    this.minCost = minCost;
    this.maxCost = maxCost;
    this.minSize = minSize;
    this.maxSize = maxSize;
    this.canMax = true;
    this.isMax = false;
}

ModelClass.prototype.SetMinUnit = function()
{
    if (!this.isUnit)
    {
        alert("Not a unit!");
        return;
    }
    this.isMax = false;
}

ModelClass.prototype.SetMaxUnit = function()
{
    if (!this.isUnit)
    {
        alert("Not a unit!");
        return;
    }
    if (this.canMax)
    {
        this.isMax = true;
    }
}

ModelClass.prototype.NiceTypeName = function()
{
    switch (this.type)
    {
        case "warcaster":
            return "Warcaster";
        case "warlock":
            return "Warlock";
        case "warjack":
            return "Warjack";
        case "warbeast":
            return "Warbeast";
        case "solo":
            return "Solo";
        case "unit":
            return "Unit";
        case "assignableSolo":
            return "Assignable Solo";
        case "unitAttachment":
            return "Unit Attachment";
        case "warcasterAttachment":
            return "Warcaster Attachment";
        case "warlockAttachment":
            return "warlockAttachment"; 
        case "specWpnAttachment":
            return "Weapon Attachment";
        default:
            return "Unknown";
    }
}
