
#include "QtWidgets/QComboBox/qcombobox_wrap.h"

#include <QWidget>

#include "Extras/Utils/nutils.h"
#include "QtCore/QModelIndex/qmodelindex_wrap.h"
#include "QtCore/QVariant/qvariant_wrap.h"
#include "QtGui/QIcon/qicon_wrap.h"
#include "QtWidgets/QLineEdit/qlineedit_wrap.h"
#include "QtWidgets/QStandardItemModel/qstandarditemmodel_wrap.h"
#include "QtWidgets/QWidget/qwidget_wrap.h"

Napi::FunctionReference QComboBoxWrap::constructor;

Napi::Object QComboBoxWrap::init(Napi::Env env, Napi::Object exports)
{
    Napi::HandleScope scope(env);
    char CLASSNAME[] = "QComboBox";
    Napi::Function func =
        DefineClass(env, CLASSNAME,
                    {InstanceMethod("addItem", &QComboBoxWrap::addItem),
                     InstanceMethod("insertItem", &QComboBoxWrap::insertItem),
                     InstanceMethod("addItems", &QComboBoxWrap::addItems),
                     InstanceMethod("insertItems", &QComboBoxWrap::insertItems),
                     InstanceMethod("currentIndex", &QComboBoxWrap::currentIndex),
                     InstanceMethod("currentText", &QComboBoxWrap::currentText),
                     InstanceMethod("insertSeparator", &QComboBoxWrap::insertSeparator),
                     InstanceMethod("itemText", &QComboBoxWrap::itemText),
                     InstanceMethod("itemData", &QComboBoxWrap::itemData),
                     InstanceMethod("removeItem", &QComboBoxWrap::removeItem),
                     InstanceMethod("sizeAdjustPolicy", &QComboBoxWrap::sizeAdjustPolicy),
                     InstanceMethod("setSizeAdjustPolicy", &QComboBoxWrap::setSizeAdjustPolicy),
                     InstanceMethod("maxVisibleItems", &QComboBoxWrap::maxVisibleItems),
                     InstanceMethod("setMaxVisibleItems", &QComboBoxWrap::setMaxVisibleItems),
                     InstanceMethod("isEditable", &QComboBoxWrap::isEditable),
                     InstanceMethod("setEditable", &QComboBoxWrap::setEditable),
                     InstanceMethod("clear", &QComboBoxWrap::clear),
                     InstanceMethod("setModel", &QComboBoxWrap::setModel),
                     InstanceMethod("setEditText", &QComboBoxWrap::setEditText),
                     InstanceMethod("count", &QComboBoxWrap::count),
                     InstanceMethod("duplicatesEnabled", &QComboBoxWrap::duplicatesEnabled),
                     InstanceMethod("hasFrame", &QComboBoxWrap::hasFrame),
                     InstanceMethod("hidePopup", &QComboBoxWrap::hidePopup),
                     InstanceMethod("iconSize", &QComboBoxWrap::iconSize),
                     InstanceMethod("maxCount", &QComboBoxWrap::maxCount),
                     InstanceMethod("minimumContentsLength", &QComboBoxWrap::minimumContentsLength),
                     InstanceMethod("modelColumn", &QComboBoxWrap::modelColumn),
                     InstanceMethod("rootModelIndex", &QComboBoxWrap::rootModelIndex),
                     InstanceMethod("setDuplicatesEnabled", &QComboBoxWrap::setDuplicatesEnabled),
                     InstanceMethod("setMinimumContentsLength", &QComboBoxWrap::setMinimumContentsLength),
                     InstanceMethod("setModelColumn", &QComboBoxWrap::setModelColumn),
                     InstanceMethod("setRootModelIndex", &QComboBoxWrap::setRootModelIndex),
                     InstanceMethod("clearEditText", &QComboBoxWrap::clearEditText),
                     InstanceMethod("setFrame", &QComboBoxWrap::setFrame),
                     InstanceMethod("setItemText", &QComboBoxWrap::setItemText),
                     InstanceMethod("setMaxCount", &QComboBoxWrap::setMaxCount),
                     InstanceMethod("showPopup", &QComboBoxWrap::showPopup),
                     InstanceMethod("insertPolicy", &QComboBoxWrap::insertPolicy),
                     InstanceMethod("setInsertPolicy", &QComboBoxWrap::setInsertPolicy),
                     InstanceMethod("sizeAdjustPolicy", &QComboBoxWrap::sizeAdjustPolicy),
                     InstanceMethod("setIconSize", &QComboBoxWrap::setIconSize),
                     QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QComboBoxWrap)});
    constructor = Napi::Persistent(func);
    exports.Set(CLASSNAME, func);
    QOBJECT_REGISTER_WRAPPER(QComboBox, QComboBoxWrap);
    return exports;
}

