//--------------------------------------------------------------------------
// ddp_params.cpp
//--------------------------------------------------------------------------

#include "ddp/ddp_params.h"
#include "base/AT_StringFunctions.h"

void DDP_Params::Param::Clear()
{
    switch(Type)
        {
        case PARAM_INT:
            Num = 0;
        break;
        case PARAM_OBJECT:
            VObject.Object = NULL;
            VObject.Size = 0;
        break;
        case PARAM_TEXT:
            delete Text;
            Text = NULL;
        break;
        case PARAM_PARAMS:
            delete Params;
            Params = NULL;
        break;
        };
}
//--------------------------------------------------------------------------

DDP_Params::Param::Param(const Param & rhs) : Type(PARAM_UNKNOWN), Text(NULL), Params(NULL), Num(0)
{
    VObject.Object = NULL;
    VObject.Size = 0;
    
    operator=(rhs);
}
//--------------------------------------------------------------------------

DDP_Params::ParamType DDP_Params::Param::FindType(const byte * type) const
{
    if(!type || !*type)
        return PARAM_TEXT; // default text type
    if(!PORT_stricmp(type, "TEXT"))
        return PARAM_TEXT;
    if(!PORT_stricmp(type, "TEXT"))
        return PARAM_TEXT;
    if(!PORT_stricmp(type, "INT") ||
        !PORT_stricmp(type, "NUM"))
        return PARAM_INT;
    if(!PORT_stricmp(type, "OBJ") ||
        !PORT_stricmp(type, "OBJECT"))
        return PARAM_OBJECT;
    if(!PORT_stricmp(type, "PARAMS") ||
        !PORT_stricmp(type, "PARAM"))
        return PARAM_PARAMS;

return PARAM_UNKNOWN;
}
//--------------------------------------------------------------------------

const byte * DDP_Params::Param::GetTypeString(ParamType type) const
{
    switch(type)
        {
        case PARAM_TEXT:
            return PORT_cbyte("TEXT");
        case PARAM_INT:
            return PORT_cbyte("INT");
        case PARAM_OBJECT:
            return PORT_cbyte("OBJECT");
        case PARAM_PARAMS:
            return PORT_cbyte("PARAMS");
        }

return PORT_cbyte("UNKNOWN");
}
//--------------------------------------------------------------------------

DDP_Params::Param::Param(const byte * name, const byte * str, ParamType type) :
    Type(PARAM_UNKNOWN), Text(NULL), Params(NULL), Num(0)
{
    VObject.Object = NULL;
    VObject.Size = 0;

    SetParam(name, str, type);
}
//--------------------------------------------------------------------------

void DDP_Params::Param::SetParam(const byte * name, const byte * val, ParamType type)
{
    if(!val || !*val)
        return;
        
    std::string swork;
    if(name)
        {
        swork = (char *)name;
        swork += ":";
        }

    swork += (char *)val;
    if(type != PARAM_UNKNOWN)
        {
        swork += "/t";
        swork += (char *)GetTypeString(type);
        }

    SetParam((const byte *)swork.c_str());
}
//--------------------------------------------------------------------------

void DDP_Params::Param::SetParam(const byte * buf)
{
    Clear();

    if(!buf || !*buf)
        return;

// param buffer:
// [param name:]value/t[Type: TEXT, INT, PARAMS]
// default type: TEXT

    std::string sval;
    std::string stype;
    std::string swork((char *)buf);

    // find type
    uint pos = UTL_String::RGetSubStrRight(swork, PORT_cbyte("/t"), stype);
    if(pos != std::string::npos)
        {
        swork.erase(pos);
        Type = FindType(PORT_c_str(stype));
        }

    // find val
    pos = UTL_String::GetSubStrRight(swork, PORT_cbyte(":"), sval);
    if(pos == std::string::npos)
        // no name.
        sval = swork;
    else
        {
        // clean up name from value
        swork.erase(pos);
        Name = swork;
        }

    switch(Type)
        {
        case PARAM_TEXT:
            Text = new std::string(sval);
        break;
        case PARAM_INT:
            Num = std::atoi(sval.c_str());
        break;
        case PARAM_OBJECT:
            std::sscanf(sval.c_str(), "%p%u", &VObject.Object, &VObject.Size);
        break;
        case PARAM_PARAMS:
            Params = new DDP_Params((const byte *)sval.c_str());
        break;
        }
}
//--------------------------------------------------------------------------

