///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
// The helper functions can be found at:
// http://grimore.org/fuss:lsl
//////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
// Returns a reversed list.
//////////////////////////////////////////////////////////
list wasListReverse(list lst) {
    if(llGetListLength(lst)<=1) return lst;
    return wasListReverse(llList2List(lst, 1, llGetListLength(lst))) + llList2List(lst,0,0);
}
 
//////////////////////////////////////////////////////////
// Deletes elements delete from list input.
//////////////////////////////////////////////////////////
list wasSubtractSubList(list input, list delete) {
    do {
        string tok = llList2String(delete, 0);
        list clean = input;
        do {
            if(llList2String(clean, 0) == tok) {
                integer idx = llListFindList(input, (list)tok);
                input = llDeleteSubList(input, idx, idx);
            }
        } while(clean = llDeleteSubList(clean, 0, 0));
    } while(delete = llDeleteSubList(delete, 0, 0));
    return input;
}
 
//////////////////////////////////////////////////////////
// Returns a list of operators and operands.
//////////////////////////////////////////////////////////
list wasInfixTokenize(string input) {
    list op = [ "+", "-", "(", ")", "%", "*", "/", "^", "sin", "asin", "cos", "acos", "tan", "sqrt", "ln" ];
    list result = llParseString2List(input, [], op);
    return wasSubtractSubList(result, [" "]);
}
 
//////////////////////////////////////////////////////////
// Transforms an infix expression to a postfix expression.
//////////////////////////////////////////////////////////
list wasInfixToPostfix(list infix) {
    list op = [  "+", "-", "%", "*", "/", "^", "sin", "asin", "cos", "acos", "tan", "sqrt", "ln" ];
    list opStack = [];
    list result = [];
    do {
        string t = llList2String(infix, 0);
        infix = llDeleteSubList(infix, 0, 0);
        if(t == "(") {
            opStack += "(";
            jump continue;
        }
        if(t == ")") {
            while(llGetListLength(opStack) != 0) {
                string topa = llList2String(opStack, llGetListLength(opStack)-1);
                opStack = llDeleteSubList(opStack, llGetListLength(opStack)-1, llGetListLength(opStack)-1);
                if(topa != "(" && topa != ")") result += topa;
            }
            opStack = llDeleteSubList(opStack, llGetListLength(opStack)-1, llGetListLength(opStack)-1);
            jump continue;
        }
        integer idx = llListFindList(op, (list)t);
        if(idx == -1) {
            result += t;
            jump continue;
        }
@repeat;
        string topb = llList2String(opStack, llGetListLength(opStack)-1);
        integer odx = llListFindList(op, (list)topb);
        if(odx >= idx) {
            opStack = llDeleteSubList(opStack, llGetListLength(opStack)-1, llGetListLength(opStack)-1);
            result += topb;
            if(llGetListLength(opStack) != 0) jump repeat;
        }
        opStack += t;
@continue;
    } while(llGetListLength(infix) != 0);
    result += wasListReverse(opStack);
    return result;   
}
 
//////////////////////////////////////////////////////////
// Evaluate a postfix expression.
//////////////////////////////////////////////////////////
float wasPostfixEval(list postfix) {
    list op = [ "+", "-", "%", "*", "/", "^", "sin", "asin", "cos", "acos", "tan", "sqrt", "ln" ];
    list orStack = [];
    do {
        string t = llList2String(postfix, 0);
        postfix = llDeleteSubList(postfix, 0, 0);
        integer idx = llListFindList(op, (list)t);
        if(idx == -1) {
            orStack += t;
            jump continue;
        }
        float a = llList2Float(orStack, llGetListLength(orStack)-1);
        orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
        float b = llList2Float(orStack, llGetListLength(orStack)-1);
        float r = 0;
        if(t == "+") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = b + a;
            jump push;
        }
        if(t == "-") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = b - a;
            jump push;
        }
        if(t == "*") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = b * a;
            jump push;
        }
        if(t == "/") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            if(a == 0) {
                r = (float)"NaN";
                jump push;
            }
            r = b / a;
            jump push;
        }
        if(t == "^") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = llPow(b,a);
            jump push;
        }
        if(t == "%") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = (integer)b % (integer)a;
            jump push;
        }
        if(t == "sin") {
            r = llSin(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "asin") {
            r = llAsin(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "cos") {
            r = llCos(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "acos") {
            r = llAcos(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "tan") {
            r = llTan(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "ln") {
            r = llLog(a);
            jump push;
        }
        if(t == "sqrt") {
            r = llSqrt(a);
        }
@push;
        orStack += r;
@continue;
    } while(llGetListLength(postfix) != 0);
    return llList2Float(orStack, 0);
}
 
list cmd = [];
 
default {
    link_message(integer sender_num, integer num, string str, key id) {
        cmd = llCSV2List(str);
        if(llList2String(cmd,1) != "calculate") return;
        llMessageLinked(LINK_THIS, 0, "glow:MAIN:HALT", NULL_KEY);
        state calculate;
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state calculate {
    state_entry() {
        llSay(0, "I evaluated that to: " + (string)wasPostfixEval(wasInfixToPostfix(wasInfixTokenize(llDumpList2String(llList2List(cmd, 2, -1),"")))));
        llMessageLinked(LINK_THIS, 0, "glow:MAIN:CONT", NULL_KEY);
        state default;
    }
    link_message(integer sender_num, integer num, string str, key id) {
        if(str != "glow:PLUG:HALT") return;
        state default;
    }
    on_rez(integer num) {
        llResetScript();
    }
}