/*
 * Copyright © Dynamsoft Corporation. All rights reserved.
 */

#include "ImageDataHostObject.hpp"
#include "MutableRawBuffer.hpp"

#define GET_VALUE(X) if(name==#X){return this->X;}
#define SET_INT_VALUE(X) if(name==#X){this->X = (int) value.asNumber();}

ImageDataHostObject::ImageDataHostObject(int width, int height, int stride, int format, int orientation, uint8_t *buffer, int bufferSize)
    : width(width), height(height), stride(stride), format(format), orientation(orientation), buffer(buffer), bufferSize(bufferSize) {}

ImageDataHostObject ImageDataHostObject::getHOFromObject(Runtime &runtime, Object &jsObj) {
    if (jsObj.isHostObject<ImageDataHostObject>(runtime)) {
        return *((ImageDataHostObject *) (jsObj.asHostObject(runtime).get()));
    }
    ImageDataHostObject imageHO;
    int width = jsObj.hasProperty(runtime, "width") ? (int) jsObj.getProperty(runtime, "width").asNumber() : 0;
    int height = jsObj.hasProperty(runtime, "height") ? (int) jsObj.getProperty(runtime, "height").asNumber() : 0;
    int stride = jsObj.hasProperty(runtime, "stride") ? (int) jsObj.getProperty(runtime, "stride").asNumber() : 0;
    int format = jsObj.hasProperty(runtime, "format") ? (int) jsObj.getProperty(runtime, "format").asNumber() : 0;
    int orientation = jsObj.hasProperty(runtime, "orientation") ? (int) jsObj.getProperty(runtime, "orientation").asNumber() : 0;
    uint8_t *buffer = nullptr;
    int bufferSize = 0;
    if (jsObj.hasProperty(runtime, "buffer")) {
        ArrayBuffer array_buffer = jsObj.getProperty(runtime, "buffer").asObject(runtime).getArrayBuffer(runtime);
        buffer = array_buffer.data(runtime);
        bufferSize = (int) array_buffer.length(runtime);
    }
    return ImageDataHostObject(width, height, stride, format, orientation, buffer, bufferSize);
}

Value ImageDataHostObject::get(Runtime &runtime, const PropNameID &propName) {
    auto name = propName.utf8(runtime);
    GET_VALUE(width)
    GET_VALUE(height)
    GET_VALUE(stride)
    GET_VALUE(format)
    GET_VALUE(orientation)
    GET_VALUE(bufferSize)
    if (name == "buffer") {
        auto mutableBuffer = std::make_shared<MutableRawBuffer>(buffer, bufferSize, false);
        ArrayBuffer arrayBuffer(runtime, mutableBuffer);
        auto destinationBuffer = arrayBuffer.data(runtime);
        memcpy(destinationBuffer, buffer, sizeof(uint8_t) * bufferSize);
        return arrayBuffer;
    }
    if (name == "release") {
        auto release = [=, this](Runtime & runtime, const Value& thisValue, const Value* arguments, size_t count) -> Value {
            if(buffer != nullptr) {
                delete[] buffer;
                buffer = nullptr;
                bufferSize = 0;
            }
            return {};
        };
        return Function::createFromHostFunction(runtime, PropNameID::forUtf8(runtime, "release"), 0, release);
    }
    return {};
}

void ImageDataHostObject::set(Runtime &runtime, const PropNameID &propName, const Value &value) {
    auto name = propName.utf8(runtime);
    SET_INT_VALUE(width)
    SET_INT_VALUE(height)
    SET_INT_VALUE(stride)
    SET_INT_VALUE(format)
    SET_INT_VALUE(orientation)
    if (name == "buffer") {
        auto arrayBuffer = value.asObject(runtime).getArrayBuffer(runtime);
        this->buffer = arrayBuffer.data(runtime);
        this->bufferSize = (int) arrayBuffer.length(runtime);
    }
}

std::vector<PropNameID> ImageDataHostObject::getPropertyNames(Runtime &rt) {
    std::vector<PropNameID> result;
    result.push_back(PropNameID::forUtf8(rt, std::string("width")));
    result.push_back(PropNameID::forUtf8(rt, std::string("height")));
    result.push_back(PropNameID::forUtf8(rt, std::string("stride")));
    result.push_back(PropNameID::forUtf8(rt, std::string("format")));
    result.push_back(PropNameID::forUtf8(rt, std::string("orientation")));
    result.push_back(PropNameID::forUtf8(rt, std::string("buffer")));
    result.push_back(PropNameID::forUtf8(rt, std::string("release")));
    return result;
}
