//============================================================================
// Copyright 2009-2020 ECMWF.
// This software is licensed under the terms of the Apache Licence version 2.0
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
// In applying this licence, ECMWF does not waive the privileges and immunities
// granted to it by virtue of its status as an intergovernmental organisation
// nor does it submit to any jurisdiction.
//
//============================================================================

#include "AttributeEditor.hpp"

#include "AttributeEditorFactory.hpp"
#include "ConnectState.hpp"
#include "VAttribute.hpp"
#include "VAttributeType.hpp"
#include "ServerHandler.hpp"
#include "UiLogS.hpp"
#include "VConfig.hpp"
#include "VRepeatAttr.hpp"

#include <QPushButton>

#define _USE_MODELESS_ATTRIBUTEDITOR
#define _UI_ATTRIBUTEDITOR_DEBUG

#ifdef _USE_MODELESS_ATTRIBUTEDITOR
static QList<AttributeEditor*> editors;
#endif

AttributeEditor::AttributeEditor(VInfo_ptr info,QString type,QWidget* parent) : QDialog(parent), info_(info), form_(nullptr), type_(type)
{    
    setupUi(this);
    setAttribute(Qt::WA_DeleteOnClose);

#ifdef _USE_MODELESS_ATTRIBUTEDITOR
    setModal(false);
#endif

    Q_ASSERT(info_ && info_->isAttribute() && info_->attribute());
    messageLabel_->hide();
    
    attrData_=info_->attribute()->data();

    QString wt="Edit " + type;
    wt+="  -  " + QString::fromStdString(VConfig::instance()->appLongName());
    setWindowTitle(wt);

    attachInfo();

    connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)),
            this,SLOT(slotButton(QAbstractButton*)));
}

AttributeEditor::~AttributeEditor()
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG
#endif
    detachInfo();
#ifdef _USE_MODELESS_ATTRIBUTEDITOR
    editors.removeOne(this);
#endif
}

void AttributeEditor::edit(VInfo_ptr info,QWidget *parent)
{
    Q_ASSERT(info && info->isAttribute() && info->attribute());
    VAttribute* a=info->attribute();
    Q_ASSERT(a->type());

#ifdef _USE_MODELESS_ATTRIBUTEDITOR
    Q_FOREACH(AttributeEditor* e,editors)
    {
        if((e->info_ && info) &&
           *(e->info_.get()) == *(info.get()))
        {
            e->raise();
            return;
        }
    }
#endif

    //For repeats we create an editor for each type
    std::string typeStr=a->type()->strName();
    if(a->type() == VAttributeType::find("repeat"))
    {        
        typeStr+="_" + a->subType();
    }

    //The edtior will be automatically deleted on close (Qt::WA_DeleteOnClose is set)
    if(AttributeEditor* e=AttributeEditorFactory::create(typeStr,info,nullptr))
    {
#ifdef _USE_MODELESS_ATTRIBUTEDITOR
        editors << e;
        e->show();
#else
        e->exec();
#endif
    }
}

void AttributeEditor::addForm(QWidget* w)
{
    form_=w;
    mainLayout_->insertWidget(3,w,1);
}

void AttributeEditor::accept()
{
    apply();
    QDialog::accept();
}

void AttributeEditor::attachInfo()
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG
#endif

    if(info_)
    {
        if(info_->server())
        {
            info_->server()->addNodeObserver(this);
            info_->server()->addServerObserver(this);
        }

        info_->addObserver(this);
    } 
}

void AttributeEditor::detachInfo()
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG
#endif
    if(info_)
    {
        if(info_->server())
        {
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
            UiLog().dbg() << " remove NodeObserver " << this;
#endif
            info_->server()->removeNodeObserver(this);
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
            UiLog().dbg() << " remove ServerObserver " << this;
#endif
            info_->server()->removeServerObserver(this);
        }
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
        UiLog().dbg() << " remove InfoObserver";
#endif
        info_->removeObserver(this);
    }

    messageLabel_->stopLoadLabel();
}

void AttributeEditor::slotButton(QAbstractButton* b)
{
    if(b && buttonBox_->buttonRole(b) == QDialogButtonBox::ResetRole)
        resetValue();
}

void AttributeEditor::checkButtonStatus()
{
    setResetStatus(isValueChanged());
    setSaveStatus(isValueChanged());
}

void AttributeEditor::setResetStatus(bool st)
{
    QPushButton *resetpb=buttonBox_->button(QDialogButtonBox::Reset);
    Q_ASSERT(resetpb);
    resetpb->setEnabled(st);
}

void AttributeEditor::setSaveStatus(bool st)
{
    QPushButton *savepb=buttonBox_->button(QDialogButtonBox::Save);
    Q_ASSERT(savepb);
    savepb->setEnabled(st);
}

void AttributeEditor::setSuspended(bool st)
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG
    UiLog().dbg() << " status=" << st;
