#include "CPropertyView.h"
#include "CPropertyDelegate.h"
#include "Editor/WorldEditor/CTemplateEditDialog.h"
#include <Core/Resource/Script/Property/Properties.h>

#include <QEvent>
#include <QMenu>
#include <QToolTip>

CPropertyView::CPropertyView(QWidget *pParent)
    : QTreeView(pParent)
    , mpMenuProperty(nullptr)
{
    mpModel = new CPropertyModel(this);
    mpDelegate = new CPropertyDelegate(this);
    setItemDelegateForColumn(1, mpDelegate);
    setEditTriggers(AllEditTriggers);
    setModel(mpModel);

    setContextMenuPolicy(Qt::CustomContextMenu);

    mpShowNameValidityAction = new QAction("Show whether property name is correct", this);
    mpShowNameValidityAction->setCheckable(true);
    mpShowNameValidityAction->setChecked(false);
    connect(mpShowNameValidityAction, SIGNAL(triggered(bool)), this, SLOT(ToggleShowNameValidity(bool)));

    if (gTemplatesWritable)
    {
        mpEditTemplateAction = new QAction("Edit template", this);
        connect(mpEditTemplateAction, SIGNAL(triggered()), this, SLOT(EditPropertyTemplate()));
    }
    else
    {
        mpEditTemplateAction = new QAction("Template files not writable", this);
        mpEditTemplateAction->setEnabled(false);
    }

    mpGenNamesForPropertyAction = new QAction("Generate names for this property", this);
    mpGenNamesForSiblingsAction = new QAction(this); // Text set in CreateContextMenu()
    mpGenNamesForChildrenAction = new QAction(this); // Text set in CreateContextMenu()
    connect(mpGenNamesForPropertyAction, SIGNAL(triggered(bool)), this, SLOT(GenerateNamesForProperty()));
    connect(mpGenNamesForSiblingsAction, SIGNAL(triggered(bool)), this, SLOT(GenerateNamesForSiblings()));
    connect(mpGenNamesForChildrenAction, SIGNAL(triggered(bool)), this, SLOT(GenerateNamesForChildren()));

    connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(SetPersistentEditors(QModelIndex)));
    connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(edit(QModelIndex)));
    connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CreateContextMenu(QPoint)));
    connect(mpModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(SetPersistentEditors(QModelIndex)));
    connect(mpModel, SIGNAL(PropertyModified(const QModelIndex&)), this, SLOT(OnPropertyModified(const QModelIndex&)));
}

void CPropertyView::setModel(QAbstractItemModel *pModel)
{
    CPropertyModel *pPropModel = qobject_cast<CPropertyModel*>(pModel);
    mpModel = pPropModel;
    mpDelegate->SetPropertyModel(pPropModel);
    QTreeView::setModel(pPropModel);

    if (pPropModel)
    {
        QModelIndex Root = pPropModel->index(0, 0, QModelIndex());
        SetPersistentEditors(Root);
        setExpanded(Root, true);
    }
}

bool CPropertyView::event(QEvent *pEvent)
{
    if (pEvent->type() == QEvent::ToolTip)
    {
        QPoint MousePos = QCursor::pos();
        QModelIndex Index = indexAt(viewport()->mapFromGlobal(MousePos));

        if (Index.isValid())
        {
            QString Desc = mpModel->data(Index, Qt::ToolTipRole).toString();

            if (!Desc.isEmpty())
            {
                QToolTip::showText(MousePos, Desc, this);
                pEvent->accept();
                return true;
            }
        }

        QToolTip::hideText();
        pEvent->ignore();
        return true;
    }
    else if (pEvent->type() == QEvent::Resize && !isVisible())
    {
        resizeColumnToContents(0);
    }

    return QTreeView::event(pEvent);
}

int CPropertyView::sizeHintForColumn(int Column) const
{
    if (Column == 0)
        return width() * 0.6f;
    else
        return width() * 0.4f;
}

void CPropertyView::SetEditor(IEditor* pEditor)
{
    mpDelegate->SetEditor(pEditor);
}

void CPropertyView::ClearProperties()
{
    mpObject = nullptr;
    mpModel->ConfigureScript(nullptr, nullptr, nullptr);
}

