/*---------------------------------------------------------------------------*/
/* client_config.cpp                                                         */
/* copyright (c) innovaphone 2018                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"
#include "common/ilib/json.h"
#include "client_config.h"

/*---------------------------------------------------------------------------*/

class ClientConfigKeyValueItem : public btree {
    friend class IClientConfigKeyValueStore;

    char * id;
    char * val;

    int btree_compare(void * key) { return strcmp((char *)key, id); };
    int btree_compare(class btree * b) { return strcmp(((class ClientConfigKeyValueItem *)b)->id, id); };
public:
    ClientConfigKeyValueItem(const char * id, const char * val) {
        this->id = _strdup(id);
        this->val = _strdup(val);
    }
    ~ClientConfigKeyValueItem() {
        if (id) free(id);
        if (val) free(val);
    }
};

/*---------------------------------------------------------------------------*/

const char * ClientConfigIdName(const ClientConfigIdNameItem * map, int id)
{
    unsigned i = 0;
    while (map[i].name && (map[i].id != id)) {
        i++;
    }
    return map[i].name;
}

int ClientConfigNameId(const ClientConfigIdNameItem * map, const char * name)
{
    unsigned i = 0;
    if (!name) {
        while (map[i].name) {
            i++;
        }
    }
    else {
        while (map[i].name && (strcmp(map[i].name, name) != 0)) {
            i++;
        }
    }
    return map[i].id;
}

/*---------------------------------------------------------------------------*/

IClientConfigItem::IClientConfigItem(IClientConfigItems * items, const char * name) :
    items(items),
    name(_strdup(name))
{
}

IClientConfigItem::~IClientConfigItem()
{
    free(name);
}

int IClientConfigItem::btree_compare(void * key)
{
    return strcmp((char *)key, name);
}

int IClientConfigItem::btree_compare(class btree * b)
{
    return strcmp(((class IClientConfigItem *)b)->name, name);
}

/*---------------------------------------------------------------------------*/

IClientConfigBasicItem::IClientConfigBasicItem(class IClientConfigItems * items, const char * name) :
    IClientConfigItem(items, name)
{
    items->Add(this);
}

IClientConfigBasicItem::~IClientConfigBasicItem()
{
    items->Remove(this);
}

/*---------------------------------------------------------------------------*/

IClientConfigBool::IClientConfigBool(IClientConfigItems * items, UClientConfigBool * user, const char * name, bool init) :
    IClientConfigBasicItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

IClientConfigBool::~IClientConfigBool()
{
}

void IClientConfigBool::Set(bool newValue)
{
    value = newValue;
    items->ItemSet(this);
    user->Apply(this);
}

void IClientConfigBool::ToJson(class json_io & json, word base, char * & tmp)
{
    json.add_bool(base, name, value);
}

void IClientConfigBool::FromJson(class json_io & json, word base)
{
    value = json.get_bool(base, name);
}

/*---------------------------------------------------------------------------*/

IClientConfigEnum::IClientConfigEnum(IClientConfigItems * items, UClientConfigEnum * user, const char * name, const ClientConfigIdNameItem * map, int init) :
    IClientConfigBasicItem(items, name),
    user(user),
    map(map),
    value(init)
{
    value = ClientConfigNameId(map, NULL);
    items->ItemAdded(this);
}

IClientConfigEnum::~IClientConfigEnum()
{
}

IClientConfigEnum::IClientConfigEnum(IClientConfigItems * items, UClientConfigEnum * user, const char * name, const ClientConfigIdNameItem * map) :
    IClientConfigEnum(items, user, name, map, ClientConfigNameId(map, NULL))
{
}

void IClientConfigEnum::Set(int newValue)
{
    value = newValue;
    items->ItemSet(this);
    user->Apply(this);
}

void IClientConfigEnum::ToJson(class json_io & json, word base, char * & tmp)
{
    json.add_string(base, name, ClientConfigIdName(map, value));
}

void IClientConfigEnum::FromJson(class json_io & json, word base)
{
    value = ClientConfigNameId(map, json.get_string(base, name));
}

/*---------------------------------------------------------------------------*/

IClientConfigInt::IClientConfigInt(IClientConfigItems * items, UClientConfigInt * user, const char * name, int init) :
    IClientConfigBasicItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

IClientConfigInt::~IClientConfigInt()
{
}

void IClientConfigInt::Set(int newValue)
{
    value = newValue;
    items->ItemSet(this);
    user->Apply(this);
}

void IClientConfigInt::ToJson(class json_io & json, word base, char * & tmp)
{
    json.add_int(base, name, value, tmp);
}

void IClientConfigInt::FromJson(class json_io & json, word base)
{
    value = json.get_int(base, name);
}

/*---------------------------------------------------------------------------*/