QComboBox *QComboBoxWrap::getInternalInstance()
{
    return this->instance;
}

QComboBoxWrap::~QComboBoxWrap()
{
    extrautils::safeDelete(this->instance);
}

QComboBoxWrap::QComboBoxWrap(const Napi::CallbackInfo &info) : Napi::ObjectWrap<QComboBoxWrap>(info)
{
    Napi::Env env = info.Env();
    size_t argCount = info.Length();
    if (argCount == 0)
    {
        // --- Construct a new instance
        this->instance = new NComboBox();
    }
    else if (argCount == 1)
    {
        if (info[0].IsExternal())
        {
            // --- Wrap a given C++ instance
            this->instance = info[0].As<Napi::External<QComboBox>>().Data();
        }
        else
        {
            // --- Construct a new instance and pass a parent
            Napi::Object parentObject = info[0].As<Napi::Object>();
            NodeWidgetWrap *parentWidgetWrap = Napi::ObjectWrap<NodeWidgetWrap>::Unwrap(parentObject);
            this->instance = new NComboBox(parentWidgetWrap->getInternalInstance());
        }
    }
    else
    {
        Napi::TypeError::New(env, "Vixen: QComboBoxWrap: Wrong number of arguments to constructor")
            .ThrowAsJavaScriptException();
    }
    this->rawData = extrautils::configureQWidget(this->getInternalInstance(), true);
}

Napi::Value QComboBoxWrap::addItem(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    if (info.Length() == 3)
    {
        Napi::Object iconWrap = info[0].As<Napi::Object>();
        QIconWrap *iconWrapValue = Napi::ObjectWrap<QIconWrap>::Unwrap(iconWrap);
        QIcon *icon = iconWrapValue->getInternalInstance();
        std::string text = info[1].As<Napi::String>().Utf8Value();
        Napi::Object variantWrap = info[2].As<Napi::Object>();
        QVariantWrap *variantWrapValue = Napi::ObjectWrap<QVariantWrap>::Unwrap(variantWrap);
        QVariant *variant = variantWrapValue->getInternalInstance();
        this->instance->addItem(*icon, text.c_str(), *variant);
    }
    else
    {
        std::string text = info[0].As<Napi::String>().Utf8Value();
        Napi::Object variantWrap = info[1].As<Napi::Object>();
        QVariantWrap *variantWrapValue = Napi::ObjectWrap<QVariantWrap>::Unwrap(variantWrap);
        QVariant *variant = variantWrapValue->getInternalInstance();
        this->instance->addItem(text.c_str(), *variant);
    }

    return env.Null();
}

Napi::Value QComboBoxWrap::insertItem(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();
    std::string text = info[1].As<Napi::String>().Utf8Value();

    this->instance->insertItem(index, text.c_str());
    return env.Null();
}

