// $Id: constraintmenus.cpp,v 1.13 2018/01/03 21:33:00 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


#include <cassert>
#include <string>

#include "display.h"
#include "lamarc_strings.h"
#include "newmenuitems.h"
#include "constraintmenus.h"
#include "togglemenuitem.h"
#include "ui_interface.h"
#include "ui_strings.h"

using std::string;

//------------------------------------------------------------------------------------

ConstraintMenuForOneForce::ConstraintMenuForOneForce(UIInterface& ui,UIId id)
    : NewMenu(ui,lamarcmenu::forceConstraintTitle+ToString(id.GetForceType()),
              lamarcmenu::forceConstraintInfo)
{
    AddMenuItem(new AddToGroupedConstraintsMenuItem("A", ui, id));
    AddMenuItem(new RemoveFromGroupedConstraintsMenuItem("R", ui, id));
    AddMenuItem(new ToggleMenuItemUngroupedConstraints(ui,id));
    AddMenuItem(new ToggleMenuItemGroupedConstraints(ui, id));
}

ConstraintMenuForOneForce::~ConstraintMenuForOneForce()
{
}

//------------------------------------------------------------------------------------

SubMenuConstraintsForOneForce::SubMenuConstraintsForOneForce(std::string key, UIInterface& ui, UIId id)
    : ForceSubMenuItem(key, ui, new ConstraintsMenuForOneForceCreator(ui, id), id)
{
}

SubMenuConstraintsForOneForce::~SubMenuConstraintsForOneForce()
{
}

//------------------------------------------------------------------------------------

AddToGroupedConstraintsMenuItem::AddToGroupedConstraintsMenuItem
(std::string key, UIInterface& ui, UIId id)
    : SetMenuItemId(key, ui, uistr::addParamToGroup, id)
{
}

AddToGroupedConstraintsMenuItem::~AddToGroupedConstraintsMenuItem()
{
}

MenuInteraction_ptr
AddToGroupedConstraintsMenuItem::GetHandler(std::string input)
{
    assert(Handles(input));
    return MenuInteraction_ptr(new GetIdAndAddDialog(ui, menuKey, id));
}

//------------------------------------------------------------------------------------

GetIdAndAddDialog::GetIdAndAddDialog(UIInterface& ui, string menuKey, UIId id)
    : SetDialog(ui, menuKey, id),
      outputId()
{
}

GetIdAndAddDialog::~GetIdAndAddDialog()
{
}

menu_return_type GetIdAndAddDialog::InvokeMe(Display& display)
{
    display.DisplayDialogOutput(beforeLoopOutputString());
    bool success = false;
    for(int i=0;(i<maxTries() && success==false);i++)
    {
        display.DisplayDialogOutput(inLoopOutputString());
        std::string result = display.GetUserInput();
        if (handleInput(result))
        {
            display.DisplayDialogOutput(afterLoopSuccessOutputString());
            success = true;
        }
        else
        {
            display.DisplayDialogOutput(inLoopFailureOutputString());
        }
    }
    if (success)
    {
        for(int i=0;i<maxTries();i++)
        {
            display.DisplayDialogOutput(inLoopOutputString2());
            std::string result = display.GetUserInput();
            if (handleInput2(result))
            {
                display.DisplayDialogOutput(afterLoopSuccessOutputString());
                return menu_REDISPLAY;
            }
            else
            {
                display.DisplayDialogOutput(inLoopFailureOutputString());
            }
        }
    }
    display.DisplayDialogOutput(afterLoopFailureOutputString());
    doFailure();
    return menu_REDISPLAY;
}

bool GetIdAndAddDialog::handleInput2(std::string input)
{
    haveError = false;
    try
    {
        stuffVal2IntoUI(input);
    }
    catch (data_error& e)
    {
        currError = e.what();
        haveError = true;
        return false;
    }
    return true;
}