uint DDP_Params::Param::GetParam(std::vector<byte> & buf) const
{
    switch(Type)
        {
        case PARAM_TEXT:
            if(Text)
                {
                buf.resize(Text->length() + 1, 0);
                return PORT_snprintf_1(&buf[0], buf.size(), "%s", Text->c_str());
                }
        break;
        case PARAM_INT:
            buf.resize(40);
            return PORT_snprintf_1(&buf[0], buf.size(), "%u", Num);
        case PARAM_OBJECT:
            buf.resize(VObject.Size + 1);
            return PORT_snprintf_1(&buf[0], buf.size(), "%p%u", VObject.Object, VObject.Size);
        case PARAM_PARAMS:
            if(Params)
                return Params->PrintToBuf(buf);
        break;
        }
        
return 0;
}
//--------------------------------------------------------------------------

uint DDP_Params::Param::PrintToBuf(std::vector<byte> & buf) const
{
// param buffer:
// [param name:]value/t[Type: TEXT, INT, PARAMS]
// default type: TEXT
       
    std::vector<byte> sval;
    uint size = GetParam(sval);
    if(!size)
        return 0;

    buf.resize(sval.size() + Name.length() + PARAM_SIZE);
    if(Name.length())
        return PORT_snprintf_1(&buf[0], buf.size(), "\"%s:%s/t%s\"", Name.c_str(), sval, GetTypeString(Type));

return PORT_snprintf_1(&buf[0], buf.size(), "\"%s/t%s\"", sval, GetTypeString(Type));
}
//--------------------------------------------------------------------------

DDP_Params::Param & DDP_Params::Param::operator = (const Param & rhs)
{
    Clear();
    
    Type = rhs.Type;
    Name = rhs.Name;

    switch(Type)
        {
        case PARAM_INT:
            Num = rhs.Num;
        break;
        case PARAM_OBJECT:
            VObject = rhs.VObject;
        break;
        case PARAM_TEXT:
            Text = new std::string(*rhs.Text);
        break;
        case PARAM_PARAMS:
            Params = new DDP_Params(*rhs.Params);
        break;
        };

return *this;
}
//--------------------------------------------------------------------------

bool DDP_Params::Param::operator ==(const byte * name) const
{
    if(!name || !*name)
        return false;
        
    if(!PORT_stricmp(Name.c_str(), name))
        return true;
        
return false;
}
//--------------------------------------------------------------------------

DDP_Params::DDP_Params(const byte * str)
{
// load from string:
// format: <param:value>[/t[TEXT, INT, PARAM]] <param:value>[/t[TEXT, INT, PARAM]]
// default type - TEXT. Use double quotes if param has spaces

    if(!str || !*str)
        return;
    std::string param;
    std::string type;
    std::string work((char *)str);

    uint pos = 0;
    for(;;)
        {
        pos = UTL_String::GetTokBetween(pos, work, PORT_cbyte(" "), PORT_cbyte("\""), param);
        if(pos == std::string::npos)
            break;
        Params.push_back(new Param(NULL, PORT_c_str(param)));        
        ++pos;
        }
}
//--------------------------------------------------------------------------

void DDP_Params::Clear()
{
    uint i, cnt = GetParamCount();
    for(i = 0; i < cnt; ++i)
        {
        const Param * param = get_param(i);
        delete param;
        }
    Params.clear();
}
//--------------------------------------------------------------------------

void DDP_Params::_copy(const DDP_Params & rhs)
{
    Clear();
    uint i, cnt = rhs.GetParamCount();
    for(i = 0; i < cnt; ++i)
        Params.push_back(new Param(*rhs.get_param(i)));
}
//--------------------------------------------------------------------------

uint DDP_Params::find_param_pos(const byte * name) const
{
    int i, cnt = GetParamCount();
    for(i = cnt - 1; i >= 0; --i)
        {
        const Param * param = get_param(i);
        if(param && *param == name)
            return i;
        }
        
return MAXUINT;
}
//--------------------------------------------------------------------------

const DDP_Params::Param * DDP_Params::find_param(const byte * name) const
{
    uint pos = find_param_pos(name);
    if(pos != MAXUINT)
        return get_param(pos);
        
return NULL;
}
//--------------------------------------------------------------------------

void DDP_Params::SetParam(const byte * param, const byte * val, ParamType type)
{
    Param * p = (Param *)find_param(param);
    if(p)
        p->SetParam(param, val, type);
    else
        Params.push_back(new Param(param, val, type));
}
//--------------------------------------------------------------------------