Napi::Value QComboBoxWrap::addItems(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    Napi::Array textsNapi = info[0].As<Napi::Array>();
    QList<QString> list;
    for (int i = 0; i < textsNapi.Length(); i++)
    {
        Napi::Value textNapi = textsNapi[i];
        list.append(textNapi.As<Napi::String>().Utf8Value().c_str());
    }
    QStringList texts = QStringList(list);

    this->instance->addItems(texts);
    return env.Null();
}

Napi::Value QComboBoxWrap::insertItems(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();
    Napi::Array textsNapi = info[1].As<Napi::Array>();
    QList<QString> list;
    for (int i = 0; i < textsNapi.Length(); i++)
    {
        Napi::Value textNapi = textsNapi[i];
        list.append(textNapi.As<Napi::String>().Utf8Value().c_str());
    }
    QStringList texts = QStringList(list);

    this->instance->insertItems(index, texts);
    return env.Null();
}

Napi::Value QComboBoxWrap::currentIndex(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    return Napi::Number::New(env, this->instance->currentIndex());
}

Napi::Value QComboBoxWrap::currentText(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    return Napi::String::New(env, this->instance->currentText().toStdString());
}

Napi::Value QComboBoxWrap::insertSeparator(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();

    this->instance->insertSeparator(index);
    return env.Null();
}

Napi::Value QComboBoxWrap::itemText(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();

    return Napi::String::New(env, this->instance->itemText(index).toStdString());
}

Napi::Value QComboBoxWrap::itemData(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();
    QVariant *variant = new QVariant(this->instance->itemData(index));
    auto variantWrap = QVariantWrap::constructor.New({Napi::External<QVariant>::New(env, variant)});
    return variantWrap;
}

Napi::Value QComboBoxWrap::removeItem(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();

    this->instance->removeItem(index);
    return env.Null();
}

Napi::Value QComboBoxWrap::sizeAdjustPolicy(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    return Napi::Number::New(env, this->instance->sizeAdjustPolicy());
}
Napi::Value QComboBoxWrap::setSizeAdjustPolicy(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QComboBox::SizeAdjustPolicy policy =
        static_cast<QComboBox::SizeAdjustPolicy>(info[0].As<Napi::Number>().Int32Value());
    this->instance->setSizeAdjustPolicy(policy);
    return env.Null();
}

Napi::Value QComboBoxWrap::maxVisibleItems(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    return Napi::Number::New(env, this->instance->maxVisibleItems());
}

Napi::Value QComboBoxWrap::setMaxVisibleItems(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int maxItems = info[0].As<Napi::Number>().Int32Value();

    this->instance->setMaxVisibleItems(maxItems);
    return env.Null();
}

Napi::Value QComboBoxWrap::isEditable(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    return Napi::Boolean::New(env, this->instance->isEditable());
}

Napi::Value QComboBoxWrap::setEditable(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    bool editable = info[0].As<Napi::Boolean>().Value();

    this->instance->setEditable(editable);
    return env.Null();
}

Napi::Value QComboBoxWrap::clear(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    this->instance->clear();
    return env.Null();
}

Napi::Value QComboBoxWrap::setModel(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    Napi::Object itemObject = info[0].As<Napi::Object>();
    QStandardItemModelWrap *modelWrap = Napi::ObjectWrap<QStandardItemModelWrap>::Unwrap(itemObject);
    QStandardItemModel *model = modelWrap->getInternalInstance();

    this->instance->setModel(model);
    return env.Null();
}
Napi::Value QComboBoxWrap::setEditText(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    Napi::String text = info[0].As<Napi::String>();
    this->instance->setEditText(text.Utf8Value().c_str());

    return env.Null();
}
Napi::Value QComboBoxWrap::count(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int result = this->instance->count();
    return Napi::Number::New(env, result);
}
Napi::Value QComboBoxWrap::duplicatesEnabled(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    bool result = this->instance->duplicatesEnabled();
    return Napi::Boolean::New(env, result);
}
Napi::Value QComboBoxWrap::hasFrame(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    bool result = this->instance->hasFrame();
    return Napi::Boolean::New(env, result);
}