#endif

    Q_ASSERT(form_);
    form_->setEnabled(!st);

    QPushButton *savePb=buttonBox_->button(QDialogButtonBox::Save);
    Q_ASSERT(savePb);
    savePb->setEnabled(!st);

    QPushButton *resetPb=buttonBox_->button(QDialogButtonBox::Reset);
    Q_ASSERT(resetPb);
    resetPb->setEnabled(!st);
}

void AttributeEditor::notifyDataLost(VInfo* info)
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG
#endif
    if(info_ && info_.get() == info)
    {
        detachInfo();
        messageLabel_->showWarning("The parent node or the edited " + type_ + " <b>is not available</b> anymore! Please close the dialog!");
        setSuspended(true);
    }
}

void AttributeEditor::notifyBeginNodeChange(const VNode* vn, const std::vector<ecf::Aspect::Type>& aspect,const VNodeChange&)
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG
#endif
    if(info_ && info_->node() && info_->node() == vn)
    {
        bool attrNumCh=(std::find(aspect.begin(),aspect.end(),ecf::Aspect::ADD_REMOVE_ATTR) != aspect.end());
        if(attrNumCh)
        {
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
            UiLog().dbg() << " ADD_REMOVE_ATTR";
#endif
            //try to regain the data
            info_->regainData();

            //If the attribute is not available dataLost() was already called.
            if(!info_ || !info_->hasData())
            {
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
                UiLog().dbg() << " attribute does not exist";
#endif
                return;
            }

            Q_ASSERT(info_->server() && info_->node());
            setSuspended(false);

#if 0
            if(1)
            {
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
                UiLog().dbg() << " attribute does not exist";
#endif
                detachInfo();
                messageLabel_->showWarning("The edited " + type_ +
                                           " <b>is not available</b> any more! Please close the dialog!");
                setSuspended(true);
            }
#endif
        }
        else
        {          
            nodeChanged(aspect);
        }
    }
}

void AttributeEditor::notifyDefsChanged(ServerHandler* server, const std::vector<ecf::Aspect::Type>& a)
{
    if(info_ && info_->server() && info_->server() == server)
    {
        //TODO: implement

    }
}

void AttributeEditor::notifyServerDelete(ServerHandler* server)
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG_S(server)
#endif
    if(info_ && info_->server() == server)
    {
        detachInfo();
        messageLabel_->showWarning("Server <b>" + QString::fromStdString(server->name()) +
                                   "</b> was removed from ecFlowUI! The edited " + type_ +
                                   " <b>is not available</b> anymore! Please close the dialog!");
        setSuspended(true);
    }
}
    
//This must be called at the beginning of a reset
void AttributeEditor::notifyBeginServerClear(ServerHandler* server)
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG_S(server)
#endif

    if(info_)
    {
        if(info_->server() && info_->server() == server)
        {
            messageLabel_->showWarning("Server <b>" + QString::fromStdString(server->name()) + "</b> is being reloaded. \
                   Until it is finished this " + type_ + " <b>cannot be modified</b>!");

            messageLabel_->startLoadLabel();

            setSuspended(true);
            checkButtonStatus();
        }
    }
}

//This must be called at the end of a reset
void AttributeEditor::notifyEndServerScan(ServerHandler* server)
{
#ifdef _UI_ATTRIBUTEDITOR_DEBUG
    UI_FUNCTION_LOG_S(server)
#endif

    if(info_)
    {
        if(info_->server() && info_->server() == server)
        {
            messageLabel_->hide();
            messageLabel_->clear();

            //We try to ressurect the info. We have to do it explicitly because it is not guaranteed
            //that notifyEndServerScan() will be first called on other objects than AttributeEditor. So it
            //is possible that the node exists but is still set to NULL in VInfo.
            info_->regainData();

            //If the node is not available dataLost() was already called.
            if(!info_ || !info_->hasData())
                return;

            Q_ASSERT(info_->server() && info_->node());

            setSuspended(false);
        }
    }
}

void AttributeEditor::notifyServerConnectState(ServerHandler* server)
{
    Q_ASSERT(server);
    if(info_->server() && info_->server() == server)
    {
        switch(server->connectState()->state())
        {
        case ConnectState::Lost:
            messageLabel_->showWarning("Connection lost to server <b>" + QString::fromStdString(server->name()) + "</b>. \
                   Until it the connection regained this " + type_ + " <b>cannot be modified</b>!");
            messageLabel_->stopLoadLabel();
            setSuspended(true);
            break;
        case ConnectState::Normal:
            messageLabel_->hide();
            messageLabel_->clear();
            messageLabel_->stopLoadLabel();
            setSuspended(false);
            break;
        default:
            break;
        }
    }
}

void AttributeEditor::doNotUseReset()
{
    if(QPushButton *resetPb=buttonBox_->button(QDialogButtonBox::Reset))
    {
        resetPb->setEnabled(false);
        resetPb->hide();
    }
}

void AttributeEditor::disableCancel()
{
    if(QPushButton *cancelPb=buttonBox_->button(QDialogButtonBox::Cancel))
        cancelPb->hide();
}
