// RICP : Robot Information/Control Protocol
// Vendor Sample
//
// This script handles vendors with configuration files
// Example include JEVN and many other vendors
//
// You can configure the script by setting the variables
// below so that it can read most any format.
//
// It will also read configuration files that use multiple
// lines for a single product.
//
// Feel free to modify this script if it does not work
// for you.
//
// Created: 2007-08-18
// Authors: Diva Canto and Felix Wakmann
//
// Copyright (c) 2007 by The Regents of the University of California.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.


// struture of the config file
string CONFIG_FILE_LABEL = "_items"   ;
string COMMENT_MARK      = "#"        ;
string SEPARATOR         = ";"        ;

// set MULTI_LINE to true for multi-line config files
integer MULTI_LINE = FALSE    ;

// for single line
//-----------------------------------------
// each line is a list of items with a separator, as in 
// price ; texture name ; item name ; item info ; description ; discounted price; category; limited; type
// 99; pic_Swallowtail -Small- Living Butterfly Wings ; Swallowtail -Large- Living Butterfly Wings ;; Large Living Butterfly Wings ; 
//-----------------------------------------
integer PRICE_NUMBER =        1    ;
integer IMAGE_NUMBER =        2    ;
integer TITLE_NUMBER =        4    ;
integer ALT_TITLE_NUMBER =    3    ;
integer DESCRIPTION_NUMBER =  5    ;
integer LOCATION_NUMBER =    -1    ;

// for multiline
// each entry is a series of tagged lines, for example:
//-----------------------------------------
// price:: 99
// image:: Swallowtail -Small- Living Butterfly Wings.image
// title:: Swallowtail -Large- Living Butterfly Wings
//-----------------------------------------
string MULTI_LINE_SEPARATOR =  ""               ;
string TITLE_LABEL           = "title"          ;
string ALT_TITLE_LABEL       = "item"           ;
string DESCRIPTION_LABEL     = "description"    ;
string IMAGE_LABEL           = "image"          ;
string PRICE_LABEL           = "price"          ;
string LOCATION_LABEL        = ""               ;

// the offset is used to pick a point in *front* of the vendor
vector gOffset = <0, -1.5, 0>;

// prints out location
integer gDebug = FALSE;



// ******** Internal Variables ********

// handling request
integer SLRCP_channel = 1020304;

key gTarget;
string gDefaultLocation;
string gParcel;

// reading notecards
string gUploadFileName;
integer gUploadLine;
key gLineRequestID;

// current values
string gTitle;
string gAltTitle;
string gDescription;
string gImage;
string gPrice;
string gLocation;

string gSecret;

// send information to the robot
publish()
{
    // no description
    string loc = gLocation;
    if (loc == "")
        loc = gDefaultLocation;
    string title = gTitle;
    if (title == "")
        title = gAltTitle;
    string image = llGetInventoryKey(gImage);
    if (image == "" || image == NULL_KEY)
        image = gImage;
        
    string info = 
        "product?tag=" + gSecret +
            "&title=" + llEscapeURL(title) + 
            "&id=" + (string)llGetInventoryKey(gAltTitle) +
            "&description=" + llEscapeURL(gDescription) + 
            "&image=" + llEscapeURL(image) + 
            "&location=" + llEscapeURL(loc) + 
            "&price=" + llEscapeURL(gPrice);
    llInstantMessage(gTarget, info);
    gTitle = gAltTitle = gDescription = gImage = gPrice = gLocation = "";
}

// Data Handling

startScan(key target, string file)
{
    gTarget = target;
    gUploadFileName = file;
    gUploadLine = 0;
    vector pos = llGetPos() + gOffset * llGetRot();
    gDefaultLocation = llGetRegionName() + "/" + ftos(pos.x) + "/" + ftos(pos.y) + "/" + ftos(pos.z);
    if (gDebug)
        llOwnerSay("secondlife://" + gDefaultLocation);
    list details = llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME]);
    gParcel = llList2String(details, 0);
    
    gSecret = (string)llFrand(1000000);
    list detailParts = llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_OWNER]);
    string owner = llList2String(detailParts, 0);
    string msg = "verify?tag=" + gSecret + "&owner=" + owner;
    // THis command sets the media URL *only* for the robot!
    // It does not affect other users
    llParcelMediaCommandList( [
        PARCEL_MEDIA_COMMAND_AGENT, target,
        PARCEL_MEDIA_COMMAND_URL, msg
        ]);
    
    queryLine();
}

// read a line form the data server
queryLine()
{
    gLineRequestID = llGetNotecardLine(gUploadFileName, ++gUploadLine);
}

// extract the fields the line (for multi-line, it is just one field)
translateLine(string data)
{
    if (startsWith(data, COMMENT_MARK))
        return;
    list parts = llParseStringKeepNulls(data, [SEPARATOR], []);
    data = ""; // save space
    if (llGetListLength(parts) < 2)
        return;
    if (MULTI_LINE)
    {
        if (startsWith(data, MULTI_LINE_SEPARATOR))
            publish();
        string field = llStringTrim(llToLower(llList2String(parts, 0)), STRING_TRIM );
        string value = llList2String(parts, 1);
        if (field == TITLE_LABEL)
            gTitle = value;
        else if (field == ALT_TITLE_LABEL)
            gAltTitle = value;
        else if (field == IMAGE_LABEL)
            gImage = value;
        else if (field == PRICE_LABEL)
            gPrice = value;
        else if (field == DESCRIPTION_LABEL)
            gDescription = value;
        else if (field == LOCATION_LABEL)
            gLocation = value;
    }
    else
    {
        gTitle      = get(parts, TITLE_NUMBER, "");
        gAltTitle   = get(parts, ALT_TITLE_NUMBER, "");
        gDescription = get(parts, DESCRIPTION_NUMBER, "");
        gImage      = get(parts, IMAGE_NUMBER, ""); 
        gPrice      = get(parts, PRICE_NUMBER, "");
        gLocation   = get(parts, LOCATION_NUMBER, gDefaultLocation);
        publish();
    }
}

// get a value from a list, or return a default value
string get(list parts, integer index, string defaultVal)
{
    if (index < 1)
        return defaultVal;
    else
        return llStringTrim(llList2String(parts, index - 1), STRING_TRIM );
}

// test if a string starts with a prefix
integer startsWith(string str, string prefix)
{
    integer len =  llStringLength(prefix);
    if (len == 0)
        return TRUE;
    return llGetSubString(str, 0, len) == prefix;
}


string ftos(float n)
{
    return (string)((integer) (n + 0.5));
}


default
{
    // set up the listener
    state_entry()
    {
        llListen(SLRCP_channel, "", "", "");
    }
    
    // listen for a robot
    listen(integer chan, string name, key id, string msg)
    {
        // The incoming message msg looks like this:
        // robot|
        list parts = llParseStringKeepNulls(msg, ["|"], []);
        key botKey;
        if (llList2String(parts, 0) == "robot") 
        {
            key bot = llList2String(parts, 1);
            if (bot == "")
                bot = id;
                
            startScan(bot, CONFIG_FILE_LABEL);
        }
    }

    // handle data server returns
    dataserver(key qid, string data)
    {
        if (qid != gLineRequestID)
            return;
        if (data != EOF)
        {
            translateLine(data);
            queryLine();
        }
        else if (gTitle != "" || gAltTitle != "" || gImage != "")
        { 
            // if we have data after the last line, then publish it
            publish();
        }
    }
    
}