void DDP_Params::SetParam(const byte * param, uint val)
{
    byte buf[35];
    std::sprintf((char *)buf, "%u", val);
    SetParam(param, buf, PARAM_INT);
}
//--------------------------------------------------------------------------

void DDP_Params::SetParam(const byte * param, const void * obj, uint size)
{
    byte buf[100];
    std::sprintf((char *)buf, "%p%u", obj, size);
    SetParam(param, buf, PARAM_OBJECT);
}
//--------------------------------------------------------------------------

void DDP_Params::SetParam(const byte * param, const DDP_Params * params)
{
    if(!params)
        return ;

    std::vector<byte> buf;
    if(!params->PrintToBuf(buf))
        return ;

    SetParam(param, &buf[0], buf.size());
}
//--------------------------------------------------------------------------

uint DDP_Params::GetInt(uint num) const
{
    const Param * p = get_param(num);
    if(!p)
        return 0;

    switch(p->Type)
        {
        case PARAM_INT:
            return p->Num;
        case PARAM_OBJECT:
            return p->VObject.Size;
        case PARAM_TEXT:
            if(p->Text)
                return std::atoi(p->Text->c_str());
        break;
        case PARAM_PARAMS:
            if(p->Params)
                return p->Params->GetParamCount();
        break;
        }

return 0;
}
//--------------------------------------------------------------------------

void * DDP_Params::GetObject(uint num, uint * sz) const
{
    uint size;
    sz = sz ? sz : &size;
    const Param * p = get_param(num);
    if(!p)
        {
        *sz = 0;
        return NULL;
        }

    switch(p->Type)
        {
        case PARAM_INT:
            {
            *sz = sizeof(p->Num);
            return (void *)&p->Num;
            }
        case PARAM_OBJECT:
            {
            *sz = p->VObject.Size;
            return p->VObject.Object;
            }
        case PARAM_TEXT:
            {
            if(p->Text)
                {
                *sz = p->Text->length();
                return (void *)p->Text->c_str();
                }
            }
        break;
        case PARAM_PARAMS:
            if(p->Params)
                {
                *sz = p->Params->GetParamCount();
                return (void *)p->Params;
                }
        break;
        }

    *sz = 0;
return NULL;
}
//--------------------------------------------------------------------------

DDP_Params DDP_Params::GetParams(uint num) const
{
    const Param * p = get_param(num);
    if(!p)
        return DDP_Params();

    if(p->Type == PARAM_PARAMS)
        {
        if(p->Params)
            return DDP_Params(*(p->Params));
        else
            return DDP_Params();
        }

    std::vector<byte> buf;
    if(p->PrintToBuf(buf))
        return DDP_Params(&buf[0]);

return DDP_Params();
}
//--------------------------------------------------------------------------

std::string DDP_Params::GetText(uint num) const
{
    const Param * p = get_param(num);
    if(!p)
        return std::string();

    switch(p->Type)
        {
        case PARAM_TEXT:
            if(p->Text)
                return std::string(*p->Text);
        return std::string();
        case PARAM_OBJECT:
            if(p->VObject.Object)
                return std::string((char *)p->VObject.Object, p->VObject.Size);
        return std::string();
        case PARAM_INT:
            {
            byte snum[35];
            PORT_snprintf_1(snum, sizeof(snum), "%u", p->Num);
            return std::string((char *)snum);
            }
        case PARAM_PARAMS:
            {
            if(p->Params)
                {
                std::vector<byte> params;
                if(p->Params->PrintToBuf(params))
                    return std::string(PORT_char(&params[0]), params.size());
                }
            break;
            }
        }

return std::string();
}
//--------------------------------------------------------------------------

uint DDP_Params::PrintToBuf(std::vector<byte> & buf) const
{
    uint i, cnt = GetParamCount();
    uint size = 0;
    
    for(i = 0; i < cnt; ++i)
        {
        const Param * param = get_param(i);
        if(!param)
            continue;

        std::vector<byte> b;
        uint ss = param->PrintToBuf(b);
        if(!ss)
            return 0;

        size += ss;

        if(i < cnt - 1)
            {
            b.push_back(' ');
            ++size;
            }
        buf.insert(buf.end(), b.begin(), b.end());
        }
        
return size;
}
//--------------------------------------------------------------------------

