///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2021 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
    integer i = llGetListLength(data);
    if (i % 2 != 0 || i == 0) return "";
    --i;
    do {
        data = llListInsertList(
            llDeleteSubList(
                data, 
                i-1, 
                i
            ),
            [ llList2String(data, i-1) + "=" + llList2String(data, i) ], 
            i-1
        );
        i -= 2;
    } while(i > 0);
    return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
vector wasUpperHemiSpherePoint(float radius) {
    float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    float z = llFrand(radius*2);
    if(llPow(x,2) + llPow(y,2) + llPow(z,2) <= llPow(radius,2))
        return <x, y, z>;
    return wasUpperHemiSpherePoint(radius);
}
 
// for notecard reading
integer line = 0;
 
// key-value data will be read into this list
list tuples = [];
 
default {
    state_entry() {
        if(llGetInventoryType(llGetInventoryName(INVENTORY_NOTECARD, 0)) != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Sorry, could not find a configuration notecard inside the object.");
            return;
        }
        llGetNotecardLine(
            llGetInventoryName(
                INVENTORY_NOTECARD, 
                0
            ),
            line
        );
    }
    dataserver(key id, string data) {
        if(data == EOF) state spawn; // invariant, length(tuples) % 2 == 0
        // Skip empty lines.
        if(data == "") {
            llGetNotecardLine(
                llGetInventoryName(
                    INVENTORY_NOTECARD,
                    0
                ), 
                ++line
            );
            return;
        }
        integer i = llSubStringIndex(data, "#");
        if(i != -1) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        // get rid of starting and ending quotes
        string k = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o, 
                        0
                    ), 
                STRING_TRIM), 
            ["\""], []
        ), "\"");
        string v = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o, 
                        1
                    ), 
                STRING_TRIM), 
            ["\""], []
        ), "\"");
        if(k == "" || v == "") {
            llGetNotecardLine(
                llGetInventoryName(
                    INVENTORY_NOTECARD,
                    0
                ), 
                ++line
            );
            return;
        }
        tuples += k;
        tuples += v;
        llGetNotecardLine(
            llGetInventoryName(
                INVENTORY_NOTECARD,
                0
            ), 
            ++line
        );
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state spawn {
    state_entry() {
        llSetTimerEvent(
            1.0/llList2Float(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "rate"
                    ]
                )+1
            )
        );
        llSensorRepeat("", NULL_KEY, AGENT, 0.1, 0.1, 
            1.0/llList2Float(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "rate"
                    ]
                )+1
            )
        );
    }
    no_sensor() {
        llRezObject(
            llGetInventoryName(
                INVENTORY_OBJECT, 
                (integer)llFrand(
                    llGetInventoryNumber(INVENTORY_OBJECT)
                )
            ), 
            llGetPos() + wasUpperHemiSpherePoint(
                llList2Float(
                    tuples, 
                    llListFindList(
                        tuples, 
                            [
                                "radius"
                            ]
                        )+1
                    )
                ), 
            ZERO_VECTOR, 
            llEuler2Rot(
                (vector)llList2String(
                    tuples,
                    llListFindList(
                        tuples,
                        [
                            "rotation"
                        ]
                    )+1
                ) * DEG_TO_RAD
            ), 
            (integer)(
                "0x8" + llGetSubString(llGetKey(), 0, 6)
            )
        );
    }
    timer() {
        llRegionSay(
            (integer)("0x8" + llGetSubString(llGetKey(), 0, 6)), 
            wasKeyValueEncode(tuples)
        );
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
    on_rez(integer num) {
        llResetScript();
    }
}