void GetIdAndAddDialog::stuffValIntoUI(std::string val)
{
    long pindex = ProduceLongOrBarf(val) - 1;
    UIIdVec1d ungroupedIds=ui.doGetUIIdVec1d(uistr::ungroupedParamsForForce,id);
    bool matched = false;
    //The following code basically reiterates Group::Handles, below.
    for (UIIdVec1d::iterator thisId = ungroupedIds.begin();
         thisId != ungroupedIds.end(); thisId++)
    {
        UIId& visibleId = *thisId;
        if(visibleId.GetIndex1() == pindex)
        {
            matched = true;
        }
    }
    if (!matched)
    {
        throw data_error("That number is not the index of an ungrouped parameter.");
    }

    //Set Index2 of the UIId equal to the pindex.  Since we don't know the
    // gindex yet, just set it to zero.
    long gindex = 0;
    UIId newid(id.GetForceType(), gindex, pindex);
    outputId = newid;
};

void GetIdAndAddDialog::stuffVal2IntoUI(std::string val)
{
    if (CaselessStrCmp("N", val))
    {
        ui.doSet(uistr::addParamToNewGroup, ToString(noval_none), outputId);
        return;
    }
    long gindex = ProduceLongOrBarf(val) - 1;
    UIIdVec2d groupedIds=ui.doGetUIIdVec2d(uistr::groupedParamsForForce,id);
    if (0 > gindex || gindex >= static_cast<long>(groupedIds.size()))
    {
        throw data_error("Please enter a valid group index or 'N'.");
    }
    UIId newId(outputId.GetForceType(), gindex, outputId.GetIndex2());
    ui.doSet(menuKey,ToString(noval_none),newId);
};

string GetIdAndAddDialog::inLoopOutputString()
{
    return "Select a parameter to add to a group.\n";
}

string GetIdAndAddDialog::inLoopOutputString2()
{
    return "Select a group to add this parameter to (or N for a new group).\n";
}

//------------------------------------------------------------------------------------

RemoveFromGroupedConstraintsMenuItem::RemoveFromGroupedConstraintsMenuItem
(std::string key, UIInterface& ui, UIId id)
    : SetMenuItemId(key, ui, uistr::removeParamFromGroup, id)
{
}

RemoveFromGroupedConstraintsMenuItem::~RemoveFromGroupedConstraintsMenuItem()
{
}

MenuInteraction_ptr
RemoveFromGroupedConstraintsMenuItem::GetHandler(std::string input)
{
    assert(Handles(input));
    return MenuInteraction_ptr(new GetIdAndRemoveDialog(ui, menuKey, id));
}

//------------------------------------------------------------------------------------

GetIdAndRemoveDialog::GetIdAndRemoveDialog(UIInterface& ui, string menuKey,
                                           UIId id)
    : SetDialog(ui, menuKey, id)
{
}

GetIdAndRemoveDialog::~GetIdAndRemoveDialog()
{
}

void GetIdAndRemoveDialog::stuffValIntoUI(std::string val)
{
    long pindex = ProduceLongOrBarf(val) - 1;
    UIIdVec2d groupIds = ui.doGetUIIdVec2d(uistr::groupedParamsForForce,id);
    bool matched = false;
    //The following code basically reiterates 2dGroup::Handles, below.
    UIIdVec2d::iterator group;
    for(group = groupIds.begin(); group != groupIds.end(); group++)
    {
        for (UIIdVec1d::iterator id = (*group).begin();
             id != (*group).end(); id++)
        {
            UIId& visibleId = *id;
            if(visibleId.GetIndex2() == pindex)
            {
                matched = true;
            }
        }
    }
    if (!matched)
    {
        throw data_error("That number is not the index of a grouped parameter.");
    }
    UIId newid(id.GetForceType(), pindex);
    ui.doSet(menuKey,ToString(noval_none),newid);
};

string GetIdAndRemoveDialog::inLoopOutputString()
{
    return "Enter the index of the parameter you wish to remove from a group.\n";
}

//------------------------------------------------------------------------------------

ToggleMenuItemUngroupedConstraints::ToggleMenuItemUngroupedConstraints(UIInterface & ui,UIId id)
    : ToggleMenuItemGroup(ui,uistr::constraintType), m_id(id)
{
}

ToggleMenuItemUngroupedConstraints::~ToggleMenuItemUngroupedConstraints()
{
}

string ToggleMenuItemUngroupedConstraints::GetGroupDescription()
{
    return "Ungrouped parameters:";
}

string ToggleMenuItemUngroupedConstraints::GetEmptyDescription()
{
    return "  **None**";
}

