// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/track_ctrl/drot/Interface/Sharknir/Qt/Drot/SharknirDrotServiceWidget.cc 3113 2020-05-28 14:09:02Z depa $

// Original from ln unstable

#include <Basda/Mocca/Constant.h>
#include <Nice/Map/Point.h>
#include <Nice/Property/BridgeMemFn.hh>
#include <Nice/Qt/WidgetFactory.hh>
#include <Nice/Qt/QtWidgetAttributeName.h>
#include <Sharknir/Qt/Drot/SharknirDrotServiceWidget.h>
#include <Sharknir/Qt/Drot/SharknirDrotServiceWidgetUI.h>
#include <Qt/qglobal.h>
#include <QTextEdit>
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
#include <Basda/Mocca/PropertyName.h>

//extern "C" void instantType()
//{
//    D_LOG("Load Basda::MoccaServiceWidgetClassic");
//}


namespace bms = Basda::Mocca::Svc;

extern int qInitResources_Basda_Qt_NavigationCrossResource();

SharknirDrotServiceWidget::SharknirDrotServiceWidget(QWidget * parent,
                                                     bool /*_connect*/) :
    QWidget(parent), Nice::BridgeItem()
{
    qInitResources_Basda_Qt_NavigationCrossResource();
    Nice::BridgeItem::m_autoSynch = true;

    m_drotView = new Ui::SharknirDrotServiceWidgetUI;
    m_drotView->setupUi(this);

    m_notDefined = "not defined";
    m_unit = m_notDefined;
    m_namedPos = 0;
    m_namedPositionNotDefined = "named position n.d.";
    init(this);
}

void SharknirDrotServiceWidget::initSlots()
{
    m_drotView->m_basdaServiceState->Nice::BridgeItem::connect(
        m_root->node("STATE"));
    m_drotView->m_reachableLed->Nice::BridgeItem::connect(
        m_root->leaf("CMD")->attribute(
            Basda::Mocca::Svc::AttributeName::isReachable()));
    m_drotView->m_nameItemLabel->Nice::BridgeItem::connect(
        m_root->node(Basda::Mocca::Svc::PropertyName::serviceName()));
    m_drotView->m_paAngle->Nice::BridgeItem::connect(m_root->node("VAR.PANG_STRT_ANG"));
    m_drotView->m_iifLed->Nice::BridgeItem::connect(m_root->node("VAR.IIF"));

    std::string unit = m_root->leaf(
        // Basda::Mocca::Svc::PropertyName::position())
        Basda::Mocca::Svc::PropertyName::absoluteEncoderPosition())
                       ->getAttribute<std::string>(
        Basda::Mocca::Svc::AttributeName::unit());

    m_drotView->m_positionItemLabel->setToolTip(
        QString::fromStdString(
            // Basda::Mocca::Svc::PropertyName::position() + " in ["
            Basda::Mocca::Svc::PropertyName::absoluteEncoderPosition() + " in ["
            + unit + "]"));
    m_drotView->m_positionComboBox->addItem( QString::fromStdString("POSITION"));

    Nice::JointPtrMap children = m_root->node(
        Basda::Mocca::Svc::PropertyName::position())->children();
    std::map<std::string, Nice::JointPtr>::iterator it = children.begin();
    while (it != children.end())
    {
        Nice::JointPtrMap grandchild = it->second->children();
        if (it->second->hasLeaf())
            addPositionItemLabel(it->first);
        std::map<std::string, Nice::JointPtr>::iterator jt = grandchild.begin();
        while (jt != grandchild.end())
        {
            addPositionItemLabel(it->first + "." + jt->first);
            jt++;
        }
        it++;
    }

    Nice::PropertyPtr ptr = m_root->leaf(bms::PropertyName::position())->attribute("DEFAULT");
    if(ptr) { m_drotView->m_positionComboBox->setCurrentIndex(ptr->get<Nice::Int>()); }

    m_root->setAtNode<Nice::Time>("CMD.STARTDEROTATION.DELAY", Nice::Time::seconds(20));
    m_root->setAtNode<Nice::Time>("CMD.CHANGEDEROTATION.DELAY", Nice::Time::seconds(20));

    m_drotView->m_deviceNameItemLabel->Nice::BridgeItem::connect(
        m_root->leaf("CMD")->attribute(
            Basda::Mocca::Svc::AttributeName::deviceName()));
    m_drotView->m_namedPositionComboBox->Nice::BridgeItem::connect(
        m_root->node("CMD.MOVETONAMEDPOSITION.NAMED_POSITION"));
    m_drotView->m_startDerotationInTimeEdit->Nice::BridgeItem::connect(
        m_root->node("CMD.STARTDEROTATION.DELAY"));
    m_drotView->m_changeDerotationInTimeEdit->Nice::BridgeItem::connect(
        m_root->node("CMD.CHANGEDEROTATION.DELAY"));

    m_drotView->m_progressBar->Nice::BridgeItem::connect(m_root->node("CMD"));
    m_drotView->m_statusLabel->Nice::BridgeItem::connect(m_root->node("CMD"));

    // in order to keep the GUI updated when the property change
    NICE_QT_CONNECT(
        m_root->leaf("CMD")->attribute(Basda::Mocca::Svc::AttributeName::namedPositionAbs()),
        this, SharknirDrotServiceWidget, slotNice_setNamedPositionAbs,
        std::vector<double>);
    NICE_QT_CONNECT(
        m_root->leaf("CMD")->attribute(Basda::Mocca::Svc::AttributeName::namedPositionRel()),
        this, SharknirDrotServiceWidget, slotNice_setNamedPositionRel,
        std::vector<double>);
}

