function formatThousands (n)
{
    if (n < 1000) { return n; }
    n = '' + n;	// convert to string
    var r = n.split ('').reverse ().join ('');
	var withCommas = r.replace(/(\d\d\d)/g, '$1,');
    return withCommas.split ('').reverse ().join ('');
};



function RemoteRequest (url, callback, methodType, params)
{
	// make one of these to load text (one-time) from a server.
	// call it as follows:
	//
	// 	rr = new RemoteRequest ('/path/on/server', myFunction, {})
	//
	// when the data arrives, myFunction(status, data) will be called.
	// there is currently no timeout to protect against the data not arriving.
    // if params is not empty, it's a POST request, otherwise GET.

    var me = this;
	var req = undefined;

	var IS_IE = false;
	if (window.XMLHttpRequest)
	{
		IS_IE = false;
	}
	else if (window.ActiveXObject)
	{
		IS_IE = true;
	}
	else
	{
        alert ("Sorry, your browser does not support this application.");
	}

	function processReqChange ()
	{
		if (req.readyState == 4)
		{
            callback (req.status, req.responseText);
		}
	}

    function encodedValues ()
    {
        var buf = [];
        var first = false;
        for (var i in params) {
            if (!first) {
                first = true;
            } else {
                buf.push("&");
            }
            buf.push (encodeURIComponent (i));
            buf.push ("=");
            buf.push (encodeURIComponent (params[i]));
        }
        return buf.join("");
    }


    if (!IS_IE)
    {
        req = new XMLHttpRequest ();
    } else {
        req = new ActiveXObject ('Microsoft.XMLHTTP');
    }
    if (callback)
    {
        req.onreadystatechange = processReqChange;
    }
    var requestData = encodedValues ();
    if (methodType == 'GET')
    {
        //console.debug (req + " " + url);
        if (requestData != "")
        {
            url = url + '?' + requestData;
        }
        req.open ('GET', url, true);
        if (!IS_IE)
        {
            req.send (null);
        } else {
            req.send ();
        }
    }
    else
    {
        req.open ('POST', url, true);
        req.setRequestHeader ('Content-Type', 'text/plain');
        req.send (requestData);
    }
}

