//
// Dictionary.cpp
//

#include <fcntl.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <io.h>
#include <share.h>
#else
#include <unistd.h>
#endif
#include <libxml/xmlreader.h>

#define DBG_LEVEL 0
#include <Raym/Log.h>
#include <Raym/Dictionary.h>

/*
#define RELEASE(P1)         \
    if ((P1) != NULL)       \
    {                       \
        (P1)->release();    \
        (P1) = NULL;        \
    }
*/
namespace Raym
{

Dictionary::Dictionary()
{
    DebugLog2("Dictionary::Dictionary()");

    _dict.clear();
}

Dictionary::~Dictionary()
{
    for (unsigned int i = 0; i < _dict.size(); ++i)
    {
        Object *obj = _dict.at(i);
        obj->release();
    }
    _dict.clear();

    DebugLog2("Dictionary::~Dictionary()");
}

Dictionary *Dictionary::dictionaryWithCapacity(UInteger numItems)
{
    DebugLog2("Dictionary::dictionaryWithCapacity(numItems)");

    Dictionary *result = Dictionary::alloc()->initWithCapacity(numItems);
    if (result != NULL)
    {
        result->autorelease();
    }
    return result;
}

Dictionary *Dictionary::dictionaryWithDictionary(Dictionary *dictionary)
{
    DebugLog2("Dictionary::dictionaryWithDictionary(dictionary)");

    Dictionary *result = Dictionary::alloc()->initWithDictionary(dictionary);
    if (result != NULL)
    {
        result->autorelease();
    }
    return result;
}

Dictionary *Dictionary::alloc()
{
    DebugLog2("Dictionary::alloc()");

    return new Dictionary();
}

Dictionary *Dictionary::initWithCapacity(UInteger numItems)
{
    DebugLog2("Dictionary::initWithCapacity(numItems)");

    return this;
}

Dictionary *Dictionary::initWithDictionary(Dictionary *dictionary)
{
    DebugLog2("Dictionary::initWithDictionary(dictionary)");

    if (dictionary != NULL)
    {
        for (unsigned int i = 0; i < _dict.size(); ++i)
        {
            _dict.at(i)->release();
        }
        _dict.clear();
        _dict = dictionary->_dict;
        for (unsigned int i = 0; i < _dict.size(); ++i)
        {
            _dict.at(i)->retain();
        }
        return this;
    }

    release();
    return NULL;
}

static String *parseKey(xmlTextReaderPtr reader)
{
    DebugLog2("parseKey(reader)");

    String *result = NULL;
    if ((strcmp("key", (const char *)xmlTextReaderConstName(reader)) == 0) && (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT))
    {
        if (xmlTextReaderRead(reader) == 1)
        {
            if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT)
            {
                const char *value = (const char *)xmlTextReaderConstValue(reader);
                if (value != NULL)
                {
                    DebugLog3("key: %s\n", value);
                    result = String::alloc()->initWithCString(value, UTF8StringEncoding);
                    if (xmlTextReaderRead(reader) == 1)
                    {
                        if ((strcmp("key", (const char *)xmlTextReaderConstName(reader)) == 0) &&
                            (xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT))
                        {
                            return result;
                        }
                    }
                    RELEASE(result);
                }
            }
        }
    }
    return NULL;
}

static Object *parseString(xmlTextReaderPtr reader)
{
    DebugLog2("parseString(reader)");

    String *result = NULL;
    if ((strcmp("string", (const char *)xmlTextReaderConstName(reader)) == 0) && (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT))
    {
        if (xmlTextReaderRead(reader) == 1)
        {
            int type = xmlTextReaderNodeType(reader);
            if ((type == XML_READER_TYPE_TEXT) || (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE))
            {
                const char *value = (const char *)xmlTextReaderConstValue(reader);
                if (value != NULL)
                {
                    result = String::alloc()->initWithCString(value, UTF8StringEncoding);
                    if (xmlTextReaderRead(reader) == 1)
                    {
                        if ((strcmp("string", (const char *)xmlTextReaderConstName(reader)) == 0) &&
                            (xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT))
                        {
                            return result;
                        }
                    }
                    RELEASE(result);
                }
            }
        }
    }
    return NULL;
}