void SharknirDrotServiceWidget::addPositionItemLabel(std::string _name)
{
    QFont font;
    font.setPointSize(14);
    font.setBold(true);
    font.setWeight(75);
    QWidget *page = new QWidget;
    page->setObjectName(QString::fromStdString(_name));
    QHBoxLayout *horizontalLayout = new QHBoxLayout(page);
    horizontalLayout->setSpacing(0);
    horizontalLayout->setContentsMargins(0, 0, 0, 0);
    ItemLabel *itemLabel = new ItemLabel(page);
    itemLabel->setFont(font);
    itemLabel->Nice::BridgeItem::connect(
        m_root->node(
            Basda::Mocca::Svc::PropertyName::position() + "." + _name));
    std::string unit = m_root->leaf(
        Basda::Mocca::Svc::PropertyName::position() + "." + _name)
                       ->getAttribute<std::string>(
        Basda::Mocca::Svc::AttributeName::unit());
    itemLabel->setToolTip(
        QString::fromStdString(
            Basda::Mocca::Svc::PropertyName::position() + "." + _name
            + " in [" + unit + "]"));
    horizontalLayout->addWidget(itemLabel);
    m_drotView->m_positionComboBox->addItem(QString::fromStdString(_name));
    m_drotView->m_stackedWidget->addWidget(page);
}

void SharknirDrotServiceWidget::initAttributes()
{
    m_cmd = Sharknir::DrotServiceWorkerInterface::create(getNode());

    m_unitPrec = m_root->leaf(
        Basda::Mocca::Svc::PropertyName::position())->attribute(
        Nice::QtWidgetAttributeName::unitPrec())
                 ->get<std::map<std::string, int> >();
    std::map<std::string, int>::iterator unitNamePrecIt = m_unitPrec.begin();
    m_drotView->m_unitComboBox->blockSignals(true);
    while (unitNamePrecIt != m_unitPrec.end())
    {
        m_drotView->m_unitComboBox->addItem(QString::fromStdString(unitNamePrecIt->first));
        unitNamePrecIt++;
    }
    m_drotView->m_unitComboBox->blockSignals(false);

    std::string unit = m_root->leaf(
        Basda::Mocca::Svc::PropertyName::position())
                       ->getAttribute<std::string>(
        Basda::Mocca::Svc::AttributeName::unit());
    I_LOG(unit)
    m_drotView->m_unitComboBox->setCurrentIndex(m_drotView->m_unitComboBox->findText(QString::fromStdString(unit)));
    slotQt_valueUnitChanged(QString::fromStdString(unit));

    m_drotView->m_positionItemLabel->Nice::BridgeItem::connect(
        // m_root->node(Basda::Mocca::Svc::PropertyName::position()));
        m_root->node(Basda::Mocca::Svc::PropertyName::absoluteEncoderPosition()));

    m_namedPos = m_root->node("CMD.MOVETONAMEDPOSITION.NAMED_POSITION")
                 ->get<int>();
    // save the named position in a vector
    // in case the attributes does not exist, the default value will be return
    std::vector<double> ref(1, 0);
    m_namedPosRel = m_root->leaf("CMD")
                    ->getAttributeWithDefault<std::vector<double> >(
        Basda::Mocca::Svc::AttributeName::namedPositionRel(), ref);
    m_namedPosAbs = m_root->leaf("CMD")
                    ->getAttributeWithDefault<std::vector<double> >(
        Basda::Mocca::Svc::AttributeName::namedPositionAbs(), ref);

    if (m_namedPosRel.empty()) m_namedPosRel.resize(1);
    if (m_namedPosAbs.empty()) m_namedPosAbs.resize(1);

    m_drotView->m_namedPositionComboBox->setToolTip(
        "Select pre-defined named position.\nSelecting here will not initiate the move,\nbut will change button behavior.");

    m_drotView->m_unitComboBox->setToolTip(
        "Select pre-defined unit transformation matrix.");
    m_drotView->m_moveHomeToolButton->setToolTip("Home the motor");
    m_drotView->m_moveAbsoluteToolButton->setToolTip(
        "Move to location indicated in spinbox above");
    m_drotView->m_moveRelativeToolButton->setToolTip(
        "Move in increments as specified in the spinbox above.");

    updateDoubleSpinBoxRel();
    updateDoubleSpinBoxAbs();
}

