/*[
 * This file is part of the SharedComponents Software.
 * 
 * Copyright 2008 Paradigm B.V. and/or its affiliates and subsidiaries.
 * All rights reserved.
 * 
 * Use of this software is subject to the terms and conditions of the license
 * agreement with Paradigm Geophysical B.V. and/or its affiliates and
 * subsidiaries (collectively "Paradigm").
 * 
 * Warning: This computer program is protected by copyright law and international
 * treaties. Unauthorized reproduction or distribution of this program, or any
 * portion of it, may result in severe civil and criminal penalties, and will be
 * prosecuted to the maximum extent possible under the law.
]*/

#include <designer/Plugins.h>
#include <widgets/ExpandableFrame.h>

#include <QtCore/QtPlugin>
#include <QtCore/QList>
#include <QtGui/QErrorMessage>
#include <QtGui/QAction>

#include <QtDesigner/QDesignerContainerExtension>
#include <QtDesigner/QDesignerFormEditorInterface>
#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtDesigner/QDesignerFormWindowCursorInterface>
#include <QtDesigner/QExtensionManager>


static const char* paradigmDesignerGroup = QT_TR_NOOP("Paradigm Widgets");

namespace pdgm {
namespace gui {

namespace {
    // Code coming from Qt: tools\designer\src\lib\shared\extensionfactory_p.h
    // ------------ ExtensionFactory:
    //  Template to register extension factories.

    // Extension factory for registering an extension for an object type.
    template <class ExtensionInterface, class Object, class Extension>
    class ParadigmExtensionFactory : public QExtensionFactory {
    public:
        explicit ParadigmExtensionFactory(const QString& iid, QExtensionManager* parent = 0)
        : QExtensionFactory(parent),
            iid_(iid) {
        }

        // Convenience for registering the extension. Do not use for derived classes.
        static void registerExtension(QExtensionManager* mgr, const QString& iid) {
            if( mgr == NULL ) {
                return;
            }
            ParadigmExtensionFactory* factory = new ParadigmExtensionFactory(iid, mgr);
            mgr->registerExtensions(factory, iid);
        }

    protected:
        virtual QObject* createExtension(QObject* qObject, const QString& iid, QObject* parent) const {
            if( iid != iid_ ) {
                return NULL;
            }

            Object* object = checkObject(qObject);
            if( object == NULL ) {
                return NULL;
            }

            return new Extension(object, parent);
        }

    private:
        // Can be overwritten to perform checks on the object.
        // Default does a qobject_cast to the desired class.
        virtual Object* checkObject(QObject* qObject) const {
            return dynamic_cast<Object*>(qObject);
        }

        const QString iid_;
    };


    // Code coming from Qt: tools\designer\src\components\formeditor\default_container.h
    // ------------ SingleChildContainer:
    //  Template for containers that have a single child widget using widget()/setWidget().

    template <class Container>
    class SingleChildContainer : public QDesignerContainerExtension {
    protected:
        explicit SingleChildContainer(Container* widget, bool active = true) :
            active_(active),
            container_(widget)
        {
        }

    public:
        virtual int count() const {
            return  active_ && container_->widget() != NULL ? 1 : 0;
        }

        virtual QWidget* widget(int /*index*/) const {
            return container_->widget();
        }
        virtual int currentIndex() const {
            return active_ && container_->widget() != NULL ? 0 : -1;
        }
        virtual void setCurrentIndex(int /*index*/) {
        }
        virtual void addWidget(QWidget* widget) {
            widget->setParent(container_);
            container_->setWidget(widget);
        }
        virtual void insertWidget(int /*index*/, QWidget* widget) {
            addWidget(widget);
        }
        virtual void remove(int /*index*/) {}

    private:
        const bool active_;
        Container* container_;
    };

    // Code similar to QScrollAreaContainer
    // ------------  ExpandableFrameContainer

    class ExpandableFrameContainer: public QObject, public SingleChildContainer<ExpandableFrame>
    {
        Q_OBJECT
        Q_INTERFACES(QDesignerContainerExtension)
    public:
        // We pass on active=true only if there are no children yet.
        // If there are children, it is a legacy custom widget QScrollArea that has an internal,
        // unmanaged child, in which case we deactivate the extension (otherwise we crash).
        // The child will then not show up in the task menu
        explicit ExpandableFrameContainer(ExpandableFrame* widget, QObject* parent = 0) :
            QObject(parent),
            SingleChildContainer<ExpandableFrame>(widget /*, widget->widget() == 0*/)
        {
        }
    };

