//-----------------------------------------------------------------------------
// Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
// This program is free software: you can modify it and/or redistribute it
// under the terms of:
//
// (i)  the Universal Permissive License v 1.0 or at your option, any
//      later version (http://oss.oracle.com/licenses/upl); and/or
//
// (ii) the Apache License v 2.0. (http://www.apache.org/licenses/LICENSE-2.0)
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// dpiSodaDoc.c
//   Implementation of SODA documents.
//-----------------------------------------------------------------------------

#include "dpiImpl.h"

//-----------------------------------------------------------------------------
// dpiSodaDoc__allocate() [INTERNAL]
//   Allocate and initialize a SODA document structure.
//-----------------------------------------------------------------------------
int dpiSodaDoc__allocate(dpiSodaDb *db, void *handle, dpiSodaDoc **doc,
        dpiError *error)
{
    dpiSodaDoc *tempDoc;

    if (dpiGen__allocate(DPI_HTYPE_SODA_DOC, db->env, (void**) &tempDoc,
            error) < 0)
        return DPI_FAILURE;
    dpiGen__setRefCount(db, error, 1);
    tempDoc->db = db;
    tempDoc->handle = handle;
    *doc = tempDoc;
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiSodaDoc__check() [INTERNAL]
//   Determine if the SODA document is available to use.
//-----------------------------------------------------------------------------
static int dpiSodaDoc__check(dpiSodaDoc *doc, const char *fnName,
        dpiError *error)
{
    if (dpiGen__startPublicFn(doc, DPI_HTYPE_SODA_DOC, fnName, error) < 0)
        return DPI_FAILURE;
    if (!doc->db->conn->handle || doc->db->conn->closing)
        return dpiError__set(error, "check connection", DPI_ERR_NOT_CONNECTED);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiSodaDoc__free() [INTERNAL]
//   Free the memory for a SODA document. Note that the reference to the
// database must remain until after the handle is freed; otherwise, a segfault
// can take place.
//-----------------------------------------------------------------------------
void dpiSodaDoc__free(dpiSodaDoc *doc, dpiError *error)
{
    if (doc->handle) {
        dpiOci__handleFree(doc->handle, DPI_OCI_HTYPE_SODA_DOCUMENT);
        doc->handle = NULL;
    }
    if (doc->db) {
        dpiGen__setRefCount(doc->db, error, -1);
        doc->db = NULL;
    }
    dpiUtils__freeMemory(doc);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc__getAttributeText() [INTERNAL]
//   Get the value of the OCI attribute as a text string.
//-----------------------------------------------------------------------------
static int dpiSodaDoc__getAttributeText(dpiSodaDoc *doc, uint32_t attribute,
        const char **value, uint32_t *valueLength, const char *fnName)
{
    dpiError error;
    int status;

    // validate parameters
    if (dpiSodaDoc__check(doc, fnName, &error) < 0)
        return dpiGen__endPublicFn(doc, DPI_FAILURE, &error);
    DPI_CHECK_PTR_NOT_NULL(doc, value)
    DPI_CHECK_PTR_NOT_NULL(doc, valueLength)

    // get attribute value
    status = dpiOci__attrGet(doc->handle, DPI_OCI_HTYPE_SODA_DOCUMENT,
            (void*) value, valueLength, attribute, "get value", &error);
    return dpiGen__endPublicFn(doc, status, &error);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_addRef() [PUBLIC]
//   Add a reference to the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_addRef(dpiSodaDoc *doc)
{
    return dpiGen__addRef(doc, DPI_HTYPE_SODA_DOC, __func__);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_getContent() [PUBLIC]
//   Return the content of the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_getContent(dpiSodaDoc *doc, const char **value,
        uint32_t *valueLength, const char **encoding)
{
    uint16_t charsetId;
    dpiError error;

    // validate parameters
    if (dpiSodaDoc__check(doc, __func__, &error) < 0)
        return dpiGen__endPublicFn(doc, DPI_FAILURE, &error);
    DPI_CHECK_PTR_NOT_NULL(doc, value)
    DPI_CHECK_PTR_NOT_NULL(doc, valueLength)
    DPI_CHECK_PTR_NOT_NULL(doc, encoding)

    // get content
    if (dpiOci__attrGet(doc->handle, DPI_OCI_HTYPE_SODA_DOCUMENT,
            (void*) value, valueLength, DPI_OCI_ATTR_SODA_CONTENT,
            "get content", &error) < 0)
        return dpiGen__endPublicFn(doc, DPI_FAILURE, &error);

    // if content is not in binary form, always use UTF-8
    if (!doc->binaryContent)
        *encoding = DPI_CHARSET_NAME_UTF8;

    // otherwise, determine the encoding from OCI
    else {
        if (dpiOci__attrGet(doc->handle, DPI_OCI_HTYPE_SODA_DOCUMENT,
                (void*) &charsetId, 0, DPI_OCI_ATTR_SODA_JSON_CHARSET_ID,
                "get charset", &error) < 0)
            return dpiGen__endPublicFn(doc, DPI_FAILURE, &error);
        switch (charsetId) {
            case 0:
                *encoding = NULL;
                break;
            case DPI_CHARSET_ID_UTF8:
                *encoding = DPI_CHARSET_NAME_UTF8;
                break;
            case DPI_CHARSET_ID_UTF16BE:
                *encoding = DPI_CHARSET_NAME_UTF16BE;
                break;
            case DPI_CHARSET_ID_UTF16LE:
                *encoding = DPI_CHARSET_NAME_UTF16LE;
                break;
            default:
                dpiError__set(&error, "check charset",
                        DPI_ERR_INVALID_CHARSET_ID, charsetId);
                return dpiGen__endPublicFn(doc, DPI_FAILURE, &error);
        }
    }

    return dpiGen__endPublicFn(doc, DPI_SUCCESS, &error);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_getCreatedOn() [PUBLIC]
//   Return the created timestamp of the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_getCreatedOn(dpiSodaDoc *doc, const char **value,
        uint32_t *valueLength)
{
    return dpiSodaDoc__getAttributeText(doc,
            DPI_OCI_ATTR_SODA_CREATE_TIMESTAMP, value, valueLength, __func__);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_getKey() [PUBLIC]
//   Return the key of the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_getKey(dpiSodaDoc *doc, const char **value,
        uint32_t *valueLength)
{
    return dpiSodaDoc__getAttributeText(doc, DPI_OCI_ATTR_SODA_KEY, value,
            valueLength, __func__);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_getLastModified() [PUBLIC]
//   Return the last modified timestamp of the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_getLastModified(dpiSodaDoc *doc, const char **value,
        uint32_t *valueLength)
{
    return dpiSodaDoc__getAttributeText(doc,
            DPI_OCI_ATTR_SODA_LASTMOD_TIMESTAMP, value, valueLength, __func__);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_getMediaType() [PUBLIC]
//   Return the media type of the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_getMediaType(dpiSodaDoc *doc, const char **value,
        uint32_t *valueLength)
{
    return dpiSodaDoc__getAttributeText(doc, DPI_OCI_ATTR_SODA_MEDIA_TYPE,
            value, valueLength, __func__);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_getVersion() [PUBLIC]
//   Return the version of the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_getVersion(dpiSodaDoc *doc, const char **value,
        uint32_t *valueLength)
{
    return dpiSodaDoc__getAttributeText(doc, DPI_OCI_ATTR_SODA_VERSION,
            value, valueLength, __func__);
}


//-----------------------------------------------------------------------------
// dpiSodaDoc_release() [PUBLIC]
//   Release a reference to the SODA document.
//-----------------------------------------------------------------------------
int dpiSodaDoc_release(dpiSodaDoc *doc)
{
    return dpiGen__release(doc, DPI_HTYPE_SODA_DOC, __func__);
}