IClientConfigUnsigned::IClientConfigUnsigned(IClientConfigItems * items, UClientConfigUnsigned * user, const char * name, unsigned init) :
    IClientConfigBasicItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

IClientConfigUnsigned::~IClientConfigUnsigned()
{
}

void IClientConfigUnsigned::Set(unsigned newValue)
{
    value = newValue;
    items->ItemSet(this);
    user->Apply(this);
}

void IClientConfigUnsigned::ToJson(class json_io & json, word base, char * & tmp)
{
    json.add_unsigned(base, name, value, tmp);
}

void IClientConfigUnsigned::FromJson(class json_io & json, word base)
{
    value = json.get_unsigned(base, name);
}

/*---------------------------------------------------------------------------*/

IClientConfigString::IClientConfigString(IClientConfigItems * items, UClientConfigString * user, const char * name, const char * init) :
    IClientConfigBasicItem(items, name),
    user(user),
    value(_strdup(init))
{
    items->ItemAdded(this);
}

IClientConfigString::~IClientConfigString()
{
    if (value) free(value);
}

void IClientConfigString::Set(const char * newValue)
{
    if (value) free(value);
    value = _strdup(newValue);
    items->ItemSet(this);
    user->Apply(this);
}

void IClientConfigString::ToJson(class json_io & json, word base, char * & tmp)
{
    json.add_string(base, name, value);
}

void IClientConfigString::FromJson(class json_io & json, word base)
{
    if (value) free(value);
    value = _strdup(json.get_string(base, name));
}

/*---------------------------------------------------------------------------*/

IClientConfigKeyValueStore::IClientConfigKeyValueStore(IClientConfigItems * items, UClientConfigKeyValueStore * user, const char * name) :
    IClientConfigBasicItem(items, name),
    user(user),
    store(NULL)
{
    items->ItemAdded(this);
}

IClientConfigKeyValueStore::~IClientConfigKeyValueStore()
{
    while (store) {
        class ClientConfigKeyValueItem * p = (class ClientConfigKeyValueItem *)(store);
        store = store->btree_get(p);
        delete p;
    }
}

void IClientConfigKeyValueStore::SetItem(const char * id, const char * val)
{
    class btree *b = store->btree_find(id);
    if (b) {
        class ClientConfigKeyValueItem * p = (class ClientConfigKeyValueItem *)b;
        if (p->val) free(p->val);
        p->val = _strdup(val);
    }
    else {
        class ClientConfigKeyValueItem * p = new ClientConfigKeyValueItem(id, val);
        store = store->btree_put(p);
    }
    items->ItemSet(this);
}

char * IClientConfigKeyValueStore::GetItem(const char * id)
{
    class btree * b = store->btree_find(id);
    return (b ? _strdup(((class ClientConfigKeyValueItem *)b)->val) : NULL);
}

void IClientConfigKeyValueStore::ToJson(class json_io & json, word base, char * & tmp)
{
    if (store) {
        word baseObject = json.add_object(base, name);
        for (class btree * b = store->btree_find_left(); b; b = store->btree_find_next_left(((class ClientConfigKeyValueItem *)b)->id)) {
            class ClientConfigKeyValueItem *p = (class ClientConfigKeyValueItem *)b;
            json.add_string(baseObject, p->id, p->val);
        }
    }
}

