/****************************************************************************/
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0/
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License 2.0 are satisfied: GNU General Public License, version 2
// or later which is available at
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
/****************************************************************************/
/// @file    GNETypeFrame.cpp
/// @author  Pablo Alvarez Lopez
/// @date    Feb 2018
///
// The Widget for edit Type elements (vehicle, person and container)
/****************************************************************************/

#include <netedit/GNEApplicationWindow.h>
#include <netedit/GNENet.h>
#include <netedit/GNETagPropertiesDatabase.h>
#include <netedit/GNEUndoList.h>
#include <netedit/GNEViewParent.h>
#include <netedit/changes/GNEChange_DemandElement.h>
#include <netedit/dialogs/basic/GNEQuestionBasicDialog.h>
#include <netedit/elements/demand/GNEVType.h>
#include <netedit/frames/GNEAttributesEditor.h>
#include <utils/gui/div/GUIDesigns.h>
#include <utils/gui/windows/GUIAppEnum.h>

#include "GNETypeFrame.h"

// ===========================================================================
// FOX callback mapping
// ===========================================================================

FXDEFMAP(GNETypeFrame::TypeSelector) typeSelectorMap[] = {
    FXMAPFUNC(SEL_COMMAND,  MID_GNE_SET_TYPE,   GNETypeFrame::TypeSelector::onCmdSelectItem)
};

FXDEFMAP(GNETypeFrame::TypeEditor) typeEditorMap[] = {
    FXMAPFUNC(SEL_COMMAND,  MID_GNE_CREATE,    GNETypeFrame::TypeEditor::onCmdCreateType),
    FXMAPFUNC(SEL_COMMAND,  MID_GNE_DELETE,    GNETypeFrame::TypeEditor::onCmdDeleteResetType),
    FXMAPFUNC(SEL_COMMAND,  MID_GNE_COPY,      GNETypeFrame::TypeEditor::onCmdCopyType)
};

// Object implementation
FXIMPLEMENT(GNETypeFrame::TypeSelector,         GNEGroupBoxModule,  typeSelectorMap,        ARRAYNUMBER(typeSelectorMap))
FXIMPLEMENT(GNETypeFrame::TypeEditor,           GNEGroupBoxModule,  typeEditorMap,          ARRAYNUMBER(typeEditorMap))

// ===========================================================================
// method definitions
// ===========================================================================

// ---------------------------------------------------------------------------
// GNETypeFrame::TypeSelector - methods
// ---------------------------------------------------------------------------

GNETypeFrame::TypeSelector::TypeSelector(GNETypeFrame* typeFrameParent) :
    GNEGroupBoxModule(typeFrameParent, TL("Current Type")),
    myTypeFrameParent(typeFrameParent),
    myCurrentType(nullptr) {
    // Create MFXComboBoxIcon
    myTypeComboBox = new MFXComboBoxIcon(getCollapsableFrame(), typeFrameParent->getViewNet()->getViewParent()->getGNEAppWindows()->getStaticTooltipMenu(),
                                         true, GUIDesignComboBoxVisibleItems, this, MID_GNE_SET_TYPE, GUIDesignComboBox);
    // add default Types (always first)
    for (const auto& vType : myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_VTYPE)) {
        if (DEFAULT_VTYPES.count(vType.second->getID()) != 0) {
            myTypeComboBox->appendIconItem(vType.second->getID().c_str(), vType.second->getACIcon(), FXRGB(255, 255, 200));
        }
    }
    // fill myTypeMatchBox with list of VTypes IDs
    for (const auto& vType : myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_VTYPE)) {
        if (DEFAULT_VTYPES.count(vType.second->getID()) == 0) {
            myTypeComboBox->appendIconItem(vType.second->getID().c_str(), vType.second->getACIcon());
        }
    }
    // set DEFAULT_VEHTYPE as default VType
    myCurrentType = myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->retrieveDemandElement(SUMO_TAG_VTYPE, DEFAULT_VTYPE_ID);
    myTypeComboBox->setCurrentItem(myTypeComboBox->findItem(DEFAULT_VTYPE_ID.c_str()));
    // TypeSelector is always shown
    show();
}