static Object *parseInteger(xmlTextReaderPtr reader)
{
    DebugLog2("parseInteger(reader)\n");

    Number *result = NULL;
    if ((strcmp("integer", (const char *)xmlTextReaderConstName(reader)) == 0) && (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT))
    {
        if (xmlTextReaderRead(reader) == 1)
        {
            if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT)
            {
                const char *value = (const char *)xmlTextReaderConstValue(reader);
                if (value != NULL)
                {
                    int val = atoi(value);
                    char tmp[32];
#ifdef _WIN32
                    sprintf_s(tmp, 32, "%d", val);
#else
                    sprintf(tmp, "%d", val);
#endif
                    if (strcmp(tmp, value) != 0)
                    {
                        DebugLog0("convert error\n");
                        abort();
                    }
                    result = Number::alloc()->initWithInt(val);
                    if (xmlTextReaderRead(reader) == 1)
                    {
                        if ((strcmp("integer", (const char *)xmlTextReaderConstName(reader)) == 0) &&
                            (xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT))
                        {
                            return result;
                        }
                    }
                    RELEASE(result);
                }
            }
        }
    }
    return NULL;
}

static Dictionary *parseDict(xmlTextReaderPtr reader);

static Array *parseArray(xmlTextReaderPtr reader)
{
    DebugLog2("parseArray(reader)");

    Array *result = NULL;
    if ((strcmp("array", (const char *)xmlTextReaderConstName(reader)) == 0) && (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT))
    {
        result = Array::alloc()->initWithCapacity(0);
        Object *value = NULL;
        int baseDepth = xmlTextReaderDepth(reader);
        while (xmlTextReaderRead(reader) == 1)
        {
            const char *name = (const char *)xmlTextReaderConstName(reader);
            int type = xmlTextReaderNodeType(reader);
            int depth = xmlTextReaderDepth(reader);
            if (type == XML_READER_TYPE_ELEMENT)
            {
                if (baseDepth + 1 == depth)
                {
                    if (strcmp("integer", name) == 0)
                    {
                        value = parseInteger(reader);
                    }
                    else if (strcmp("string", name) == 0)
                    {
                        value = parseString(reader);
                    }
                    else if (strcmp("array", name) == 0)
                    {
                        value = parseArray(reader);
                    }
                    else if (strcmp("dict", name) == 0)
                    {
                        value = parseDict(reader);
                    }
                    else if (strcmp("true", name) == 0)
                    {
                        value = Number::alloc()->initWithBool(true);
                    }
                    else if (strcmp("false", name) == 0)
                    {
                        value = Number::alloc()->initWithBool(false);
                    }
                    else
                    {
                        // unknown tag
                        DebugLog0("format error: unknown tag: %s\n", name);
                        RELEASE(result);
                        break;
                    }
                    if (value == NULL)
                    {
                        DebugLog0("format error: parse NG.(%s)\n", name);
                        RELEASE(result);
                        break;
                    }
                    result->addObject(value);
                    RELEASE(value);
                }
            }
            else if (type == XML_READER_TYPE_END_ELEMENT)
            {
                if ((baseDepth == depth) && (strcmp("array", name) == 0))
                {
                    break;
                }
                DebugLog0("format error: desuka?\n");
                RELEASE(result);
                break;
            }
        }
        RELEASE(value);
    }
    return result;
}