    typedef ParadigmExtensionFactory<QDesignerContainerExtension, ExpandableFrame, ExpandableFrameContainer> ExpandableFrameContainerFactory;

}   // namespace



template <typename T_Widget> QWidget* createCustomWidget( QWidget* parent ) {
    return new T_Widget( parent );
}

template <> QWidget* createCustomWidget<ExpandableFrame>( QWidget* parent ) {
    return new ExpandableFrame( parent );
}

typedef QWidget* (*WidgetCreateFunction)( QWidget* );

// not used yet, but very likely to be at one point
typedef void ( *PluginInitFunction )( QDesignerCustomWidgetInterface* , QDesignerFormEditorInterface* );

typedef void (*RegisterContainerExtension)(QExtensionManager*, const QString&);
typedef void (*RegisterTaskMenuExtension)(QExtensionManager*, const QString&);


// This static array of structs is used to store the custom widgets info.
// It allows simple addition of custom widgets later on, simply by adding
// a new entry in the array instead of adding new classes for the plugin, and 
// editing domXml() if necessary

struct ParadigmCustomWidgetInterface::Data {
    const char* name;
    const char* group;
    QString icon;
    const char* includeFile;
    const char* toolTip;
    const char* whatsThis;
    WidgetCreateFunction create;
    const char* domXML;
    PluginInitFunction init;
    RegisterTaskMenuExtension registerTaskMenuExtension;
    RegisterContainerExtension registerContainerExtension;
};

namespace {
    static const char* emptyDomXML = NULL;
    static PluginInitFunction noInitFunction = NULL;
    static RegisterTaskMenuExtension noTaskMenuRegistation = NULL;
    static RegisterContainerExtension noContainerRegistration = NULL;
}

static const ParadigmCustomWidgetInterface::Data widget_infos[] = {
    {
        "pdgm::gui::ExpandableFrame",
        paradigmDesignerGroup,
        "expandable_frame",
        "pdgm/gui/widgets/ExpandableFrame.h",
        QT_TR_NOOP("A frame that can be expanded or collapsed"),
        QT_TR_NOOP("A frame that can be expanded or collapsed"),
        &createCustomWidget<ExpandableFrame>,
        "<ui language=\"c++\">\n"
            "<widget class=\"pdgm::gui::ExpandableFrame\" name=\"pdgm_gui_expandableframe\">\n"
                "<widget class=\"QWidget\" name=\"expandableFrameWidget\" native=\"true\"/>\n"
            "</widget>\n"
            "<customwidgets>\n"
                "<customwidget>\n"
                    "<class>pdgm::gui::ExpandableFrame</class>\n"
                    "<extends>QFrame</extends>\n"
                    "<addpagemethod>setWidget</addpagemethod>\n"
                "</customwidget>\n"
            "</customwidgets>\n"
        "</ui>\n",
        noInitFunction,
        noTaskMenuRegistation,
        &ExpandableFrameContainerFactory::registerExtension
    }
};

static const size_t num_widget_infos = sizeof(widget_infos) / sizeof(*widget_infos) ;


/*
 * \brief A plugin to make Paradigm's custom widgets available in Qt Designer
 *
 * Currently the plugin supports the following custom widgets :
 * - ExpandableFrame
 * - ColorSelectorButton
 * - RangeEdit
 * - ColorRangeEdit
 */


ParadigmCustomWidgetCollectionInterface::ParadigmCustomWidgetCollectionInterface( QObject* p ) :
    QObject( p ),
    custom_widgets_()
{
    for ( size_t i = 0 ; i < num_widget_infos ; ++i ) {
        custom_widgets_ << new ParadigmCustomWidgetInterface( widget_infos[i], this );
    }
}

QList<QDesignerCustomWidgetInterface*> ParadigmCustomWidgetCollectionInterface::customWidgets() const
{
    return custom_widgets_;
}

/************************************************************************************/
/*! \class ParadigmCustomWidgetInterface 
 * \brief Implementation of QDesignerCustomWidgetInterface to make each custom widget
 * available in Qt Designer.
 */

ParadigmCustomWidgetInterface::ParadigmCustomWidgetInterface(const Data& data, QObject* parent) :
    QObject( parent ),
    data_( data ),
    initialized_( false )
{
}

QString ParadigmCustomWidgetInterface::name() const
{
    return tr( data_.name );
}

QString ParadigmCustomWidgetInterface::group() const
{
    return tr( data_.group );
}

QString ParadigmCustomWidgetInterface::toolTip() const
{
    return tr( data_.toolTip );
}

QString ParadigmCustomWidgetInterface::whatsThis() const
{
    return tr( data_.whatsThis );
}

QString ParadigmCustomWidgetInterface::includeFile() const
{
    return QString::fromLatin1( data_.includeFile );
}

QIcon ParadigmCustomWidgetInterface::icon() const
{
    QString path = QString(":%1").arg(data_.icon);
    return QIcon( path );
}

bool ParadigmCustomWidgetInterface::isContainer() const
{
    return data_.registerContainerExtension != NULL;
}

QWidget* ParadigmCustomWidgetInterface::createWidget(QWidget* parent)
{
    return data_.create(parent);
}

bool ParadigmCustomWidgetInterface::isInitialized() const
{
    return initialized_;
}

void ParadigmCustomWidgetInterface::initialize(QDesignerFormEditorInterface* formEditor)
{
    if( initialized_ ) {
        return;
    }
    QExtensionManager* manager = formEditor->extensionManager();

    if( manager != NULL ) {
        if( data_.registerTaskMenuExtension != NULL) {
            data_.registerTaskMenuExtension(manager, Q_TYPEID(QDesignerTaskMenuExtension));
        }

        if( data_.registerContainerExtension ) {
            data_.registerContainerExtension(manager, Q_TYPEID(QDesignerContainerExtension));
        }
    }

    if( data_.init ) {
        data_.init(this, formEditor);
    }
    initialized_ = true;
}

QString ParadigmCustomWidgetInterface::codeTemplate() const
{
    return QString();
}

QString ParadigmCustomWidgetInterface::domXml() const
{
    QString geometryProperty;
    if( data_.domXML != NULL ) {
        return QString::fromLatin1(data_.domXML);
    }
    //return QString::fromLatin1("<widget class=\"%1\" name=\"%2\"\n%3</widget>\n")
    //    .arg( name() )
    //    .arg( name().toLower() )
    //    .arg( geometryProperty );
    return QString::fromLatin1(
        "<ui language=\"c++\">\n"
            "<widget class=\"%1\" name=\"%2\">\n"
                "<property name=\"objectName\">\n"
                    "<string notr=\"true\">%2</string>\n"
                "</property>\n"
            "</widget>\n"
        "</ui>\n"
        )
        .arg( name() )
        .arg( name().toLower().replace("::", "_") );

    // for container, add
    //"<widget class=\"QWidget\" name=\"containerName\"/>\n"
}

/*****************************************************************************/


RangeTaskMenuExtension::RangeTaskMenuExtension(QWidget* widget, QObject* parent): QObject(parent), widget_(widget)
{
    editAction_ = new QAction(tr("Edit Range Widget Attributes ..."), this);
    connect(editAction_, SIGNAL(triggered()), this, SLOT(editProperties()));
}

QList<QAction*> RangeTaskMenuExtension::taskActions() const
{
    QList<QAction*> list;
    list.append(editAction_);
    return list;
}

QAction* RangeTaskMenuExtension::preferredEditAction() const
{
    return editAction_ ;
}

void RangeTaskMenuExtension::editProperties()
{
}

void RangeTaskMenuExtension::applyProperties(const QString& properties)
{
    QDesignerFormWindowInterface* formWindow
        = QDesignerFormWindowInterface::findFormWindow(widget_);
    if ( formWindow && formWindow->cursor() )
        formWindow->cursor()->setProperty("propertiesDocument", properties);
}


Q_EXPORT_PLUGIN2( paradigmwidgets, pdgm::gui::ParadigmCustomWidgetCollectionInterface )

} // namespace gui
} // namespace pdgm

#include <Plugins.cpp.moc>