void SharknirDrotServiceWidget::updateDoubleSpinBoxAbs()
{
    // restore the named position values
    // if no named position is defined set the values with 0
    if (m_namedPos >= 0 && m_namedPos < (int) m_namedPosAbs.size())
    {
        m_drotView->m_moveAbsoluteDoubleSpinBox->setValue(
            m_cmd->convertPosition(
                m_namedPosAbs[m_namedPos],
                true, "STEPS", m_unit));
    }
    else
    {
        m_namedPosAbs.push_back(m_drotView->m_moveAbsoluteDoubleSpinBox->value());
        m_root->leaf("CMD")->attribute(
            Basda::Mocca::Svc::AttributeName::namedPositionAbs())
        ->set<std::vector<double> >(m_namedPosAbs);
    }
}

void SharknirDrotServiceWidget::updateDoubleSpinBoxRel()
{
    // restore the named position values
    // if no named position is defined set the values with 0
    if (m_namedPos >= 0 && m_namedPos < (int) m_namedPosRel.size())
    {
        m_drotView->m_moveRelativeDoubleSpinBox->setValue(
            m_cmd->convertPosition(
                m_namedPosRel[m_namedPos],
                false, "STEPS", m_unit));
    }
    else
    {
        m_namedPosRel.push_back(m_drotView->m_moveRelativeDoubleSpinBox->value());
        m_root->leaf("CMD")->attribute(
            Basda::Mocca::Svc::AttributeName::namedPositionRel())
        ->set<std::vector<double> >(m_namedPosRel);
    }
}

void SharknirDrotServiceWidget::slotNice_setNamedPositionAbs(std::vector<
                                                                 double> _p)
{
    if (m_namedPosAbs != _p)
    {
        m_namedPosAbs = _p;
    }
}

void SharknirDrotServiceWidget::slotNice_setNamedPositionRel(std::vector<
                                                                 double> _p)
{
    if (m_namedPosRel != _p)
    {
        m_namedPosRel = _p;
    }
}

void SharknirDrotServiceWidget::slotQt_valueRelChanged(double _v)
{
    // we don't save the edited named position any more
//    m_namedPosRel[m_namedPos] = _v;
}

void SharknirDrotServiceWidget::slotQt_valueAbsChanged(double _v)
{
    // we don't save the edited named position any more
//    m_namedPosAbs[m_namedPos] = _v;
}

void SharknirDrotServiceWidget::slotQt_signRelChanged()
{
    double pos = m_drotView->m_moveRelativeDoubleSpinBox->value();
    m_drotView->m_moveRelativeDoubleSpinBox->setValue(pos * -1.0);
}

void SharknirDrotServiceWidget::slotQt_signAbsChanged()
{
    double pos = m_drotView->m_moveAbsoluteDoubleSpinBox->value();
    m_drotView->m_moveAbsoluteDoubleSpinBox->setValue(pos * -1.0);
}

void SharknirDrotServiceWidget::slotQt_valueUnitChanged(QString _v)
{
    if (_v.toStdString() != m_unit)
    {
        std::string unitFrom = m_unit;
        std::string unitTo = _v.toStdString();
        m_unit = _v.toStdString();
        try
        {
            m_drotView->m_moveAbsoluteDoubleSpinBox->setValue(
                m_cmd->convertPosition(
                    m_drotView->m_moveAbsoluteDoubleSpinBox->value(),
                    true, unitFrom, unitTo));
            m_drotView->m_moveRelativeDoubleSpinBox->setValue(
                m_cmd->convertPosition(
                    m_drotView->m_moveRelativeDoubleSpinBox->value(),
                    false, unitFrom, unitTo));
        }

        catch (Sharknir::DrotUnitNotKnownException &e)
        {
            // pass
        }

        catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
        {
            E_LOG(e)
        }

        catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
        {
            E_LOG(e)
        }

        catch (const std::exception& ex)
        {

            E_LOG(ex.what());
            I_LOG("unitFrom " << unitFrom);
            I_LOG("unitTo " << unitTo);
            assert(false);
        }
        m_drotView->m_moveAbsoluteDoubleSpinBox->setDecimals(m_unitPrec[m_unit]);
        m_drotView->m_moveRelativeDoubleSpinBox->setDecimals(m_unitPrec[m_unit]);
    }
}

