//
// Data.cpp
//

#ifdef _WIN32
#include <io.h>
#include <share.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#define DBG_LEVEL 0
#include <Raym/Log.h>
#include <Raym/Data.h>
#include <Raym/Error.h>
#include <Raym/URLRequest.h>
#include <Raym/URLResponse.h>
#include <Raym/URLConnection.h>

namespace Raym
{

Data::Data()
{
    DebugLog2("Data::Data()");

    _length = 0;
    _bytes = NULL;
}

Data::~Data()
{
    if (_bytes != NULL)
    {
        free(_bytes);
    }

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

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

    return new Data();
}

Data *Data::initWithCapacity(UInteger aNumItems)
{
    DebugLog2("Data::initWithCapacity(aNumItems)");

    return this;
}

Data *Data::initWithBytesAndLength(const void *bytes, UInteger length)
{
    DebugLog2("Data::initWithBytesAndLength(bytes,length)");

    if (bytes != NULL)
    {
        void *ptr = malloc(length);
        if (ptr != NULL)
        {
            _length = length;
            _bytes = ptr;
            memcpy(_bytes, bytes, length);
        }
        else
        {
            release();
            return NULL;
        }
    }
    return this;
}

Data *Data::dataWithCapacity(UInteger aNumItems)
{
    DebugLog2("Data::dataWithCapacity(aNumItems)");

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

Data *Data::dataWithContentsOfFile(const char *path)
{
    DebugLog2("Data::dataWithContentsOfFile(path)");

    Data *result = Data::alloc()->initWithContentsOfFile(path);
    if (result != NULL)
    {
        result->autorelease();
    }
    return result;
}

Data *Data::dataWithContentsOfURL(URL *url)
{
    DebugLog2("Data::dataWithContentsOfURL(url)");

    Data *result = Data::alloc()->initWithContentsOfURL(url);
    if (result != NULL)
    {
        result->autorelease();
    }
    return result;
}

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

    if (path != NULL)
    {
#ifdef _WIN32
        struct __stat64 buffer;
        if (_stat64(path->cString(), &buffer) == 0)
        {
            unsigned char *ptr = (unsigned char *)malloc(buffer.st_size);
            if (ptr != NULL)
            {
                int fd = -1;
                if (_sopen_s(&fd, path->cString(), _O_RDONLY | _O_BINARY, _SH_DENYNO, 0) == 0)
                {
                    __int64 sum = 0;
                    while (sum < buffer.st_size)
                    {
                        int size = _read(fd, &ptr[sum], 256);
                        if (size < 0)
                        {
                            DebugLog0("Data::initWithContentsOfFile(): _read ng(%d).: %s", errno, path->cString());
                            free(ptr);
                            release();
                            _close(fd);
                            return NULL;
                        }
                        sum += size;
                        if (sum >= buffer.st_size)
                        {
                            break;
                        }
                    }
                    if (sum != buffer.st_size)
                    {
                        DebugLog0("Data::initWithContentsOfFile(): buffer.st_size ng(%d).: %s", errno, path->cString());
                        free(ptr);
                        release();
                        _close(fd);
                        return NULL;
                    }
                    _bytes = ptr;
                    _length = (UInteger)sum;
                    _close(fd);
                }
                else
                {
                    DebugLog0("Data::initWithContentsOfFile(): _sopen_s ng(%d).: %s", errno, path->cString());
                    free(ptr);
                    release();
                    return NULL;
                }
            }
            else
            {
                DebugLog0("Data::initWithContentsOfFile(): mallog ng.");
                release();
                return NULL;
            }
        }
        else
        {
            DebugLog0("Data::initWithContentsOfFile(): stat ng.");
            release();
            return NULL;
        }
#else
#endif
    }
    else
    {
        release();
        return NULL;
    }
    return this;
}

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

    return initWithContentsOfFile(String::stringWithUTF8String(path));
}

Data *Data::initWithContentsOfURL(URL *url)
{
    DebugLog2("Data::initWithContentsOfURL(url)");

    URLRequest *req = URLRequest::requestWithURL(url);
    DebugLog2("nsdata: req: 0x%016x\n", req);
    URLResponse *resp = NULL;
    Error *error = NULL;
    Data *data = URLConnection::sendSynchronousRequest(req, &resp, &error);
    if (data != NULL)
    {
        DebugLog2("nsdata: data 0x%016x\n", data);
        DebugLog2("nsdata: resp 0x%016x\n", resp);
        return initWithBytesAndLength(data->bytes(), data->length());
    }

    return NULL;
}

Data *Data::retain()
{
    DebugLog2("Data::retain()");
    Object::retain();
    return this;
}

Data *Data::autorelease()
{
    DebugLog2("Data::autorelease()");
    Object::autorelease();
    return this;
}

UInteger Data::length()
{
    DebugLog2("Data::length()");

    return _length;
}

const void *Data::bytes()
{
    DebugLog2("Data::bytes()");

    return _bytes;
}

void Data::appendBytes(const void *bytes, UInteger length)
{
    DebugLog2("Data::appendBytes(bytes,length)");

    if (bytes != NULL)
    {
        void *buf = realloc(_bytes, _length + length);
        if (buf != NULL)
        {
            _bytes = buf;
            memcpy(&((char *)_bytes)[_length], bytes, length);
            _length += length;
        }
    }
}

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


} // Raym