static Dictionary *parseDict(xmlTextReaderPtr reader)
{
    DebugLog2("parseDict(reader)");

    Dictionary *result = NULL;
    if ((strcmp("dict", (const char *)xmlTextReaderConstName(reader)) == 0) && (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT))
    {
        result = Dictionary::alloc()->initWithCapacity(0);
        String *key = NULL;
        Object *value = NULL;
        int baseDepth = xmlTextReaderDepth(reader);
        while (xmlTextReaderRead(reader) == 1)
        {
            const char *name = (const char *)xmlTextReaderConstName(reader);
            int type = xmlTextReaderNodeType(reader);
            int depth = xmlTextReaderDepth(reader);
            if (type == XML_READER_TYPE_ELEMENT)
            {
                if (baseDepth + 1 == depth)
                {
                    if (strcmp("key", name) == 0)
                    {
                        RELEASE(key);
                        key = parseKey(reader);
                    }
                    else if (strcmp("integer", name) == 0)
                    {
                        RELEASE(value);
                        value = parseInteger(reader);
                    }
                    else if (strcmp("string", name) == 0)
                    {
                        RELEASE(value);
                        value = parseString(reader);
                    }
                    else if (strcmp("array", name) == 0)
                    {
                        RELEASE(value);
                        value = parseArray(reader);
                    }
                    else if (strcmp("dict", name) == 0)
                    {
                        RELEASE(value);
                        value = parseDict(reader);
                    }
                    else if (strcmp("true", name) == 0)
                    {
                        RELEASE(value);
                        value = Number::alloc()->initWithBool(true);
                    }
                    else if (strcmp("false", name) == 0)
                    {
                        RELEASE(value);
                        value = Number::alloc()->initWithBool(false);
                    }
                    else
                    {
                        // unknown tag
                        DebugLog0("format error: unknown tag: %s\n", name);
                        RELEASE(result);
                        break;
                    }
                    if ((key != NULL) && (value != NULL))
                    {
                        result->setObject(value, key);
                        RELEASE(key);
                        RELEASE(value);
                    }
                }
            }
            else if (type == XML_READER_TYPE_END_ELEMENT)
            {
                if ((baseDepth == depth) && (strcmp("dict", name) == 0))
                {
                    break;
                }
                DebugLog0("format error: %s(): %s\n", __FUNCTION__, name);
                RELEASE(result);
                break;
            }
        }
        RELEASE(key);
        RELEASE(value);
    }
    return result;
}

static Dictionary *parsePlist(xmlTextReaderPtr reader)
{
    DebugLog2("parsePlist(reader)");

    Dictionary *result = NULL;
    if ((strcmp("plist", (const char *)xmlTextReaderConstName(reader)) == 0) && (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT))
    {
        int baseDepth = xmlTextReaderDepth(reader);
        DebugLog3("baseDepth = %d\n", baseDepth);
        while (xmlTextReaderRead(reader) == 1)
        {
            const char *name = (const char *)xmlTextReaderConstName(reader);
            int type = xmlTextReaderNodeType(reader);
            int depth = xmlTextReaderDepth(reader);
            if (type == XML_READER_TYPE_ELEMENT)
            {
                if (baseDepth + 1 == depth)
                {
                    if (strcmp("dict", name) == 0)
                    {
                        result = parseDict(reader);
                    }
                }
                else
                {
                    DebugLog0("format error: %s(): name:%s\n", __FUNCTION__, name);
                    RELEASE(result);
                    break;
                }
            }
            else if (type == XML_READER_TYPE_END_ELEMENT)
            {
                if ((baseDepth == depth) && (strcmp("plist", name) == 0))
                {
                    break;
                }
                DebugLog0("format error: %s(): name:%s, depth:%d\n", __FUNCTION__, name, depth);
                RELEASE(result);
                break;
            }
        }
    }
    return result;
}

Dictionary *Dictionary::initWithContentsOfFile(const char *path)
{
    DebugLog2("Dictionary::initWithContentsOfFile(path)");

    return initWithContentsOfFile(String::alloc()->initWithUTF8String(path)->autorelease());
}