void CPropertyView::SetIntrinsicProperties(CStructRef InProperties)
{
    mpObject = nullptr;
    mpModel->ConfigureIntrinsic(nullptr, InProperties.Property(), InProperties.DataPointer());
    SetPersistentEditors(QModelIndex());
}

void CPropertyView::SetInstance(CScriptObject *pObj)
{
    mpObject = pObj;
    mpModel->SetBoldModifiedProperties(gpEdApp->CurrentGame() > EGame::Prime);

    if (pObj)
        mpModel->ConfigureScript(pObj->Area()->Entry()->Project(), pObj->Template()->Properties(), pObj);
    else
        mpModel->ConfigureScript(nullptr, nullptr, nullptr);

    SetPersistentEditors(QModelIndex());

    // Auto-expand EditorProperties
    QModelIndex Index = mpModel->index(0, 0, QModelIndex());
    IProperty *pProp = mpModel->PropertyForIndex(Index, false);
    if (pProp && pProp->ID() == 0x255A4580)
        expand(Index);
}

void CPropertyView::UpdateEditorProperties(const QModelIndex& rkParent)
{
    // Check what game this is
    EGame Game = gpEdApp->CurrentGame();

    // Iterate over all properties and update if they're an editor property.
    for (int iRow = 0; iRow < mpModel->rowCount(rkParent); iRow++)
    {
        QModelIndex Index0 = mpModel->index(iRow, 0, rkParent);
        QModelIndex Index1 = mpModel->index(iRow, 1, rkParent);
        IProperty *pProp = mpModel->PropertyForIndex(Index0, false);

        if (pProp)
        {
            // For structs, update sub-properties.
            if (pProp->Type() == EPropertyType::Struct)
            {
                CStructProperty *pStruct = TPropCast<CStructProperty>(pProp);

                // As an optimization, in MP2+, we don't need to update unless this is an atomic struct or if
                // it's EditorProperties, because other structs never have editor properties in them.
                // In MP1 this isn't the case so we need to update every struct regardless
                if ((Game <= EGame::Prime) || (pStruct->IsAtomic() || pStruct->ID() == 0x255A4580))
                    UpdateEditorProperties(Index0);
                else
                    continue;
            }

            else if (mpObject && mpObject->IsEditorProperty(pProp))
            {
                mpModel->dataChanged(Index1, Index1);

                if (mpModel->rowCount(Index0) != 0)
                {
                    QModelIndex SubIndexA = mpModel->index(0, 1, Index0);
                    QModelIndex SubIndexB = mpModel->index(mpModel->rowCount(Index0) - 1, 1, Index0);
                    mpModel->dataChanged(SubIndexA, SubIndexB);
                }
            }
        }
    }
}

void CPropertyView::SetPersistentEditors(const QModelIndex& rkParent)
{
    uint32 NumChildren = mpModel->rowCount(rkParent);

    for (uint32 iChild = 0; iChild < NumChildren; iChild++)
    {
        QModelIndex ChildIndex = mpModel->index(iChild, 1, rkParent);
        IProperty *pProp = mpModel->PropertyForIndex(ChildIndex, false);
        EPropertyType Type = (pProp ? pProp->Type() : EPropertyType::Invalid);
        bool IsAnimSet = false;

        // Handle persistent editors under character properties
        if (!pProp && ChildIndex.internalId() & 0x80000000)
        {
            pProp = mpModel->PropertyForIndex(ChildIndex, true);

            if (pProp->Type() == EPropertyType::AnimationSet)
            {
                Type = mpDelegate->DetermineCharacterPropType(pProp->Game(), ChildIndex);
                IsAnimSet = true;
            }

            if (pProp->Type() == EPropertyType::Flags)
                Type = EPropertyType::Bool;
        }


        switch (Type)
        {
        case EPropertyType::Bool:
        case EPropertyType::Color:
        case EPropertyType::Asset:
            openPersistentEditor(ChildIndex);
            break;

        case EPropertyType::Enum:
        case EPropertyType::Choice:
            if (IsAnimSet || TPropCast<CEnumProperty>(pProp)->NumPossibleValues() > 0)
                openPersistentEditor(ChildIndex);
            break;

        case EPropertyType::Struct:
            setFirstColumnSpanned(iChild, rkParent, true);
            break;
        default: break;
        }

        if (isExpanded(ChildIndex))
            SetPersistentEditors(ChildIndex);
    }
}

