// $Id: ui_interface.cpp,v 1.52 2018/01/03 21:33:05 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 <string>
#include <fstream>

#include "constants.h"
#include "front_end_warnings.h"
#include "parameter.h"
#include "setgetmachine.h"
#include "stringx.h"
#include "ui_constants.h"
#include "ui_interface.h"
#include "ui_strings.h"
#include "ui_vars.h"
#include "ui_regid.h"
#include "undoredochain.h"
#include "xml.h"

UIInterface::UIInterface(FrontEndWarnings & warnings, DataPack& datapack, string fileName)
    :
    m_warnings(warnings),
    undoRedoChain(new UndoRedoChain(datapack,fileName,this)),
    undoRedoMode(undoRedoMode_FILE)
{
}

UIInterface::~UIInterface()
{
    delete undoRedoChain;
}

UIVars & UIInterface::GetCurrentVars()
{
    return undoRedoChain->GetCurrentVars();
}

//------------------------------------------------------------------------------------
// get, set and prints for variables that are indexed by id number
//------------------------------------------------------------------------------------

bool UIInterface::doGetBool(string varName, const UIId id)
{
    return setGetMachine.doGetBool(varName, GetCurrentVars(),id);
}

data_type UIInterface::doGetDataType(string varName, const UIId id)
{
    return setGetMachine.doGetDataType(varName, GetCurrentVars(),id);
}

double UIInterface::doGetDouble(string varName, const UIId id)
{
    return setGetMachine.doGetDouble(varName, GetCurrentVars(),id);
}

DoubleVec1d UIInterface::doGetDoubleVec1d(string varName, const UIId id)
{
    return setGetMachine.doGetDoubleVec1d(varName, GetCurrentVars(),id);
}

force_type
UIInterface::doGetForceType(string varName, const UIId id)
{
    return setGetMachine.doGetForceType(varName,GetCurrentVars(),id);
}

ForceTypeVec1d UIInterface::doGetForceTypeVec1d(string variableName, const UIId id)
{
    return setGetMachine.doGetForceTypeVec1d(variableName,GetCurrentVars(),id);
}

long UIInterface::doGetLong(string varName, const UIId id)
{
    return setGetMachine.doGetLong(varName, GetCurrentVars(),id);
}

LongVec1d UIInterface::doGetLongVec1d(string variableName, const UIId id)
{
    return setGetMachine.doGetLongVec1d(variableName,GetCurrentVars(),id);
}

method_type UIInterface::doGetMethodType(string varName, const UIId id)
{
    return setGetMachine.doGetMethodType(varName, GetCurrentVars(),id);
}

model_type UIInterface::doGetModelType(string varName, const UIId id)
{
    return setGetMachine.doGetModelType(varName, GetCurrentVars(),id);
}

proftype UIInterface::doGetProftype(string varName, const UIId id)
{
    return setGetMachine.doGetProftype(varName, GetCurrentVars(),id);
}

string UIInterface::doGetString(string varName, const UIId id)
{
    return setGetMachine.doGetString(varName, GetCurrentVars(),id);
}

StringVec1d UIInterface::doGetStringVec1d(string varName, const UIId id)
{
    return setGetMachine.doGetStringVec1d(varName, GetCurrentVars(),id);
}

UIIdVec1d
UIInterface::doGetUIIdVec1d(string varName, const UIId id)
{
    return setGetMachine.doGetUIIdVec1d(varName, GetCurrentVars(),id);
}

UIIdVec2d
UIInterface::doGetUIIdVec2d(string varName, const UIId id)
{
    return setGetMachine.doGetUIIdVec2d(varName, GetCurrentVars(),id);
}

verbosity_type UIInterface::doGetVerbosityType(string varName, const UIId id)
{
    return setGetMachine.doGetVerbosityType(varName, GetCurrentVars(),id);
}

void UIInterface::doSet(string varName, string value, const UIId id)
// EWFIX.P5 REFACTOR -- uiInterface::doSet and uiInterface::doToggle
// have important duplicated code
{
    if(undoRedoMode == undoRedoMode_USER)
    {
        undoRedoChain->StartNewFrame();
        try
        {
            setGetMachine.doSet(varName, GetCurrentVars(),id,value);
        }
        catch(const data_error &e)
        {
            undoRedoChain->RejectNewFrame();
            throw;
        }
        undoRedoChain->AcceptNewFrame();
    }
    else
    {
        setGetMachine.doSet(varName, GetCurrentVars(),id,value);
    }
}

void UIInterface::doToggle(string varName, const UIId id)
// EWFIX.P5 REFACTOR -- uiInterface::doSet and uiInterface::doToggle
// have important duplicated code
{
    if(undoRedoMode == undoRedoMode_USER)
    {
        undoRedoChain->StartNewFrame();
        try
        {
            setGetMachine.doToggle(varName, GetCurrentVars(),id);
        }
        catch(const data_error &e)
        {
            undoRedoChain->RejectNewFrame();
            throw;
        }
        undoRedoChain->AcceptNewFrame();
    }
    else
    {
        setGetMachine.doToggle(varName, GetCurrentVars(),id);
    }
}