Dictionary *Dictionary::initWithContentsOfFile(String *path)
{
    DebugLog2("Dictionary::initWithContentsOfFile(path)");

    bool success = false;
    xmlTextReaderPtr reader = xmlReaderForFile(path->cString(), NULL, 0);
    if (reader != NULL)
    {
        if (xmlTextReaderRead(reader) == 1)
        {
            const char *enc = (const char *)xmlTextReaderConstEncoding(reader);
            DebugLog2("encoding: %s\n", enc);
            if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_DOCUMENT_TYPE)
            {
                while (xmlTextReaderRead(reader) == 1)
                {
                    const char *name = (const char *)xmlTextReaderConstName(reader);
                    int type = xmlTextReaderNodeType(reader);
                    if (type == XML_READER_TYPE_ELEMENT)
                    {
                        if (strcmp("plist", name) == 0)
                        {
                            Dictionary *d = parsePlist(reader);
                            if (d != NULL)
                            {
                                // copy
                                if (initWithDictionary(d) != NULL)
                                {
                                    success = true;
                                }
                                d->release();
                            }
                        }
                        else
                        {
                            DebugLog0("Dictionary::initWithContentsOfFile(\"%s\") has error.", path->cString());
                        }
                    }
                    else if (type == XML_READER_TYPE_END_ELEMENT)
                    {
                        break;
                    }
                }
            }
        }
        xmlTextReaderClose(reader);
    }

    if (success)
    {
        return this;
    }

    release();
    return NULL;
}

Dictionary *Dictionary::retain()
{
    DebugLog2("Dictionary::retain()");

    Object::retain();
    return this;
}

Dictionary *Dictionary::autorelease()
{
    DebugLog2("Dictionary::autorelease()");

    Object::autorelease();
    return this;
}

void Dictionary::setObject(Object *object, String *forKey)
{
    DebugLog2("Dictionary::setObject(object,key)");

    if ((object != NULL) && (forKey != NULL))
    {
        RaymLock(this);
        removeObjectForKey(forKey);
        KeyAndValue *kv = KeyAndValue::keyAndValue(forKey, object);
        if (kv != NULL)
        {
            _dict.push_back(kv);
        }
        RaymUnlock(this);
    }
}

Object *Dictionary::objectForKey(String *key)
{
    DebugLog2("Dictionary::objectForKey(key)");

    Object *result = NULL;
    if (key != NULL)
    {
        RaymLock(this);
        std::vector<Object *>::iterator it;
        for (it = _dict.begin(); it != _dict.end(); ++it)
        {
            KeyAndValue *kv = (KeyAndValue *)(*it);
            if (key->isEqualToString(kv->key()))
            {
                result = kv->value();
                break;
            }
        }
        RaymUnlock(this);
    }
    return result;
}

void Dictionary::removeObjectForKey(String *key)
{
    DebugLog2("Dictionary::removeObjectForKey(key)");

    if (key != NULL)
    {
        RaymLock(this);
        std::vector<Object *>::iterator it;
        for (it = _dict.begin(); it != _dict.end(); ++it)
        {
            KeyAndValue *kv = (KeyAndValue *)(*it);
            if (key->isEqualToString(kv->key()))
            {
                _dict.erase(it);
                kv->release();
                break;
            }
        }
        RaymUnlock(this);
    }
}

void Dictionary::removeObjectForKey(const char *key)
{
    if (key != NULL)
    {
        String *k = String::alloc()->initWithUTF8String(key);
        if (k != NULL)
        {
            removeObjectForKey(k);
            k->release();
        }
    }
}

void Dictionary::setObject(Object *object, const char *forKey)
{
    DebugLog2("Dictionary::setObject(object,key)");

    if ((object != NULL) && (forKey != NULL))
    {
        String *k = String::alloc()->initWithCString(forKey, UTF8StringEncoding);
        setObject(object, k);
        k->release();
    }
}

Object *Dictionary::objectForKey(const char *key)
{
    DebugLog2("Dictionary::objectForKey(key)");

    String *k = String::alloc()->initWithCString(key, UTF8StringEncoding);
    Object *ret = objectForKey(k);
    k->release();
    return ret;
}

void Dictionary::setString(String *string, const char *forKey)
{
    DebugLog2("Dictionary::setString(string,key)");

    setObject(string, forKey);
}