void IClientConfigKeyValueStore::FromJson(class json_io & json, word base)
{
    while (store) {
        class ClientConfigKeyValueItem * p = (class ClientConfigKeyValueItem *)(store);
        store = store->btree_get(p);
        delete p;
    }
    word baseObject = json.get_object(base, name);
    if (baseObject != JSON_ID_NONE) {
        word handle = 0;
        const char *val = json.get_string(baseObject, handle);
        while(handle != JSON_ID_NONE) {
            if(!store->btree_find(json.get_name(handle))) {
                class ClientConfigKeyValueItem * p = new ClientConfigKeyValueItem(json.get_name(handle), val);
                store = store->btree_put(p);
            }
            val = json.get_string(baseObject, handle);
        }
    }
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminItem::IClientConfigAdminItem(IClientConfigItems * items, const char * name) :
    IClientConfigItem(items, name)
{
    set = false;
    force = false;
    items->Add(this);
}

IClientConfigAdminItem::~IClientConfigAdminItem()
{
    items->Remove(this);
}

void IClientConfigAdminItem::AdminToJson(class json_io & json, word base, char * & tmp)
{
    word baseObject = json.add_object(base, name);
    json.add_bool(baseObject, "set", set);
    json.add_bool(baseObject, "force", force);
    ToJson(json, baseObject, tmp);
}

void IClientConfigAdminItem::SetAdminSettings(class json_io & json, word baseObject, bool pbx)
{
    bool present, newset;

    if (pbx) {
        force = json.get_bool(baseObject, "force", &present);
        if (force) {
            set = false;
        }
        if (force || !set) {
            FromJson(json, baseObject);
            Apply();
        }
    }
    else {
        if (!force) {
            newset = json.get_bool(baseObject, "set", &present);
            set = present ? newset : true;
            FromJson(json, baseObject);
            Apply();
        }
    }
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminBool::IClientConfigAdminBool(IClientConfigItems * items, UClientConfigAdminBool * user, const char * name, bool init) :
    IClientConfigAdminItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

void IClientConfigAdminBool::Set(bool newValue)
{
    if (!force) {
        value = newValue;
        set = true;
        items->ItemSet(this);
        user->Apply(this);
    }
}

void IClientConfigAdminBool::ToJson(class json_io & json, word baseObject, char * & tmp)
{
    json.add_bool(baseObject, "value", value);
}

void IClientConfigAdminBool::FromJson(class json_io & json, word baseObject)
{
    value = json.get_bool(baseObject, "value");
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminEnum::IClientConfigAdminEnum(IClientConfigItems * items, UClientConfigAdminEnum * user, const char * name, const ClientConfigIdNameItem * map, int init) :
    IClientConfigAdminItem(items, name),
    user(user),
    map(map),
    value(init)
{
    value = ClientConfigNameId(map, NULL);
    items->ItemAdded(this);
}

IClientConfigAdminEnum::IClientConfigAdminEnum(IClientConfigItems * items, UClientConfigAdminEnum * user, const char * name, const ClientConfigIdNameItem * map) :
    IClientConfigAdminEnum(items, user, name, map, ClientConfigNameId(map, NULL))
{
}

void IClientConfigAdminEnum::Set(int newValue)
{
    if (!force) {
        value = newValue;
        set = true;
        items->ItemSet(this);
        user->Apply(this);
    }
}

void IClientConfigAdminEnum::ToJson(class json_io & json, word baseObject, char * & tmp)
{
    json.add_string(baseObject, "value", ClientConfigIdName(map, value));
}

void IClientConfigAdminEnum::FromJson(class json_io & json, word baseObject)
{
    value = ClientConfigNameId(map, json.get_string(baseObject, "value"));
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminInt::IClientConfigAdminInt(IClientConfigItems * items, UClientConfigAdminInt * user, const char * name, int init) :
    IClientConfigAdminItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

void IClientConfigAdminInt::Set(int newValue)
{
    if (!force) {
        value = newValue;
        set = true;
        items->ItemSet(this);
        user->Apply(this);
    }
}

void IClientConfigAdminInt::ToJson(class json_io & json, word baseObject, char * & tmp)
{
    json.add_int(baseObject, "value", value, tmp);
}

void IClientConfigAdminInt::FromJson(class json_io & json, word baseObject)
{
    value = json.get_int(baseObject, "value");
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminUnsigned::IClientConfigAdminUnsigned(IClientConfigItems * items, UClientConfigAdminUnsigned * user, const char * name, unsigned init) :
    IClientConfigAdminItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

void IClientConfigAdminUnsigned::Set(unsigned newValue)
{
    if (!force) {
        value = newValue;
        set = true;
        items->ItemSet(this);
        user->Apply(this);
    }
}

void IClientConfigAdminUnsigned::ToJson(class json_io & json, word baseObject, char * & tmp)
{
    json.add_unsigned(baseObject, "value", value, tmp);
}

void IClientConfigAdminUnsigned::FromJson(class json_io & json, word baseObject)
{
    value = json.get_unsigned(baseObject, "value");
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminUlong64::IClientConfigAdminUlong64(IClientConfigItems * items, UClientConfigAdminUlong64 * user, const char * name, ulong64 init) :
    IClientConfigAdminItem(items, name),
    user(user),
    value(init)
{
    items->ItemAdded(this);
}

void IClientConfigAdminUlong64::Set(ulong64 newValue)
{
    if (!force) {
        value = newValue;
        set = true;
        items->ItemSet(this);
        user->Apply(this);
    }
}

void IClientConfigAdminUlong64::ToJson(class json_io & json, word baseObject, char * & tmp)
{
    int l = _sprintf(tmp, "%016llx", (unsigned long long)value);
    json.add_string(baseObject, "value", tmp);
    tmp += l + 1;
}

void IClientConfigAdminUlong64::FromJson(class json_io & json, word baseObject)
{
    const char * s;
    ulong64 u;
    unsigned i;

    u = 0;
    s = json.get_string(baseObject, "value");
    if (s) {
        i = ((s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) ? 2 : 0;
        while (isxdigit(s[i])) {
            u = (u << 4) | (isdigit(s[i]) ? s[i] - '0' : tolower(s[i]) - 'a' + 10);
            i++;
        }
    }
    value = u;
}

/*---------------------------------------------------------------------------*/

IClientConfigAdminString::IClientConfigAdminString(IClientConfigItems * items, UClientConfigAdminString * user, const char * name, const char * init) :
    IClientConfigAdminItem(items, name),
    user(user),
    value(_strdup(init))
{
    items->ItemAdded(this);
}

IClientConfigAdminString::~IClientConfigAdminString()
{
    if (value) free(value);
}

void IClientConfigAdminString::Set(const char * newValue)
{
    if (!force) {
        if (value) free(value);
        value = _strdup(newValue);
        set = true;
        items->ItemSet(this);
        user->Apply(this);
    }
}

void IClientConfigAdminString::ToJson(class json_io & json, word baseObject, char * & tmp)
{
    json.add_string(baseObject, "value", value);
}

void IClientConfigAdminString::FromJson(class json_io & json, word baseObject)
{
    if (value) free(value);
    value = _strdup(json.get_string(baseObject, "value"));
}

/*---------------------------------------------------------------------------*/

IClientConfigItems::IClientConfigItems(UClientConfigItems * user)
{
    this->user = user;
    basicItems = NULL;
    adminItems = NULL;
}

IClientConfigItems::~IClientConfigItems()
{
    while (basicItems) {
        class IClientConfigBasicItem * item = (IClientConfigBasicItem *)basicItems;
        basicItems = basicItems->btree_get(item);
        delete item;
    }
    while (adminItems) {
        class IClientConfigAdminItem * item = (IClientConfigAdminItem *)adminItems;
        adminItems = adminItems->btree_get(item);
        delete item;
    }
}

void IClientConfigItems::Add(IClientConfigBasicItem * item)
{
    basicItems = basicItems->btree_put(item);
}

void IClientConfigItems::Remove(IClientConfigBasicItem * item)
{
    basicItems = basicItems->btree_get(item);
}

void IClientConfigItems::Add(IClientConfigAdminItem * item)
{
    adminItems = adminItems->btree_put(item);
}

void IClientConfigItems::Remove(IClientConfigAdminItem * item)
{
    adminItems = adminItems->btree_get(item);
}

void IClientConfigItems::Init()
{
    class btree * b;
    for (b = basicItems->btree_find_left(); b; b = basicItems->btree_find_next_left(((IClientConfigItem *)b)->name)) {
        IClientConfigBasicItem *item = (IClientConfigBasicItem *)b;
        item->Init();
    }
    for (b = adminItems->btree_find_left(); b; b = adminItems->btree_find_next_left(((IClientConfigItem *)b)->name)) {
        IClientConfigAdminItem *item = (IClientConfigAdminItem *)b;
        item->Init();
    }
}

void IClientConfigItems::ToJson(class json_io & json, word base, char * & tmp)
{
    class btree * b;
    for (b = basicItems->btree_find_left(); b; b = basicItems->btree_find_next_left(((IClientConfigItem *)b)->name)) {
        IClientConfigBasicItem *item = (IClientConfigBasicItem *)b;
        item->ToJson(json, base, tmp);
    }
    for (b = adminItems->btree_find_left(); b; b = adminItems->btree_find_next_left(((IClientConfigItem *)b)->name)) {
        IClientConfigAdminItem *item = (IClientConfigAdminItem *)b;
        item->AdminToJson(json, base, tmp);
    }
}

void IClientConfigItems::SetAdminSettings(class json_io & json, word base, bool pbx)
{
    class btree * b;
    word baseItem;
    byte type, flags;
    const char *name, *info;

    baseItem = 0;
    baseItem = json.get_next(base, baseItem, type, flags, name, info);
    while (baseItem != JSON_ID_NONE) {
        b = basicItems->btree_find(name);
        if (b) {
            IClientConfigBasicItem *item = (IClientConfigBasicItem *)b;
            item->FromJson(json, base);
            item->Apply();
        }
        b = adminItems->btree_find(name);
        if (b) {
            IClientConfigAdminItem *item = (IClientConfigAdminItem *)b;
            if (type == JSON_TYPE_OBJECT) {
                item->SetAdminSettings(json, baseItem, pbx);
            }
        }
        baseItem = json.get_next(base, baseItem, type, flags, name, info);
    }
}

void IClientConfigItems::SetItemAdminSettings(IClientConfigItem * item, class json_io & json, word base, bool pbx)
{
    class btree * b;

    b = basicItems->btree_find(item->name);
    if (b) {
        item->FromJson(json, base);
        item->Apply();
    }
    b = adminItems->btree_find(item->name);
    if (b) {
        word baseItem = json.get_object(base, item->name);
        if (baseItem != JSON_ID_NONE) {
            ((IClientConfigAdminItem *)item)->SetAdminSettings(json, baseItem, pbx);
        }
    }
}

/*---------------------------------------------------------------------------*/