UIIdVec1d
ToggleMenuItemUngroupedConstraints::GetVisibleIds()
{
    return ui.doGetUIIdVec1d(uistr::ungroupedParamsForForce,m_id);
}

//------------------------------------------------------------------------------------

ToggleMenuItem2dGroup::ToggleMenuItem2dGroup(UIInterface & myui,
                                             string myMenuKey)
    : ui(myui), menuKey(myMenuKey)
{
}

UIId ToggleMenuItem2dGroup::GetGroupIdFromLocalId(UIId localId)
{
    UIIdVec2d allvisibles = GetVisibleIds();
    long gindex = 0;
    for(UIIdVec2d::iterator visibleset = allvisibles.begin();
        visibleset != allvisibles.end(); visibleset++, gindex++)
    {
        for(UIIdVec1d::iterator id = (*visibleset).begin();
            id != (*visibleset).end(); id++)
        {
            UIId& visibleId = *id;
            if (visibleId.GetIndex2() == localId.GetIndex1())
            {
                return visibleId;
            }
        }
    }
    return UIId(FLAGLONG);
}

UIId ToggleMenuItem2dGroup::GetIdFromKey(string key)
{
    UIId localId(keyToIndex(key));
    return GetGroupIdFromLocalId(localId);
}

string ToggleMenuItem2dGroup::GetKey(UIId id)
{
    // There is no guarantee that this key
    // has a unique index1, but most will
    // so this is the default implementation.
    // If this is causing you trouble, override
    // this virtual method
    return indexToKey(id.GetIndex2());
}

string ToggleMenuItem2dGroup::GetText(UIId id)
{
    return ui.doGetDescription(menuKey,id);
}

string ToggleMenuItem2dGroup::GetVariableText(UIId id)
{
    return ui.doGetPrintString(menuKey,id);
}

string ToggleMenuItem2dGroup::GetGroupDescription(long)
{
    return "\n";
}

string ToggleMenuItem2dGroup::GetEmptyDescription(long)
{
    return "\n";
}

bool ToggleMenuItem2dGroup::Handles(string input)
{
    // kinda wasteful, but avoids some unpleasant to
    // code error checking
    UIIdVec2d allvisibles = GetVisibleIds();
    UIIdVec2d::iterator group;
    for(group = allvisibles.begin(); group != allvisibles.end(); group++)
    {
        for (UIIdVec1d::iterator id = (*group).begin();
             id != (*group).end(); id++)
        {
            UIId& visibleId = *id;
            if(CaselessStrCmp(GetKey(visibleId),input))
            {
                return true;
            }
        }
    }
    return false;
}

MenuInteraction_ptr ToggleMenuItem2dGroup::GetHandler(string input)
{
    UIId groupId = GetIdFromKey(input);
    if (groupId.GetIndex1() != FLAGLONG)
    {
        return MakeOneHandler(groupId);
    }
    else
    {
        return MenuInteraction_ptr(new DoNothingHandler());
    }
}

MenuInteraction_ptr ToggleMenuItem2dGroup::MakeOneHandler(UIId id)
{
    return MenuInteraction_ptr(new NewToggleHandler(ui,menuKey,id));
}

void ToggleMenuItem2dGroup::DisplayItemOn(Display & display)
{
    display.ShowMenuDisplay2dGroup(*this);
}

//------------------------------------------------------------------------------------

ToggleMenuItemGroupedConstraints::ToggleMenuItemGroupedConstraints
(UIInterface & ui,UIId id)
    : ToggleMenuItem2dGroup(ui,uistr::groupConstraintType),
      m_id(id)
{
}

ToggleMenuItemGroupedConstraints::~ToggleMenuItemGroupedConstraints()
{
}

UIIdVec2d ToggleMenuItemGroupedConstraints::GetVisibleIds()
{
    return ui.doGetUIIdVec2d(uistr::groupedParamsForForce,m_id);
}

string ToggleMenuItemGroupedConstraints::GetGroupDescription(long gid)
{
    return ("Group " + ToString(gid+1));
}

string ToggleMenuItemGroupedConstraints::GetEmptyDescription(long)
{
    return ("  **No parameters in this group**");
}

//____________________________________________________________________________________