GNETypeFrame::TypeSelector::~TypeSelector() {}


GNEDemandElement*
GNETypeFrame::TypeSelector::getCurrentType() const {
    return myCurrentType;
}


void
GNETypeFrame::TypeSelector::setCurrentType(GNEDemandElement* vType) {
    myCurrentType = vType;
    refreshTypeSelector(true);
}


void
GNETypeFrame::TypeSelector::refreshTypeSelector(const bool updateModuls) {
    bool valid = false;
    // clear items
    myTypeComboBox->clearItems();
    // add default Vehicle an Bike types in the first and second positions
    for (const auto& vType : myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_VTYPE)) {
        if (DEFAULT_VTYPES.count(vType.second->getID()) != 0) {
            myTypeComboBox->appendIconItem(vType.second->getID().c_str(), vType.second->getACIcon(), FXRGB(255, 255, 200));
        }
    }
    // fill myTypeMatchBox with list of VTypes IDs sorted by ID
    std::map<std::string, GNEDemandElement*> types;
    for (const auto& vType : myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_VTYPE)) {
        if (DEFAULT_VTYPES.count(vType.second->getID()) == 0) {
            types[vType.second->getID()] = vType.second;
        }
    }
    for (const auto& vType : types) {
        myTypeComboBox->appendIconItem(vType.first.c_str(), vType.second->getACIcon());
    }
    // make sure that tag is in myTypeMatchBox
    if (myCurrentType) {
        for (int i = 0; i < (int)myTypeComboBox->getNumItems(); i++) {
            if (myTypeComboBox->getItemText(i) == myCurrentType->getID()) {
                myTypeComboBox->setCurrentItem(i);
                valid = true;
            }
        }
    }
    // Check that give vType type is valid
    if (!valid) {
        // set DEFAULT_VEHTYPE as default VType
        myCurrentType = myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->retrieveDemandElement(SUMO_TAG_VTYPE, DEFAULT_VTYPE_ID);
        // refresh myTypeMatchBox again
        for (int i = 0; i < (int)myTypeComboBox->getNumItems(); i++) {
            if (myTypeComboBox->getItemText(i) == myCurrentType->getID()) {
                myTypeComboBox->setCurrentItem(i);
            }
        }
    }
    // check if update other moduls
    if (updateModuls) {
        // refresh vehicle type editor module
        myTypeFrameParent->myTypeEditor->refreshTypeEditorModule();
        // show modules
        myTypeFrameParent->myTypeAttributesEditor->showAttributesEditor(myCurrentType, true);
    }
}


long
GNETypeFrame::TypeSelector::onCmdSelectItem(FXObject*, FXSelector, void*) {
    // Check if value of myTypeMatchBox correspond of an allowed additional tags
    for (const auto& vType : myTypeFrameParent->getViewNet()->getNet()->getAttributeCarriers()->getDemandElements().at(SUMO_TAG_VTYPE)) {
        if (vType.second->getID() == myTypeComboBox->getText().text()) {
            // set pointer
            myCurrentType = vType.second;
            // set color of myTypeMatchBox to black (valid)
            myTypeComboBox->setTextColor(GUIDesignTextColorBlack);
            // refresh vehicle type editor module
            myTypeFrameParent->myTypeEditor->refreshTypeEditorModule();
            // show modules if selected item is valid
            myTypeFrameParent->myTypeAttributesEditor->showAttributesEditor(myCurrentType, true);
            // update viewNet
            myTypeFrameParent->getViewNet()->updateViewNet();
            return 1;
        }
    }
    myCurrentType = nullptr;
    // refresh vehicle type editor module
    myTypeFrameParent->myTypeEditor->refreshTypeEditorModule();
    // hide all modules if selected item isn't valid
    myTypeFrameParent->myTypeAttributesEditor->hideAttributesEditor();
    // set color of myTypeMatchBox to red (invalid)
    myTypeComboBox->setTextColor(GUIDesignTextColorRed);
    // update viewNet
    myTypeFrameParent->getViewNet()->updateViewNet();
    return 1;
}

// ---------------------------------------------------------------------------
// GNETypeFrame::TypeEditor - methods
// ---------------------------------------------------------------------------