function Recipe ()
{
    var me = this;

    var OIL_COUNT_MAX = 12;

    var totalPopulation;
    var totalGenerations;
    var quantityEvaluated;
    var generation;
    var narration;
    var hardness;
    var cleansing;
    var conditioning;
    var fluffy;
    var stable;
    var ins;

    var hardnessOK;
    var cleansingOK;
    var conditioningOK;
    var fluffyOK;
    var stableOK;
    var insOK;

    var lastOilSets;
    var lastSoapType;


    var water;
    var waterMinimum;
    var lye;
    var fragrance;
    var oilCount = 0;
    var oilIDs = new Array ();
    var oilNames = new Array ();
    var quantities = new Array ();
    var requiredIDs = new Object ();
    var forbiddenIDs = new Object ();

    function ingredientClick (ev, index)
    {
        //console.debug ("Clicked on ingredient " + index + " (" + oilNames[index] + ") with event: " + ev);

        var target;
        if (ev) {
            target = ev.target;
        } else {
            target = document.getElementById ('oil' + index);
        }


        var oilID = oilIDs[index];
        if (oilID in requiredIDs) {
            target.className = "forbidden";
            delete requiredIDs[oilID];
            forbiddenIDs[oilID] = true;
        } else if (oilID in forbiddenIDs) {
            target.className = "";
            delete forbiddenIDs[oilID];
        } else {
            requiredIDs[oilID] = true;
            target.className = "required";
        }

    }

    function changeHTML ()
    {
        var e;

        e = document.getElementById ('hard');
        e.innerHTML = hardness;
        if (hardnessOK) { e.className = ""; } else { e.className = "outOfRange"; }

        e = document.getElementById ('cleansing');
        e.innerHTML = cleansing;
        if (cleansingOK) { e.className = ""; } else { e.className = "outOfRange"; }

        e = document.getElementById ('conditioning');
        e.innerHTML = conditioning;
        if (conditioningOK) { e.className = ""; } else { e.className = "outOfRange"; }

        e = document.getElementById ('fluffy');
        e.innerHTML = fluffy;
        if (fluffyOK) { e.className = ""; } else { e.className = "outOfRange"; }

        e = document.getElementById ('stable');
        e.innerHTML = stable;
        if (stableOK) { e.className = ""; } else { e.className = "outOfRange"; }

        e = document.getElementById ('ins');
        e.innerHTML = ins;
        if (insOK) { e.className = ""; } else { e.className = "outOfRange"; }

        e = document.getElementById ('water');
        e.innerHTML = 'water ' + water + '% of oil weight (' + waterMinimum + '% to discount)';

        e = document.getElementById ('lye');
        if (soapType == 'liquid') {
            e.innerHTML = 'potassium hydroxide ' + lye + '% of oil weight (superfatted at ' + superfat + '%)';
        } else {
            e.innerHTML = 'lye ' + lye + '% of oil weight (superfatted at ' + superfat + '%)';
        }
        e = document.getElementById ('description');
        e.innerHTML = narration;

        function makeAlert (index)
        {
            return function (ev) {
                ingredientClick (ev, index);
                return false;
            };
        }

        for (var i = 0; i < oilCount; ++i)
        {
            e = document.getElementById ('oil' + i);
            e.innerHTML = Math.round (quantities[i] * 100) + '% ' + oilNames[i];
            if (oilIDs[i] in requiredIDs) {
                e.className = "required";
            } else if (oilIDs[i] in forbiddenIDs) {
                e.className = "forbidden";
            } else {
                e.className = "";
            }

            e.style.display = "block";
            e.onclick = makeAlert (i);
            e.ondblclick = function () { return false; }
        }
        ingredientID = "BOGUS";
        for (i = oilCount; i < OIL_COUNT_MAX; ++i)
        {
            e = document.getElementById ('oil' + i);
            e.style.display = "none";
        }

        e = document.getElementById ('quantityEvaluated');
        e.innerHTML = formatThousands (quantityEvaluated);

        e = document.getElementById ('generation');
        e.innerHTML = formatThousands (generation);

        e = document.getElementById ('totalPopulation');
        e.innerHTML = formatThousands (totalPopulation);

        e = document.getElementById ('totalGenerations');
        e.innerHTML = formatThousands (totalGenerations);

    }


    me.getNew = function (oilSets, soapType)
    {
        if (oilSets == undefined) {
            oilSets = lastOilSets;
        }
        if (soapType == undefined) {
            soapType = lastSoapType;
        }

        lastOilSets = oilSets;
        lastSoapType = soapType;

        function callback (code, data)
        {
            if (code != 200) { return; }
            var lines = data.split ('\n');
            if (lines[0] != 'OK') { return; }
            var currentLine = 1;
            totalPopulation = lines[currentLine++];
            totalGenerations = lines[currentLine++];
            quantityEvaluated = parseInt (lines[currentLine++]);
            generation = lines[currentLine++];
            soapType = lines[currentLine++];
            narration = lines[currentLine++];
            hardness = lines[currentLine++];
            hardnessOK = lines[currentLine++];
            cleansing = lines[currentLine++];
            cleansingOK = lines[currentLine++];
            conditioning = lines[currentLine++];
            conditioningOK = lines[currentLine++];
            fluffy = lines[currentLine++];
            fluffyOK = lines[currentLine++];
            stable = lines[currentLine++];
            stableOK = lines[currentLine++];
            ins = lines[currentLine++];
            insOK = lines[currentLine++];
            water = lines[currentLine++];
            waterMinimum = lines[currentLine++];
            superfat = lines[currentLine++];
            lye = lines[currentLine++];
            oilCount = parseInt (lines[currentLine++]);
            for (var i = 0; i < oilCount; ++i)
            {
                oilIDs[i] = parseInt (lines[currentLine++]);
                oilNames[i] = lines[currentLine++];
                quantities[i] = parseFloat (lines[currentLine++]);
            }

            changeHTML ();
        }

        var requiredIDList = [];
        for (var i in requiredIDs) { requiredIDList[requiredIDList.length] = i; }
        var forbiddenIDList = [];
        for (var i in forbiddenIDs) { forbiddenIDList[forbiddenIDList.length] = i; }


        new RemoteRequest ('/newRecipe.cgi', callback, 'GET', {oilSets: oilSets, type: soapType, required: requiredIDList.join (','), forbidden: forbiddenIDList.join (',')});
    } 

    document.onkeydown = function (ev)
    {
        var char = String.fromCharCode (ev.which);
        //console.debug ("Keypress: " + char);

        if (char == 'R')
        {
            me.getNew ();
            return false;
        }

        if (char >= '0' && char <= '9')
        {
            var slot = parseInt (char);
            if (slot >= oilCount) { return true; }
            ingredientClick (null, slot);
            return false;
        }

        //console.debug ("Ignoring key: " + char);

        return true;
    }

}

function getQueryDict ()
{
    var r = new Object;
    var queryStr = window.location.search.substring (1);
    var paramStrs = queryStr.split ('&');
    for (var i = 0; i < paramStrs.length; ++i)
    {
        var splitPos = paramStrs[i].indexOf ('=');
        if (splitPos > -1)
        {
            var key = paramStrs[i].substring (0, splitPos);
            var val = paramStrs[i].substring (splitPos+1);
            r[key] = val;
        } else {
            r[paramStrs[i]] = true;
        }

    }
    return r;
}

