function interpolate(lowVal, highVal, level, minLevel, maxLevel)
{
     if(level < minLevel) { level = minLevel; }
	 if(level > maxLevel) { level = maxLevel; }
	 if(minLevel == maxLevel) { return lowVal; }
	 //Round to 1 digits
     return Math.round(10*(lowVal + (highVal-lowVal) / (maxLevel-minLevel) * (level-minLevel)))/10;
}
function outputArray(arr)
{
	var arrayLength = arr.length;
	var outputString = "";
	for(var i=0; i<arrayLength; i++)
	{
		outputString += arr[i];
		outputString += "<br/>";
	}
	return outputString;
}
function sprintf(condPattern)
{
	var condRegExp = new RegExp(/\{(#\d[^:]*):([^\|^\}]+)\|?([^\}]+)?\}/g);
	var opRegExp = new RegExp(/#(\d)([=!<>]*)(\d*)/);
	var matches = new Array();
	var ops = new Array();
    var subStrings = new Array();
    var subStringStart = 0;
    var subStringEnd = 0;
    //EndPosition of last match, to start for next part
    var matchPosEnd = 0;
    var pattern = "";
    var match = null;
	var opMatch = null;
	while (match = condRegExp.exec(condPattern))
    {
		subStringStart = matchPosEnd;
		subStringEnd = condRegExp.lastIndex - match[0].length;
		//Add
		subStrings[subStrings.length] = condPattern.substring(subStringStart, subStringEnd);
		matchPosEnd = condRegExp.lastIndex;
		matches[matches.length] = { cond:match[1], thenpart:match[2], elsepart:match[3] };
	}
	subStrings[subStrings.length] = condPattern.substring(matchPosEnd);
	pattern += subStrings[0];
	for (var i=0; i<matches.length; i++)
    {
		var index = 1;
		if(matches[i]["cond"]!=undefined)
		{
			opMatch = opRegExp.exec(matches[i]["cond"]);
			ops[i] = { index:opMatch[1], operator:opMatch[2], operand:opMatch[3] };
		}
		var arg = arguments[ops[i]["index"]];
		if(ops[i]["operator"] == "") ops[i]["operator"] = "!=";
		if(ops[i]["operand"] == "") ops[i]["operand"] = 0;
		if(eval(arg+ops[i]["operator"]+ops[i]["operand"]))
		{
			pattern += matches[i]["thenpart"];
		}
		else
		{
			if(matches[i]["elsepart"] != undefined)
			{
				pattern += matches[i]["elsepart"];
			}
		}
		pattern += subStrings[i+1];
	}
	
    var regExp = new RegExp(/(#\d)?(%[dsu])/g);
    matches = new Array();
    subStrings = new Array();
    subStringStart = 0;
    subStringEnd = 0;
    //EndPosition of last match, to start for next part
    matchPosEnd = 0;
    var totalString = "";
    match = null;

      while (match = regExp.exec(pattern))
      {
            subStringStart = matchPosEnd;
            subStringEnd = regExp.lastIndex - match[0].length;
            //Add
            subStrings[subStrings.length] = pattern.substring(subStringStart, subStringEnd);
            matchPosEnd = regExp.lastIndex;
            matches[matches.length] = { place:match[1], format:match[2] };
      }
      subStrings[subStrings.length] = pattern.substring(matchPosEnd);
      totalString += subStrings[0];
	  var index = 1;
      for (var i=0; i<matches.length; i++)
      {
			var arg = null;
			if(matches[i]["place"]!=undefined)
			{
				arg = arguments[parseInt(matches[i]["place"].charAt(1))];
			}
			else
			{
				arg = arguments[index];
				index++;
			}
            if (matches[i]["format"] == "%s")
            {
                 totalString += arg;
            }
			else if (matches[i]["format"] == "%u")
            {
				totalString += "<a href=\"details.php?id=" + arg[0] + "\">" + arg[1]+ "</a>";
			}
            else if (matches[i]["format"] == "%d")
            {
                 if(arg>0)
                 {
                     totalString += "<span class=\"positive\">";
                     totalString += "+";
                     totalString += arg;
                     totalString += "</span>";
                 }
                 else
                 {
                     totalString += "<span class=\"negative\">";
                     totalString += arg;
                     totalString += "</span>";
                 }
            }
            totalString += subStrings[i+1];
      }
      return totalString;
}

function generateModString(mods, level)
{
	if(mods == undefined) return "";
	var htmlString = "";
	for(var i = 0; i < mods.length; i++)
	{
		//print constraints
		if(mods[i]["constraints"] != undefined)
		{
			htmlString += "<i>";
			for (var c=0; c<mods[i]["constraints"].length; c++)
			{
				htmlString += mods[i]["constraints"][c] + ";";
			}
			htmlString += "</i>";
		}
        switch(mods[i]["type"])
		{
			//ignores chunks
			case 53003: //mesh changes
			case 53110: //other FX changes
				break;
			case 53002:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["remove"], mods[i]["attr"]);
				if(mods[i]["period"] != undefined)
				{
					htmlString += sprintf(" every #1%s seconds", mods[i]["period"]);
				}
				htmlString += "<br>";
				break;
			case 53007:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["attr"], mods[i]["renewal"]);
				htmlString += "<br>";
				break;
			case 53019:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["abil"]);
				htmlString += "<br>";
				break;
			case 53026:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["attribute"], mods[i]["value"]);
				htmlString += "<br>";
				break;
			case 53030:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["val"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53031:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["reduction"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53032:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["val"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53045:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["name"], mods[i]["val"]);
				htmlString += "<br>";
				break;
			case 53046:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["attribute"], mods[i]["absorb"], mods[i]["maxAbsorb"], mods[i]["absorbPercent"]);
				htmlString += "<br>";
				break;
			case 53051:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["effect"]);
				htmlString += "<br>";
				break;
			case 53054:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["attr"], mods[i]["change"], mods[i]["changepercent"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53058:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["buff"]);
				htmlString += "<br>";
				break;
			case 53061:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["abil"]);
				htmlString += "<br>";
				break;
			case 53062:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["attr"], mods[i]["change"], mods[i]["changepercent"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53066:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["abilityId"]);
				if(mods[i]["period"] != undefined)
				{
					htmlString += sprintf(" every #1%s seconds", mods[i]["period"]);
				}
				htmlString += "<br>";
				break;
			case 53069:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["recastreduce"]);
				htmlString += "<br>";
				htmlString +=  mods[i]["spell"];
				htmlString += "<br>";
				break;
			case 53070:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["target"], mods[i]["selfProc"], mods[i]["targetProc"], mods[i]["procMode"], mods[i]["dmgMode"], mods[i]["procPerMin"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["effect"]);
				break;
			case 53074:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["val"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53075:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["val"]);
				htmlString += "<br>";
				htmlString += mods[i]["name"];
				htmlString += "<br>";
				break;
			case 53077:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["recast"]);
				htmlString += "<br>";
				break;
			case 53080:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["abil"], mods[i]["points"]);
				htmlString += "<br>";
				break;
			case 53087:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["ability"], mods[i]["radius"]);
				htmlString += "<br>";
				break;
			case 53088:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["ability"], mods[i]["radius"], mods[i]["maxTargets"]);
				if(mods[i]["period"] != undefined)
				{
					htmlString += sprintf(" every #1%s seconds", mods[i]["period"]);
				}
				htmlString += "<br>";
				break;
			case 53093:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["immunity"], mods[i]["removedPenalty"], mods[i]["immunityDuration"]);
				htmlString += "<br>";
				break;
			case 53121:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]]);
				htmlString += "<br>";
				break;
			case 53122:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]]);
				htmlString += "<br>";
				break;
			case 53129:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]]);
				htmlString += "<br>";
				break;
			case 53162:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["valueChange"], mods[i]["debuffId"]);
				htmlString += "<br>";
				break;
			case 53170:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["min"], mods[i]["dmgType"]);
				htmlString += "<br>";
				break;
			case 53171:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["dpsTick"], mods[i]["unknown2"], mods[i]["dmgType"]);
				if(mods[i]["period"] != undefined)
				{
					htmlString += sprintf(" every #1%s seconds", mods[i]["period"]);
				}
				htmlString += "<br>";
				break;
			case 53194:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]]);
				htmlString += "<br>";
				break;
			case 53209:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["dps"], mods[i]["cooldown"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53211:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["target"], mods[i]["selfProc"], mods[i]["targetProc"], mods[i]["procMode"], mods[i]["dmgMode"], mods[i]["procPerMin"]);
				htmlString += "<br>";
				htmlString += outputArray(mods[i]["effect"]);
				break;
			case 53212:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["target"], mods[i]["selfProc"], mods[i]["targetProc"], mods[i]["procs"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53213:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["target"], mods[i]["selfProc"], mods[i]["targetProc"], mods[i]["procMode"], mods[i]["procs"], mods[i]["procChance"]);
				htmlString += "<br>";
				break;
			case 53217:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["radius"]);
				htmlString += "<br>";
				break;
			case 53219:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["poolId"], interpolate(mods[i]["lowMinDmg"], mods[i]["highMinDmg"], level, mods[i]["lowLevel"], mods[i]["highLevel"]), interpolate(mods[i]["lowMaxDmg"], mods[i]["highMaxDmg"], level, mods[i]["lowLevel"], mods[i]["highLevel"]), mods[i]["dmgType"], mods[i]["isDmg"]);
				if(mods[i]["period"] != undefined)
				{
					htmlString += sprintf(" every #1%s seconds", mods[i]["period"]);
				}
				htmlString += "<br>";
				break;
			case 53220:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["name"], interpolate(mods[i]["lowval"], mods[i]["highval"], level, mods[i]["lowlvl"], mods[i]["highlvl"]));
				htmlString += "<br>";
				break;
			case 53221:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], interpolate(mods[i]["lowval"], mods[i]["highval"], level, mods[i]["lowlvl"], mods[i]["highlvl"]));
				if(mods[i]["period"] != undefined)
				{
					htmlString += sprintf(" every #1%s seconds", mods[i]["period"]);
				}
				htmlString += "<br>";
				break;
			case 53224:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["effectId"]);
				htmlString += "<br>";
				break;
			case 53226:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["recast"]);
				htmlString += "<br>";
				break;
			case 53227:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["health"]);
				htmlString += "<br>";
				break;
			case 53250:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["phase"], mods[i]["dpsincrease"], mods[i]["critchance"], mods[i]["hateadj"]);
				htmlString += "<br>";
				htmlString +=  outputArray(mods[i]["abil"]);
				break;
			case 53259:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["ability"], mods[i]["arc"], mods[i]["radius"]);
				htmlString += "<br>";
				break;
			case 53261:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["buff"], mods[i]["points"]);
				htmlString += "<br>";
				break;
			case 53267:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]]);
				htmlString += "<br>";
				break;
			case 53273:
				htmlString += sprintf(Pattern.Strings[mods[i]["type"]], mods[i]["arc"], mods[i]["radius"]);
				htmlString += "<br>";
				break;
			default:
				htmlString += mods[i]["type"];
				htmlString += "<br>";
				break;
		}
    }
    return htmlString;
}