GNETypeFrame::TypeEditor::TypeEditor(GNETypeFrame* typeFrameParent) :
    GNEGroupBoxModule(typeFrameParent, TL("Type Editor")),
    myTypeFrameParent(typeFrameParent) {
    // Create new vehicle type
    myCreateTypeButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Create Type"), "", "", GUIIconSubSys::getIcon(GUIIcon::VTYPE), this, MID_GNE_CREATE, GUIDesignButton);
    // Create delete/reset vehicle type
    myDeleteResetTypeButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Delete Type"), "", "", GUIIconSubSys::getIcon(GUIIcon::MODEDELETE), this, MID_GNE_DELETE, GUIDesignButton);
    // Create copy vehicle type
    myCopyTypeButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Copy Type"), "", "", GUIIconSubSys::getIcon(GUIIcon::COPY), this, MID_GNE_COPY, GUIDesignButton);
}


GNETypeFrame::TypeEditor::~TypeEditor() {}


void
GNETypeFrame::TypeEditor::showTypeEditorModule() {
    refreshTypeEditorModule();
    show();
}


void
GNETypeFrame::TypeEditor::hideTypeEditorModule() {
    hide();
}


void
GNETypeFrame::TypeEditor::refreshTypeEditorModule() {
    // first check if selected VType is valid
    if (myTypeFrameParent->myTypeSelector->getCurrentType() == nullptr) {
        // disable buttons
        myDeleteResetTypeButton->disable();
        myCopyTypeButton->disable();
    } else if (GNEAttributeCarrier::parse<bool>(myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(GNE_ATTR_DEFAULT_VTYPE))) {
        // enable copy button
        myCopyTypeButton->enable();
        // enable and set myDeleteTypeButton as "reset")
        myDeleteResetTypeButton->setText(TL("Reset Type"));
        myDeleteResetTypeButton->setIcon(GUIIconSubSys::getIcon(GUIIcon::RESET));
        // check if reset default vehicle type button has to be enabled or disabled
        if (GNEAttributeCarrier::parse<bool>(myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(GNE_ATTR_DEFAULT_VTYPE_MODIFIED))) {
            myDeleteResetTypeButton->enable();
        } else {
            myDeleteResetTypeButton->disable();
        }
    } else {
        // enable copy button
        myCopyTypeButton->enable();
        // enable and set myDeleteTypeButton as "delete")
        myDeleteResetTypeButton->setText(TL("Delete Type"));
        myDeleteResetTypeButton->setIcon(GUIIconSubSys::getIcon(GUIIcon::MODEDELETE));
        myDeleteResetTypeButton->enable();
    }
    // update module
    recalc();
}


long
GNETypeFrame::TypeEditor::onCmdCreateType(FXObject*, FXSelector, void*) {
    auto net = myTypeFrameParent->myViewNet->getNet();
    // obtain a new valid Type ID
    const std::string typeID = net->getAttributeCarriers()->generateDemandElementID(SUMO_TAG_VTYPE);
    // create new vehicle type
    GNEDemandElement* type = new GNEVType(typeID, net, net->getGNEApplicationWindow()->getFileBucketHandler()->getDefaultBucket(FileBucket::Type::DEMAND));
    // add it using undoList (to allow undo-redo)
    net->getUndoList()->begin(type, TL("create vehicle type"));
    net->getUndoList()->add(new GNEChange_DemandElement(type, true), true);
    net->getUndoList()->end();
    // set created vehicle type in selector
    myTypeFrameParent->myTypeSelector->setCurrentType(type);
    return 1;
}


long
GNETypeFrame::TypeEditor::onCmdDeleteResetType(FXObject*, FXSelector, void*) {
    // continue depending of current mode
    if (myDeleteResetTypeButton->getIcon() == GUIIconSubSys::getIcon(GUIIcon::MODEDELETE)) {
        deleteType();
    } else {
        resetType();
    }
    return 1;
}


