/************************************************************************
 *
 * Copyright 2010 - 2012 Jakob Leben (jakob.leben@gmail.com)
 *
 * This file is part of SuperCollider Qt GUI.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ************************************************************************/

#include "metatype.hpp"
#include "type_codec.hpp"
#include "Common.h" // Make sure PyrObject* is declared to QMetaType
#include <PyrKernel.h>

#include "QcCallback.hpp"

namespace QtCollider {

static QVector<MetaType*> gMetaTypes;

template <typename T> MetaTypeImpl<T>* MetaTypeImpl<T>::instance = 0;

template <typename T> void qc_init_metatype() {
    MetaTypeImpl<T>::instance = new MetaTypeImpl<T>();
    gMetaTypes.append(MetaTypeImpl<T>::instance);
}

void MetaType::initAll() {
    qRegisterMetaType<PyrObject*>();
    qRegisterMetaType<QObjectProxy*>();
    qRegisterMetaType<QcTreeWidget::ItemPtr>();
    qRegisterMetaType<QVector<double>>();
    qRegisterMetaType<QVector<int>>();
    qRegisterMetaType<SharedImage>();
    qRegisterMetaType<QcAction*>();
    qRegisterMetaType<QcWidgetAction*>();
    qRegisterMetaType<QcMenu*>();
    qRegisterMetaType<QcToolBar*>();
    qRegisterMetaType<QList<QAction*>>();

    gMetaTypes.reserve(30);

    qc_init_metatype<bool>();
    qc_init_metatype<int>();
    qc_init_metatype<float>();
    qc_init_metatype<double>();
    qc_init_metatype<QChar>();
    qc_init_metatype<QString>();
    qc_init_metatype<QPointF>();
    qc_init_metatype<QPoint>();
    qc_init_metatype<QSizeF>();
    qc_init_metatype<QSize>();
    qc_init_metatype<QRectF>();
    qc_init_metatype<QRect>();
    qc_init_metatype<QColor>();
    qc_init_metatype<QFont>();
    qc_init_metatype<QPalette>();
    qc_init_metatype<QObjectProxy*>();
    qc_init_metatype<QObject*>();
    qc_init_metatype<QWidget*>();
    qc_init_metatype<QLayout*>();
    qc_init_metatype<PyrObject*>();
    qc_init_metatype<QcTreeWidget::ItemPtr>();
    qc_init_metatype<SharedImage>();
    qc_init_metatype<QMenu*>();
    qc_init_metatype<QAction*>();
    qc_init_metatype<QList<QAction*>>();
    qc_init_metatype<QVector<double>>();
    qc_init_metatype<QVector<int>>();
    qc_init_metatype<QVariantList>();
    qc_init_metatype<QUrl>();
    qc_init_metatype<QcCallback*>();
}

MetaType* MetaType::find(PyrSlot* slot) {
    switch (GetTag(slot)) {
    case tagNil:
        return 0;
    case tagInt:
        return metaType<int>();
    case tagSym:
        return metaType<QString>();
    case tagChar:
        return metaType<QChar>();
    case tagFalse:
    case tagTrue:
        return metaType<bool>();
    case tagObj: {
        PyrObject* obj = slotRawObject(slot);
        PyrClass* klass = obj->classptr;
        unsigned char format = obj->obj_format;

        if (format == obj_double || format == obj_float)
            return metaType<QVector<double>>();

        else if (format == obj_int32 || format == obj_int16 || format == obj_int8)
            return metaType<QVector<int>>();

        else if (isKindOfSlot(slot, class_string)) {
            return metaType<QString>();
        } else if (isKindOfSlot(slot, SC_CLASS(Point))) {
            return metaType<QPointF>();
        } else if (isKindOfSlot(slot, SC_CLASS(Rect))) {
            return metaType<QRectF>();
        } else if (isKindOfSlot(slot, SC_CLASS(Size))) {
            return metaType<QSizeF>();
        } else if (klass == SC_CLASS(Color) || klass == SC_CLASS(Gradient) || klass == SC_CLASS(HiliteGradient)) {
            return metaType<QColor>();
        } else if (isKindOfSlot(slot, SC_CLASS(Font))) {
            return metaType<QFont>();
        } else if (isKindOfSlot(slot, SC_CLASS(QPalette))) {
            return metaType<QPalette>();
        } else if (isKindOfSlot(slot, SC_CLASS(QCallback))) {
            return metaType<QcCallback*>();
        } else if (isKindOfSlot(slot, SC_CLASS(AbstractMenuAction))) {
            return metaType<QAction*>();
        } else if (isKindOfSlot(slot, SC_CLASS(Menu))) {
            return metaType<QMenu*>();
        } else if (isKindOfSlot(slot, SC_CLASS(View)) || isKindOfSlot(slot, SC_CLASS(ScrollCanvas))) {
            return metaType<QWidget*>();
        } else if (isKindOfSlot(slot, SC_CLASS(Layout))) {
            return metaType<QLayout*>();
        } else if (isKindOfSlot(slot, SC_CLASS(QObject))) {
            return metaType<QObjectProxy*>();
        } else if (isKindOfSlot(slot, class_array) || isKindOfSlot(slot, class_symbolarray)) {
            return metaType<QVariantList>();
        } else if (isKindOfSlot(slot, SC_CLASS(TreeViewItem))) {
            return metaType<QcTreeWidget::ItemPtr>();
        } else if (isKindOfSlot(slot, SC_CLASS(Image))) {
            return metaType<SharedImage>();
        } else {
            QString className = TypeCodec<QString>::read(&slotRawObject(slot)->classptr->name);
            qcWarningMsg(QStringLiteral("WARNING: Do not know how to use an instance of class '%1'").arg(className));
            return 0;
        }
    }
    default:
        return metaType<double>();
    }
}

MetaType* MetaType::find(int id) {
    int n = gMetaTypes.count();
    MetaType* const* d = gMetaTypes.constData();

    int i;
    for (i = 0; i < n; ++i) {
        if ((*d)->mId == id)
            return *d;
        ++d;
    }

    return 0;
}

} // namespace QtCollider
