//
// Created by Allen on 24/08/26.
//

#include "ImgHO.h"
#include "RawBuffer.h"
#include "android_log.h"

#define JSI_FUNC [=](Runtime & runtime, const Value& thisValue, const Value* arguments, size_t count) -> Value
#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(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)
    //todo
    if (name == "buffer") {
        auto mutableBuffer = std::make_shared<MutableRawBuffer>(buffer, bufferSize, false);
        return ArrayBuffer(runtime, mutableBuffer);
    }

    if (name == "release") {
        auto release = JSI_FUNC {
            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)
    //todo
    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;
}

void ImageDataHostObject::logString(const char *preStr) const {
    LOGI("%s wid:%d, hgt:%d, stride:%d, format:%d, orientation:%d, bufferSize:%d, buffer:%p, this:%p",
         preStr, width, height, stride, format, orientation, bufferSize, buffer, this);
}