void Dictionary::setString(const char *string, const char *forKey)
{
    DebugLog2("Dictionary::setString(string,key)");

    String *v = String::alloc()->initWithCString(string, UTF8StringEncoding);
    setObject(v, forKey);
    v->release();
}

String *Dictionary::stringForKey(String *key)
{
    DebugLog2("Dictionary::stringForKey(key)");

    String *result = NULL;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(String, obj))
    {
        result = (String *)obj;
    }
    return result;
}

String *Dictionary::stringForKey(const char *key)
{
    DebugLog2("Dictionary::stringForKey(key)");

    String *result = NULL;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(String, obj))
    {
        result = (String *)obj;
    }
    return result;
}

void Dictionary::setInteger(int value, String *key)
{
    DebugLog2("Dictionary::setInteger(value,key)");

    Number *v = Number::alloc()->initWithInt(value);
    setObject(v, key);
    v->release();
}

void Dictionary::setInteger(int value, const char *key)
{
    DebugLog2("Dictionary::setInteger(value,key)");

    Number *v = Number::alloc()->initWithInt(value);
    setObject(v, key);
    v->release();
}

int Dictionary::integerForKey(String *key)
{
    DebugLog2("Dictionary::integerForKey(key)");

    int ret = 0;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Number, obj))
    {
        ret = ((Number *)obj)->intValue();
    }
    return ret;
}

int Dictionary::integerForKey(const char *key)
{
    DebugLog2("Dictionary::integerForKey(key)");

    int ret = 0;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Number, obj))
    {
        ret = ((Number *)obj)->intValue();
    }
    return ret;
}

Dictionary *Dictionary::dictionaryForKey(String *key)
{
    DebugLog2("Dictionary::dictionaryForKey(key)");

    Dictionary *result = NULL;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Dictionary, obj))
    {
        result = (Dictionary *)obj;
    }
    return result;
}

Dictionary *Dictionary::dictionaryForKey(const char *key)
{
    DebugLog2("Dictionary::dictionaryForKey(key)");

    Dictionary *result = NULL;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Dictionary, obj))
    {
        result = (Dictionary *)obj;
    }
    return result;
}

Array *Dictionary::arrayForKey(String *key)
{
    DebugLog2("Dictionary::arrayForKey(key)");

    Array *ret = NULL;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Array, obj))
    {
        ret = (Array *)obj;
    }
    return ret;
}

Array *Dictionary::arrayForKey(const char *key)
{
    DebugLog2("Dictionary::arrayForKey(key)");

    Array *ret = NULL;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Array, obj))
    {
        ret = (Array *)obj;
    }
    return ret;
}

void Dictionary::setBool(bool value, String *key)
{
    DebugLog2("Dictionary::setBool(value,key)");

    Number *obj = Number::alloc()->initWithBool(value);
    setObject(obj, key);
    obj->release();
}

void Dictionary::setBool(bool value, const char *key)
{
    DebugLog2("Dictionary::setBool(value,key)");

    Number *obj = Number::alloc()->initWithBool(value);
    setObject(obj, key);
    obj->release();
}

bool Dictionary::boolForKey(String *key)
{
    DebugLog2("Dictionary::boolForKey(key)");

    bool ret = false;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Number, obj))
    {
        ret = ((Number *)obj)->boolValue();
    }
    return ret;    
}

bool Dictionary::boolForKey(const char *key)
{
    DebugLog2("Dictionary::boolForKey(key)");

    bool ret = false;
    Object *obj = objectForKey(key);
    if ((obj != NULL) && isKindOfClass(Number, obj))
    {
        ret = ((Number *)obj)->boolValue();
    }
    return ret;    
}

Array *Dictionary::allKeys()
{
    DebugLog2("Dictionary::allKeys()");

    Array *result = Array::alloc()->initWithCapacity(0)->autorelease();
    RaymLock(this);

    std::vector<Object *>::iterator it;
    for (it = _dict.begin(); it != _dict.end(); ++it)
    {
        KeyAndValue *kv = (KeyAndValue *)(*it);
        result->addObject(kv->key());
    }

    RaymUnlock(this);

    return result;
}