long
GNETypeFrame::TypeEditor::onCmdCopyType(FXObject*, FXSelector, void*) {
    // obtain a new valid Type ID
    const std::string typeID = myTypeFrameParent->myViewNet->getNet()->getAttributeCarriers()->generateDemandElementID(SUMO_TAG_VTYPE);
    // obtain vehicle type in which new Type will be based
    GNEVType* vType = dynamic_cast<GNEVType*>(myTypeFrameParent->myTypeSelector->getCurrentType());
    // check that vType exist
    if (vType) {
        // create a new Type based on the current selected vehicle type
        GNEDemandElement* typeCopy = new GNEVType(typeID, myTypeFrameParent->myViewNet->getNet(), vType);
        // begin undo list operation
        myTypeFrameParent->myViewNet->getUndoList()->begin(typeCopy, TL("copy vehicle type"));
        // add it using undoList (to allow undo-redo)
        myTypeFrameParent->myViewNet->getUndoList()->add(new GNEChange_DemandElement(typeCopy, true), true);
        // end undo list operation
        myTypeFrameParent->myViewNet->getUndoList()->end();
        // set created vehicle type in selector
        myTypeFrameParent->myTypeSelector->setCurrentType(typeCopy);
    }
    return 1;
}


void
GNETypeFrame::TypeEditor::resetType() {
    // begin reset default vehicle type values
    myTypeFrameParent->getViewNet()->getUndoList()->begin(GUIIcon::VTYPE, TL("reset default vehicle type values"));
    // reset all values of default vehicle type
    for (const auto& attrProperty : myTypeFrameParent->getViewNet()->getNet()->getACTemplates()->getTemplateAC(SUMO_TAG_VTYPE)->getTagProperty()->getAttributeProperties()) {
        // change all attributes with "" to reset it (except ID and vClass)
        if ((attrProperty->getAttr() != SUMO_ATTR_ID) && (attrProperty->getAttr() != SUMO_ATTR_VCLASS)) {
            myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(attrProperty->getAttr(), "", myTypeFrameParent->myViewNet->getUndoList());
        }
    }
    // change manually VClass (because it depends of Default VType)
    if (myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(SUMO_ATTR_ID) == DEFAULT_VTYPE_ID) {
        myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(SUMO_ATTR_VCLASS, toString(SVC_PASSENGER), myTypeFrameParent->myViewNet->getUndoList());
    } else if (myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(SUMO_ATTR_ID) == DEFAULT_BIKETYPE_ID) {
        myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(SUMO_ATTR_VCLASS, toString(SVC_BICYCLE), myTypeFrameParent->myViewNet->getUndoList());
    } else if (myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(SUMO_ATTR_ID) == DEFAULT_TAXITYPE_ID) {
        myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(SUMO_ATTR_VCLASS, toString(SVC_TAXI), myTypeFrameParent->myViewNet->getUndoList());
    } else if (myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(SUMO_ATTR_ID) == DEFAULT_RAILTYPE_ID) {
        myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(SUMO_ATTR_VCLASS, toString(SVC_RAIL), myTypeFrameParent->myViewNet->getUndoList());
    } else if (myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(SUMO_ATTR_ID) == DEFAULT_PEDTYPE_ID) {
        myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(SUMO_ATTR_VCLASS, toString(SVC_PEDESTRIAN), myTypeFrameParent->myViewNet->getUndoList());
    } else if (myTypeFrameParent->myTypeSelector->getCurrentType()->getAttribute(SUMO_ATTR_ID) == DEFAULT_CONTAINERTYPE_ID) {
        myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(SUMO_ATTR_VCLASS, toString(SVC_PEDESTRIAN), myTypeFrameParent->myViewNet->getUndoList());
    }
    // change special attribute GNE_ATTR_DEFAULT_VTYPE_MODIFIED
    myTypeFrameParent->myTypeSelector->getCurrentType()->setAttribute(GNE_ATTR_DEFAULT_VTYPE_MODIFIED, "false", myTypeFrameParent->myViewNet->getUndoList());
    // finish reset default vehicle type values
    myTypeFrameParent->getViewNet()->getUndoList()->end();
    // refresh TypeSelector
    myTypeFrameParent->myTypeSelector->refreshTypeSelector(true);
}


