#pragma once

#include <memory>
#include <utility>
#include <jsi/jsi.h>

#include "JsiSkHostObjects.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

#include <SkVertices.h>

#pragma clang diagnostic pop

namespace RNSkia {

    using namespace facebook;

    class JsiSkVertices : public JsiSkWrappingSkPtrHostObject<SkVertices> {
    public:

        JsiSkVertices(std::shared_ptr<RNSkPlatformContext> context,
        sk_sp<SkVertices> vertices)
        : JsiSkWrappingSkPtrHostObject<SkVertices>(std::move(context), std::move(vertices)) {}

        // TODO: declare in JsiSkWrappingSkPtrHostObject via extra template parameter?
        JSI_PROPERTY_GET(__typename__) {
            return jsi::String::createFromUtf8(runtime, "Vertices");
        }

        JSI_EXPORT_PROPERTY_GETTERS(JSI_EXPORT_PROP_GET(JsiSkVertices, __typename__))

        JSI_HOST_FUNCTION(bounds) {
            const auto& result = getObject()->bounds();
            return jsi::Object::createFromHostObject(
                    runtime, std::make_shared<JsiSkRect>(getContext(), result));
        }

        JSI_HOST_FUNCTION(uniqueID) {
            return static_cast<double>(getObject()->uniqueID());
        }

        JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkVertices, bounds),
                             JSI_EXPORT_FUNC(JsiSkVertices, uniqueID), )

        /**
          Returns the underlying object from a host object of this type
         */
        static sk_sp<SkVertices> fromValue(jsi::Runtime &runtime,
                                                 const jsi::Value &obj) {
            return obj.asObject(runtime)
                        .asHostObject<JsiSkVertices>(runtime)
                        ->getObject();
        }

        /**
         * Creates the function for construction a new instance of the SkVertices
         * wrapper
         * @param context platform context
         * @return A function for creating a new host object wrapper for the SkVertices
         * class
         */
        static const jsi::HostFunctionType
        createCtor(std::shared_ptr<RNSkPlatformContext> context) {
            return JSI_HOST_FUNCTION_LAMBDA {
                auto mode = static_cast<SkVertices::VertexMode>(arguments[0].getNumber());
                std::vector<SkPoint> positions;
                std::vector<SkPoint> texs;
                std::vector<SkColor> colors;
                std::vector<uint16_t> indices;

                auto jsiPositions = arguments[1].asObject(runtime).asArray(runtime);
                auto positionsSize = static_cast<int>(jsiPositions.size(runtime));
                positions.reserve(positionsSize);
                for (int i = 0; i < positionsSize; i++) {
                    std::shared_ptr<SkPoint> point = JsiSkPoint::fromValue(
                            runtime, jsiPositions.getValueAtIndex(runtime, i).asObject(runtime));
                    positions.push_back(*point.get());
                }

                if (count >= 3 && !arguments[2].isNull() && !arguments[2].isUndefined()) {
                    auto jsiTexs = arguments[2].asObject(runtime).asArray(runtime);
                    auto texsSize = jsiTexs.size(runtime);
                    texs.reserve(texsSize);
                    for (int i = 0; i < texsSize; i++) {
                        auto point = JsiSkPoint::fromValue(
                                runtime, jsiTexs.getValueAtIndex(runtime, i).asObject(runtime));
                        texs.push_back(*point.get());
                    }
                }

                if (count >= 4 && !arguments[3].isNull() && !arguments[3].isUndefined()) {
                    auto jsiColors = arguments[3].asObject(runtime).asArray(runtime);
                    auto colorsSize = jsiColors.size(runtime);
                    colors.reserve(colorsSize);
                    for (int i = 0; i < colorsSize; i++) {
                        SkColor color = JsiSkColor::fromValue(runtime, jsiColors.getValueAtIndex(runtime, i));
                        colors.push_back(color);
                    }
                }

                int indicesSize = 0;
                if (count >= 5 && !arguments[4].isNull() && !arguments[4].isUndefined()) {
                    auto jsiIndices = arguments[4].asObject(runtime).asArray(runtime);
                    indicesSize = static_cast<int>(jsiIndices.size(runtime));
                    indices.reserve(indicesSize);
                    for (int i = 0; i < indicesSize; i++) {
                        uint16_t index = jsiIndices.getValueAtIndex(runtime, i).asNumber();
                        indices.push_back(index);
                    }
                }
// TODO: this is the technic used in CanvasKit: https://github.com/google/skia/blob/main/modules/canvaskit/interface.js#L1216
// Note that the isVolatile parameter is unused when using MakeCopy()
//                auto isVolatile = count >= 6 && !arguments[5].isNull() && !arguments[5].isUndefined() ? arguments[5].getBool() : false;
//                auto flags = 0;
//                // These flags are from SkVertices.h and should be kept in sync with those.
//                if (texs.size() > 0) {
//                    flags |= (1 << 0);
//                }
//                if (colors.size() > 0) {
//                    flags |= (1 << 1);
//                }
//                if (!isVolatile) {
//                    flags |= (1 << 2);
//                }
//                auto builder = SkVertices::Builder(mode, positionsSize/2, indicesSize, flags);
//                std::copy(positions.data(), positions.data() + positionsSize, builder.positions());
//                if (builder.texCoords()) {
//                    std::copy(std::begin(texs), std::end(texs), builder.texCoords());
//                }
//                if (builder.colors()) {
//                    std::copy(std::begin(colors), std::end(colors), builder.colors());
//                }
//                if (builder.indices()) {
//                    std::copy(std::begin(indices), std::end(indices), builder.indices());
//                }
//                auto vertices = builder.detach();
                auto vertices = SkVertices::MakeCopy(mode, positionsSize, positions.data(), texs.data(), colors.data(), indicesSize, indices.data());
                return jsi::Object::createFromHostObject(
                        runtime, std::make_shared<JsiSkVertices>(context, std::move(vertices)));
            };
        }
    };
} // namespace RNSkia