UInteger Dictionary::count()
{
    DebugLog2("Dictionary::count()");

    UInteger result = 0;
    RaymLock(this);
    result = _dict.size();
    RaymUnlock(this);
    return result;
}

#ifdef _WIN32
#define INDENT(LV) for (int i = 0; i < (LV); ++i) { _write(fd, "\t", 1); }
#define WRITE(STR) _write(fd, STR, strlen(STR))
#else
#define INDENT(LV) for (int i = 0; i < (LV); ++i) { write(fd, "\t", 1); }
#define WRITE(STR) write(fd, STR, strlen(STR))
#endif
#define INDENT_S(LV) for (int i =0; i < (LV); ++i) { result += "\t"; }

void Dictionary::writeValue(int fd, int level, Object *value)
{
    DebugLog2("Dictionary::writeValue(fd,level,value)");

    const char *str = NULL;
    if (isKindOfClass(Number, value))
    {
        Number *number = (Number *)value;
        str = number->description()->cString();
        if ((strcmp("true", str) == 0) || (strcmp("false", str) == 0))
        {
            INDENT(level);
            WRITE("<");
            WRITE(str);
            WRITE("/>\n");
        }
        else
        {
            INDENT(level);
            WRITE("<integer>");
            WRITE(str);
            WRITE("</integer>\n");
        }
    }
    else if (isKindOfClass(String, value))
    {
        INDENT(level);
        str = "<string>";
        WRITE(str);
        str = ((String *)value)->cString();
        WRITE(str);
        str = "</string>\n";
        WRITE(str);
    }
    else if (isKindOfClass(Array, value))
    {
        INDENT(level);
        str = "<array>\n";
        WRITE(str);

        Array *array = (Array *)value;
        for (uint i = 0; i < array->count(); ++i)
        {
            writeValue(fd, level + 1, array->objectAtIndex(i));
        }

        INDENT(level);
        str = "</array>\n";
        WRITE(str);
    }
    else if (isKindOfClass(Dictionary, value))
    {
        ((Dictionary *)value)->writeTo(fd, level);
    }
    else
    {
        DebugLog0("format error: %s()\n", __FUNCTION__);
    }
}

void Dictionary::writeTo(int fd, int level)
{
    DebugLog2("Dictionary::writeTo(fd,level)");

    INDENT(level);
    const char *str = "<dict>\n";
    WRITE(str);

    std::vector<Object *>::iterator it;
    for (it = _dict.begin(); it != _dict.end(); ++it)
    {
        KeyAndValue *kv = (KeyAndValue *)(*it);
        String *key = kv->key();
        INDENT(level + 1);
        str = "<key>";
        WRITE(str);
        str = key->cString();
        WRITE(str);
        str = "</key>\n";
        WRITE(str);

        writeValue(fd, level + 1, kv->value());
    }

    INDENT(level);
    str = "</dict>\n";
    WRITE(str);
}

bool Dictionary::writeToFile(const char *path, bool atomically)
{
    DebugLog2("Dictionary::writeToFile(path,atomically)");

    return writeToFile(String::alloc()->initWithUTF8String(path)->autorelease(), atomically);
}

bool Dictionary::writeToFile(String *path, bool atomically)
{
    DebugLog2("Dictionary::writeToFile(path,atomically)");

    RaymLock(this);

    const char *outfile = path->cString();
    int fd = -1;
#ifdef _WIN32
    errno_t err = _sopen_s(&fd, outfile, (_O_CREAT | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE));
    if (err != 0)
#else
    if ((fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) < 0)
#endif
    {
        DebugLog2("file open error: %s\n", outfile);
        return false;
    }

    const char *xmlDeclaration = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    WRITE(xmlDeclaration);

    const char *documentType = "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
    WRITE(documentType);

    const char *startElement = "<plist version=\"1.0\">\n";
    WRITE(startElement);

    writeTo(fd, 0);

    const char *endElement = "</plist>\n";
    WRITE(endElement);

#ifdef _WIN32
    _close(fd);
#else
    close(fd);
#endif

    RaymUnlock(this);

    return true;
}