void
GNETypeFrame::TypeEditor::deleteType() {
    // show question dialog if vtype has already assigned vehicles
    if (myTypeFrameParent->myTypeSelector->getCurrentType()->getChildDemandElements().size() > 0) {
        // declare title and info
        std::string title = TLF("remove % '%'", toString(SUMO_TAG_VTYPE), myTypeFrameParent->myTypeSelector->getCurrentType()->getID());
        std::string info;
        const std::string numChildren = toString(myTypeFrameParent->myTypeSelector->getCurrentType()->getChildDemandElements().size());
        // continue depending of plural
        if (myTypeFrameParent->myTypeSelector->getCurrentType()->getChildDemandElements().size() == 1) {
            info = TLF("Delete % '%' will remove one vehicle. Continue?", toString(SUMO_TAG_VTYPE), myTypeFrameParent->myTypeSelector->getCurrentType()->getID());
        } else {
            info = TLF("Delete % '%' will remove % vehicles. Continue?", toString(SUMO_TAG_VTYPE), myTypeFrameParent->myTypeSelector->getCurrentType()->getID(), numChildren);
        }
        // Ask confirmation to user
        const GNEQuestionBasicDialog questionDialog(myTypeFrameParent->getViewNet()->getViewParent()->getGNEAppWindows(),
                GNEDialog::Buttons::YES_NO, title, info);
        // continue depending of answer
        if (questionDialog.getResult() == GNEDialog::Result::ACCEPT) {
            // begin undo list operation
            myTypeFrameParent->myViewNet->getUndoList()->begin(myTypeFrameParent->myTypeSelector->getCurrentType(), ("delete vehicle type"));
            // remove vehicle type (and all of their children)
            myTypeFrameParent->myViewNet->getNet()->deleteDemandElement(myTypeFrameParent->myTypeSelector->getCurrentType(),
                    myTypeFrameParent->myViewNet->getUndoList());
            // end undo list operation
            myTypeFrameParent->myViewNet->getUndoList()->end();
        }
    } else {
        // begin undo list operation
        myTypeFrameParent->myViewNet->getUndoList()->begin(myTypeFrameParent->myTypeSelector->getCurrentType(), ("delete vehicle type"));
        // remove vehicle type (and all of their children)
        myTypeFrameParent->myViewNet->getNet()->deleteDemandElement(myTypeFrameParent->myTypeSelector->getCurrentType(),
                myTypeFrameParent->myViewNet->getUndoList());
        // end undo list operation
        myTypeFrameParent->myViewNet->getUndoList()->end();
    }
}

// ---------------------------------------------------------------------------
// GNETypeFrame - methods
// ---------------------------------------------------------------------------

GNETypeFrame::GNETypeFrame(GNEViewParent* viewParent, GNEViewNet* viewNet) :
    GNEFrame(viewParent, viewNet, TL("Types")) {

    // create module for edit vehicle types (Create, copy, etc.)
    myTypeEditor = new TypeEditor(this);

    // create vehicle type selector
    myTypeSelector = new TypeSelector(this);

    // Create vehicle type attributes editor
    myTypeAttributesEditor = new GNEAttributesEditor(this, GNEAttributesEditorType::EditorType::EDITOR);

    // set "VTYPE_DEFAULT" as default vehicle Type
    myTypeSelector->setCurrentType(myViewNet->getNet()->getAttributeCarriers()->retrieveDemandElement(SUMO_TAG_VTYPE, DEFAULT_VTYPE_ID));
}


GNETypeFrame::~GNETypeFrame() {}


void
GNETypeFrame::show() {
    // refresh vehicle type and Attribute Editor
    myTypeSelector->refreshTypeSelector(true);
    // show modules
    myTypeAttributesEditor->showAttributesEditor(myTypeSelector->getCurrentType(), true);
    // show frame
    GNEFrame::show();
}


GNETypeFrame::TypeSelector*
GNETypeFrame::getTypeSelector() const {
    return myTypeSelector;
}


void
GNETypeFrame::attributeUpdated(SumoXMLAttr /*attribute*/) {
    // after changing an attribute myTypeSelector, we need to update the list of typeSelector, because ID could be changed
    myTypeSelector->refreshTypeSelector(false);
    //... and typeEditor (due reset)
    myTypeEditor->refreshTypeEditorModule();
}

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