Napi::Value QComboBoxWrap::hidePopup(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    this->instance->hidePopup();
    return env.Null();
}

Napi::Value QComboBoxWrap::iconSize(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QSize result = this->instance->iconSize();
    auto resultInstance = QSizeWrap::constructor.New({Napi::External<QSize>::New(env, new QSize(result))});
    return resultInstance;
}
Napi::Value QComboBoxWrap::maxCount(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int result = this->instance->maxCount();
    return Napi::Number::New(env, result);
}
Napi::Value QComboBoxWrap::minimumContentsLength(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int result = this->instance->minimumContentsLength();
    return Napi::Number::New(env, result);
}
Napi::Value QComboBoxWrap::modelColumn(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int result = this->instance->modelColumn();
    return Napi::Number::New(env, result);
}
Napi::Value QComboBoxWrap::rootModelIndex(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QModelIndex result = this->instance->rootModelIndex();
    auto resultInstance =
        QModelIndexWrap::constructor.New({Napi::External<QModelIndex>::New(env, new QModelIndex(result))});
    return resultInstance;
}
Napi::Value QComboBoxWrap::setDuplicatesEnabled(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    bool enable = info[0].As<Napi::Boolean>().Value();
    this->instance->setDuplicatesEnabled(enable);
    return env.Null();
}
Napi::Value QComboBoxWrap::setMinimumContentsLength(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int characters = info[0].As<Napi::Number>().Int32Value();
    this->instance->setMinimumContentsLength(characters);
    return env.Null();
}
Napi::Value QComboBoxWrap::setModelColumn(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int visibleColumn = info[0].As<Napi::Number>().Int32Value();
    this->instance->setModelColumn(visibleColumn);
    return env.Null();
}
Napi::Value QComboBoxWrap::setRootModelIndex(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QModelIndexWrap *indexWrap = Napi::ObjectWrap<QModelIndexWrap>::Unwrap(info[0].As<Napi::Object>());
    QModelIndex *index = indexWrap->getInternalInstance();
    this->instance->setRootModelIndex(*index);
    return env.Null();
}
Napi::Value QComboBoxWrap::clearEditText(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    this->instance->clearEditText();
    return env.Null();
}
Napi::Value QComboBoxWrap::setFrame(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    bool f = info[0].As<Napi::Boolean>().Value();
    this->instance->setFrame(f);
    return env.Null();
}
Napi::Value QComboBoxWrap::setItemText(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int index = info[0].As<Napi::Number>().Int32Value();
    std::string textNapiText = info[1].As<Napi::String>().Utf8Value();
    QString text = QString::fromUtf8(textNapiText.c_str());
    this->instance->setItemText(index, text);
    return env.Null();
}
Napi::Value QComboBoxWrap::setMaxCount(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    int max = info[0].As<Napi::Number>().Int32Value();
    this->instance->setMaxCount(max);
    return env.Null();
}
Napi::Value QComboBoxWrap::showPopup(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    this->instance->showPopup();
    return env.Null();
}
Napi::Value QComboBoxWrap::insertPolicy(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QComboBox::InsertPolicy result = this->instance->insertPolicy();
    return Napi::Number::New(env, static_cast<uint>(result));
}
Napi::Value QComboBoxWrap::setInsertPolicy(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QComboBox::InsertPolicy policy = static_cast<QComboBox::InsertPolicy>(info[0].As<Napi::Number>().Int32Value());
    this->instance->setInsertPolicy(policy);
    return env.Null();
}
Napi::Value QComboBoxWrap::setIconSize(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    QSizeWrap *sizeWrap = Napi::ObjectWrap<QSizeWrap>::Unwrap(info[0].As<Napi::Object>());
    QSize *size = sizeWrap->getInternalInstance();
    this->instance->setIconSize(*size);
    return env.Null();
}