std::string Dictionary::toStringValue(int level, Object *value)
{
    DebugLog2("Dictionary::toStringValue(level,value)");

    std::string result = "";

    if (isKindOfClass(Number, value))
    {
        Number *number = (Number *)value;
        const char *str = number->description()->cString();
        if ((strcmp("true", str) == 0) || (strcmp("false", str) == 0))
        {
            INDENT_S(level);
            result += "<";
            result += str;
            result += "/>\n";
        }
        else
        {
            INDENT_S(level);
            result += "<integer>";
            result += str;
            result += "</integer>\n";
        }
    }
    else if (isKindOfClass(String, value))
    {
        INDENT_S(level);
        result += "<string>";
        result += ((String *)value)->cString();
        result += "</string>\n";
    }
    else if (isKindOfClass(Array, value))
    {
        INDENT_S(level);
        result += "<array>\n";

        Array *array = (Array *)value;
        for (uint i = 0; i < array->count(); ++i)
        {
            result += toStringValue(level + 1, array->objectAtIndex(i));
        }

        INDENT_S(level);
        result += "</array>\n";
    }
    else if (isKindOfClass(Dictionary, value))
    {
        result += ((Dictionary *)value)->toString(level);
    }
    else
    {
        DebugLog0("format error: %s()\n", __FUNCTION__);
    }

    return result;
}

std::string Dictionary::toString(int level)
{
    DebugLog2("Dictionary::toString(level)");

    std::string result = "";

    INDENT_S(level);
    result += "<dict>\n";

    std::vector<Object *>::iterator it;
    for (it = _dict.begin(); it != _dict.end(); ++it)
    {
        KeyAndValue *kv = (KeyAndValue *)(*it);
        String *key = kv->key();
        INDENT_S(level + 1);
        result += "<key>";
        result += key->cString();
        result += "</key>\n";

        result += toStringValue(level + 1, kv->value());
    }

    INDENT_S(level);
    result += "</dict>\n";

    return result;
}

std::string Dictionary::toString()
{
    DebugLog2("Dictionary::toString()");

    RaymLock(this);

    std::string result = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    result += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
    result += "<plist version=\"1.0\">\n";
    result += toString(0);
    result += "</plist>\n";

    RaymUnlock(this);

    return result;
}

const char *Dictionary::className()
{
    return "Dictionary";
}

/********************************************************/
Dictionary::KeyAndValue::KeyAndValue()
{
    DebugLog2("Dictionary::KeyAndValue::keyAndValue()");

    _key = NULL;
    _value = NULL;
}

Dictionary::KeyAndValue::~KeyAndValue()
{
    if (_key != NULL)
    {
        _key->release();
    }
    if (_value != NULL)
    {
        _value->release();
    }

    DebugLog2("Dictionary::KeyAndValue::~KeyAndValue()");
}

Dictionary::KeyAndValue *Dictionary::KeyAndValue::keyAndValue(String *key, Object *value)
{
    DebugLog2("Dictionary::KeyAndValue::keyAndValue(key,value)");

    KeyAndValue *result = NULL;
    if ((key != NULL) && (value != NULL))
    {
        result = new KeyAndValue();
        key->retain();
        result->_key = key;
        value->retain();
        result->_value = value;
    }
    return result;
}

String *Dictionary::KeyAndValue::key()
{
    DebugLog2("Dictionary::KeyAndValue::key()");

    return _key;
}

Object *Dictionary::KeyAndValue::value()
{
    DebugLog2("Dictionary::KeyAndValue::value()");

    return _value;
}

const char *Dictionary::KeyAndValue::className()
{
    return "Dictionary::KeyAndValue";
}

// for initialization
class DictionaryInternalUse
{
public:
    DictionaryInternalUse();
};
DictionaryInternalUse::DictionaryInternalUse()
{
    // o ma ji na i
    xmlInitParser();
}
static DictionaryInternalUse dictionaryInternalUse__;

} // Raym