void CPropertyView::ClosePersistentEditors(const QModelIndex& rkIndex)
{
    uint32 NumChildren = mpModel->rowCount(rkIndex);

    for (uint32 iChild = 0; iChild < NumChildren; iChild++)
    {
        QModelIndex ChildIndex = mpModel->index(iChild, 1, rkIndex);
        closePersistentEditor(ChildIndex);

        if (mpModel->rowCount(ChildIndex) != 0)
            ClosePersistentEditors(ChildIndex);
    }
}

void CPropertyView::OnPropertyModified(const QModelIndex& rkIndex)
{
    // Check for a character resource being changed. If that's the case we need to remake the persistent editors.
    IProperty* pProperty = mpModel->PropertyForIndex(rkIndex, true);

    if (pProperty->Type() == EPropertyType::AnimationSet /*&& rkIndex.internalId() & 0x1*/)
    {
        ClosePersistentEditors(rkIndex);
        SetPersistentEditors(rkIndex);
    }

    scrollTo(rkIndex);
    emit PropertyModified(rkIndex);
    emit PropertyModified(pProperty);
}

void CPropertyView::RefreshView()
{
    SetInstance(mpObject);
}

void CPropertyView::CreateContextMenu(const QPoint& rkPos)
{
    QModelIndex Index = indexAt(rkPos);

    if (Index.isValid() && Index.column() == 0)
    {
        IProperty* pProperty = mpModel->PropertyForIndex(Index, true);
        mpMenuProperty = pProperty;

        QMenu Menu;

        if (!pProperty->IsIntrinsic())
        {
            Menu.addAction(mpEditTemplateAction);
        }

        if (gpEdApp->CurrentGame() >= EGame::EchoesDemo)
        {
            Menu.addAction(mpShowNameValidityAction);
        }

        // Add options for generating property names
        if (pProperty->UsesNameMap())
        {
            Menu.addSeparator();
            Menu.addAction(mpGenNamesForPropertyAction);

            if (!pProperty->IsRootParent())
            {
                QString TypeName = TO_QSTRING( pProperty->Parent()->RootArchetype()->Name() );
                mpGenNamesForSiblingsAction->setText( QString("Generate names for %1 properties").arg(TypeName) );
                Menu.addAction(mpGenNamesForSiblingsAction);
            }

            if (pProperty->Type() == EPropertyType::Struct && !pProperty->IsAtomic())
            {
                QString TypeName = TO_QSTRING( pProperty->RootArchetype()->Name() );
                mpGenNamesForChildrenAction->setText( QString("Generate names for %1 properties").arg(TypeName) );
                Menu.addAction(mpGenNamesForChildrenAction);
            }
        }

        Menu.exec(viewport()->mapToGlobal(rkPos));
    }
}

void CPropertyView::ToggleShowNameValidity(bool ShouldShow)
{
    mpModel->SetShowPropertyNameValidity(ShouldShow);
}

void CPropertyView::EditPropertyTemplate()
{
    QMainWindow* pParentWindow = UICommon::FindAncestor<QMainWindow>(this);
    CTemplateEditDialog Dialog(mpMenuProperty, pParentWindow);
    connect(&Dialog, SIGNAL(PerformedTypeConversion()), this, SLOT(RefreshView()));
    Dialog.exec();
}


void CPropertyView::GenerateNamesForProperty()
{
    CGeneratePropertyNamesDialog* pDialog = gpEdApp->WorldEditor()->NameGeneratorDialog();
    pDialog->AddToIDPool(mpMenuProperty);
    pDialog->show();
}

void CPropertyView::GenerateNamesForSiblings()
{
    CGeneratePropertyNamesDialog* pDialog = gpEdApp->WorldEditor()->NameGeneratorDialog();
    pDialog->AddChildrenToIDPool(mpMenuProperty->Parent(), false);
    pDialog->show();
}

void CPropertyView::GenerateNamesForChildren()
{
    CGeneratePropertyNamesDialog* pDialog = gpEdApp->WorldEditor()->NameGeneratorDialog();
    pDialog->AddChildrenToIDPool(mpMenuProperty, false);
    pDialog->show();
}