void SharknirDrotServiceWidget::slotQt_valueNamedPositionChanged(int _index)
{
    if (m_drotView->m_namedPositionComboBox->count() > 1)
    {
        int index = m_drotView->m_namedPositionComboBox->findText(
            QString::fromStdString(m_namedPositionNotDefined));
        if (index > -1)
        {
            m_namedPosAbs.erase(m_namedPosAbs.begin() + index);
            m_namedPosRel.erase(m_namedPosRel.begin() + index);
            m_drotView->m_namedPositionComboBox->removeItem(index);
            if (_index > 0) _index -= 1;
        }
    }
    m_namedPos = _index;
    updateDoubleSpinBoxRel();
    updateDoubleSpinBoxAbs();
}

void SharknirDrotServiceWidget::slotQt_abort()
{
    try
    {
        m_cmd->abort();
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_stop()
{
    try
    {
        m_cmd->stop();
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_moveAbsolute()
{
    try
    {
        m_cmd->moveAbsoluteStart(
            m_drotView->m_moveAbsoluteDoubleSpinBox->value(), m_unit);
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_moveRelative()
{
    try
    {
        m_cmd->moveRelativeStart(
            m_drotView->m_moveRelativeDoubleSpinBox->value(), m_unit);
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_moveToHome()
{
    try
    {
        m_cmd->moveToHomeStart();
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_startDerotation()
{
    try
    {
        U3_LOG(m_root->getAtNode<Nice::Time>("CMD.STARTDEROTATION.DELAY"))
        U3_LOG(Nice::Date::now())
        Nice::Date date = Nice::Date::now() + m_root->getAtNode<Nice::Time>("CMD.STARTDEROTATION.DELAY");
        U3_LOG(date)
        m_cmd->startDerotationStart(date, m_drotView->m_moveAbsoluteDoubleSpinBox->value(), m_unit);
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceInterfaceUnsynchronizedException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (Nice::Exception &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_changeDerotationWithAbsoluteOffset()
{
    try
    {
        U3_LOG(m_root->getAtNode<Nice::Time>("CMD.CHANGEDEROTATION.DELAY"))
        U3_LOG(Nice::Date::now())
        Nice::Date date = Nice::Date::now() + m_root->getAtNode<Nice::Time>("CMD.CHANGEDEROTATION.DELAY");
        U3_LOG(date)
        m_cmd->changeDerotationWithAbsoluteOffset(date, m_drotView->m_moveAbsoluteDoubleSpinBox->value(), m_unit);
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceInterfaceUnsynchronizedException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (Nice::Exception &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

void SharknirDrotServiceWidget::slotQt_changeDerotationWithRelativeOffset()
{
    try
    {
        U3_LOG(m_root->getAtNode<Nice::Time>("CMD.CHANGEDEROTATION.DELAY"))
        U3_LOG(Nice::Date::now())
        Nice::Date date = Nice::Date::now() + m_root->getAtNode<Nice::Time>("CMD.CHANGEDEROTATION.DELAY");
        U3_LOG(date)
        m_cmd->changeDerotationWithRelativeOffset(date, m_drotView->m_moveRelativeDoubleSpinBox->value(), m_unit);
    }

    catch (Basda::ServiceWorkerInterfaceIsBusyException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceInterfaceUnsynchronizedException &e)
    {
        E_LOG(e)
    }

    catch (Basda::ServiceWorkerInterfaceCommandFailedException &e)
    {
        E_LOG(e)
    }

    catch (Nice::Exception &e)
    {
        E_LOG(e)
    }

    catch (const std::exception& ex)
    {
        E_LOG(ex.what());
        assert(false);
    }
}

struct WidgetFactoryTypeMapInitializerSharknirDrotServiceWidgetClassic
{
    WidgetFactoryTypeMapInitializerSharknirDrotServiceWidgetClassic()
    {
        NicePlugins::addPlugin(
            new QTPluginImpl<SharknirDrotServiceWidget>(
                "SharknirDrotServiceWidget.h",
                "SharknirDrotServiceWidget"));
        Nice::WidgetFactory::addWidgetFactory<
            SharknirDrotServiceWidget>(
            "SharknirDrotServiceWidget",
            "SharknirDrotServiceWidget");
    }
    ;
} s_WidgetFactoryTypeMapInitializerSharknirDrotServiceWidget;