string UIInterface::doGetPrintString(string varName, const UIId id)
{
    return setGetMachine.doGetPrintString(varName, GetCurrentVars(),id);
}

string UIInterface::doGetDescription(string variableName, const UIId id)
{
    return setGetMachine.doGetDescription(variableName,GetCurrentVars(),id);
}

bool UIInterface::doGetConsistency(string variableName,const UIId id)
{
    return setGetMachine.doGetConsistency(variableName,GetCurrentVars(),id);
}

string UIInterface::doGetMin(string varName, const UIId id )
{
    return setGetMachine.doGetMin(varName, GetCurrentVars(), id);
}

string UIInterface::doGetMax(string varName, const UIId id )
{
    return setGetMachine.doGetMax(varName, GetCurrentVars(), id);
}

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

void UIInterface::SetUndoRedoMode(undo_redo_mode umode)
{
    undoRedoMode = umode;
}

void UIInterface::Undo()
{
    undoRedoChain->Undo();
}

void UIInterface::Redo()
{
    undoRedoChain->Redo();
}

bool UIInterface::CanUndo()
{
    return undoRedoChain->CanUndo();
}

bool UIInterface::CanRedo()
{
    return undoRedoChain->CanRedo();
}

std::string UIInterface::GetUndoDescription()
{
    return undoRedoChain->GetUndoDescription();
}

std::string UIInterface::GetRedoDescription()
{
    return undoRedoChain->GetRedoDescription();
}

StringVec1d UIInterface::GetAndClearWarnings()
{
    return m_warnings.GetAndClearWarnings();
}

void UIInterface::AddWarning(std::string warnmsg)
{
    m_warnings.AddWarning(warnmsg);
}

bool UIInterface::IsReadyToRun()
{
    if (WhatIsWrong() == "")
    {
        return true;
    }
    return false;
}

std::string UIInterface::WhatIsWrong()
{
    //LS NOTE:  Any other tests we want can also go here.
    //Test #1:  One or more forces have too many starting values set to zero.
    vector<force_type> forces = GetCurrentVars().forces.GetActiveForces();
    for (unsigned long fnum = 0; fnum<forces.size(); fnum++)
    {
        if (!GetCurrentVars().forces.AreZeroesValid(forces[fnum]))
        {
            string msg = "Invalid settings for force " + ToString(forces[fnum]) +
                ".  Too many parameters are set invalid \n  or have a start value of " +
                "zero.  To fix this, select 'A' from the main menu,\n  then select " +
                "the appropriate force, then change the starting values or select\n" +
                "  'C' and change some of the constraints.\n";
            return msg;
        }
    }

    //Test #2: Summary file reading is on, but we cannot read the file.
    if (GetCurrentVars().userparams.GetReadSumFile())
    {
        string sumInName = GetCurrentVars().userparams.GetTreeSumInFileName();
        std::ifstream testsum(sumInName.c_str(), std::ios::in);
        if(!testsum)
        {
            string msg = "Cannot open or read file \""
                + sumInName
                + "\" for summary file reading.  Check that the name is valid and "
                + "that the permissions are correct, or simply turn off summary file "
                + "reading for this run.";
            return msg;
        }
    }

    //Test #3:  Bayesian analysis is on, but all parameters are set constant
    // or invalid.
    if (GetCurrentVars().chains.GetDoBayesianAnalysis())
    {
        if (!GetCurrentVars().forces.SomeVariableParams())
        {
            if (!GetCurrentVars().traitmodels.GetNumMovableLoci() > 0)
            {
                return "All of the parameters are set constant or invalid, "
                    "which means that this Bayesian run has nothing to do.  If you "
                    "truly want no parameter to vary, set one parameter's prior to be "
                    "very thin.";
            }
        }
    }

    //Test #4:  Some category rates are identical.
    for(long regionId = 0; regionId < GetCurrentVars().datapackplus.GetNumRegions() ; regionId++)
    {
        for (long locusId = 0; locusId < GetCurrentVars().datapackplus.GetNumLoci(regionId); locusId++)
        {
            UIRegId regId(regionId,locusId,GetCurrentVars());
            if (GetCurrentVars().datamodel.IdenticalCategoryRates(regId))
            {
                string msg;
                if (GetCurrentVars().datamodel.GetUseGlobalModel(regId))
                {
                    msg = "The global data model for " + ToString(regId.GetDataType())
                        + " data";
                }
                else
                {
                    msg = "The data model for region " + ToString(regId.GetRegion()+1);
                    if (GetCurrentVars().datapackplus.GetNumLoci(regionId) > 1)
                    {
                        msg += ", segment " + ToString(regId.GetLocus()+1);
                    }
                }
                msg += " has multiple categories with the same rate, which serves no "
                    "purpose but to slow the program down considerably.  Please change this "
                    "before running the program.";
                return msg;
            }
        }
    }

    //Passed all the tests.
    return "";
}

//____________________________________________________________________________________
