// $Id: ui_vars_userparams.cpp,v 1.37 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 <cassert>
#include <fstream> //For summary file testing.

#include "local_build.h"

#include "defaults.h"
#include "errhandling.h"
#include "timex.h"      // for GetTime()
#include "ui_regid.h"
#include "ui_vars.h"

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

UIVarsUserParameters::UIVarsUserParameters(UIVars * myUIVars,string fileName)
    :
    UIVarsComponent(myUIVars),
    m_curveFilePrefix                 (defaults::curvefileprefix),
    m_mapFilePrefix                   (defaults::mapfileprefix),
    m_reclocFilePrefix                (defaults::reclocfileprefix),
    m_traceFilePrefix                 (defaults::tracefileprefix),
    m_newickTreeFilePrefix            (defaults::newicktreefileprefix),
#ifdef LAMARC_QA_TREE_DUMP
    m_argFilePrefix                   (defaults::argfileprefix),
#endif // LAMARC_QA_TREE_DUMP
    m_dataFileName                    (fileName),
    m_plotPost                        (defaults::plotpost),
    m_programStartTime                (defaults::programstarttime),
    m_progress                        (defaults::progress),
    m_hasOldClockSeed                 (defaults::hasoldrandomseed),
    m_oldClockSeed                    (defaults::randomseed),
    m_randomSeed                      (defaults::randomseed),
    m_readSumFile                     (defaults::readsumfile),
    m_resultsFileName                 (defaults::resultsfilename),
    m_treeSumInFileName               (defaults::treesuminfilename),
    m_treeSumOutFileName              (defaults::treesumoutfilename),
    m_useSystemClock                  (defaults::usesystemclock),
    m_verbosity                       (defaults::verbosity),
    m_writeCurveFiles                 (defaults::writecurvefiles),
    m_writeSumFile                    (defaults::writesumfile),
    m_writeReclocFiles                (defaults::writereclocfiles),
    m_writeTraceFiles                 (defaults::writetracefiles),
    m_writeNewickTreeFiles            (defaults::writenewicktreefiles),
#ifdef LAMARC_QA_TREE_DUMP
    m_writeArgFiles                   (defaults::writeargfiles),
    m_writeManyArgs                   (defaults::writemanyargs),
#endif // LAMARC_QA_TREE_DUMP
    m_xmlOutFileName                  (defaults::xmloutfilename),
    m_xmlReportFileName               (defaults::xmlreportfilename),
    m_profilePrefix                   (defaults::profileprefix)
{
    SetUseSystemClock(true);// get a good initial random seed
    SetProgramStartTime();
}

UIVarsUserParameters::UIVarsUserParameters(UIVars * myUIVars,const UIVarsUserParameters& uparams)
    :
    UIVarsComponent(myUIVars),
    m_curveFilePrefix                 (uparams.m_curveFilePrefix),
    m_mapFilePrefix                   (uparams.m_mapFilePrefix),
    m_reclocFilePrefix                (uparams.m_reclocFilePrefix),
    m_traceFilePrefix                 (uparams.m_traceFilePrefix),
    m_newickTreeFilePrefix            (uparams.m_newickTreeFilePrefix),
#ifdef LAMARC_QA_TREE_DUMP
    m_argFilePrefix                   (uparams.m_argFilePrefix),
#endif // LAMARC_QA_TREE_DUMP
    m_dataFileName                    (uparams.m_dataFileName),
    m_plotPost                        (uparams.m_plotPost),
    m_programStartTime                (uparams.m_programStartTime),
    m_progress                        (uparams.m_progress),
    m_hasOldClockSeed                 (uparams.m_hasOldClockSeed),
    m_oldClockSeed                    (uparams.m_oldClockSeed),
    m_randomSeed                      (uparams.m_randomSeed),
    m_readSumFile                     (uparams.m_readSumFile),
    m_resultsFileName                 (uparams.m_resultsFileName),
    m_treeSumInFileName               (uparams.m_treeSumInFileName),
    m_treeSumOutFileName              (uparams.m_treeSumOutFileName),
    m_useSystemClock                  (uparams.m_useSystemClock),
    m_verbosity                       (uparams.m_verbosity),
    m_writeCurveFiles                 (uparams.m_writeCurveFiles),
    m_writeSumFile                    (uparams.m_writeSumFile),
    m_writeReclocFiles                (uparams.m_writeReclocFiles),
    m_writeTraceFiles                 (uparams.m_writeTraceFiles),
    m_writeNewickTreeFiles            (uparams.m_writeNewickTreeFiles),
#ifdef LAMARC_QA_TREE_DUMP
    m_writeArgFiles                   (uparams.m_writeArgFiles),
    m_writeManyArgs                   (uparams.m_writeManyArgs),
#endif // LAMARC_QA_TREE_DUMP
    m_xmlOutFileName                  (uparams.m_xmlOutFileName),
    m_xmlReportFileName               (uparams.m_xmlReportFileName),
    m_profilePrefix                   (uparams.m_profilePrefix)
{
}

UIVarsUserParameters::~UIVarsUserParameters()
{
}

bool  UIVarsUserParameters::GetReadSumFile() const
{
    return m_readSumFile;
}

bool UIVarsUserParameters::GetWriteSumFile() const
{
    return m_writeSumFile;
}

bool UIVarsUserParameters::GetWriteNewickTreeFiles() const
{
    if (GetConstUIVars().forces.GetForceOnOff(force_MIG) || 
        GetConstUIVars().forces.GetForceOnOff(force_DIVERGENCE) ||
        GetConstUIVars().forces.GetForceOnOff(force_REC))
    {
        return false;
    }
    return m_writeNewickTreeFiles;
}

//LS DEBUG:  Do we want to test for leading and/or trailing whitespace for all
// these various file names and prefixes?

void UIVarsUserParameters::SetCurveFilePrefix(string x)
{
    CheckPrefix(x);
    m_curveFilePrefix = x;
    CheckCurveFiles();
};

void UIVarsUserParameters::SetMapFilePrefix(string x)
{
    CheckPrefix(x);
    m_mapFilePrefix = x;
    CheckMapFiles();
};

void UIVarsUserParameters::SetReclocFilePrefix(string x)
{
    CheckPrefix(x);
    m_reclocFilePrefix = x;
    CheckReclocFiles();
};

void UIVarsUserParameters::SetTraceFilePrefix(string x)
{
    CheckPrefix(x);
    m_traceFilePrefix = x;
    CheckTraceFiles();
};

void UIVarsUserParameters::SetNewickTreeFilePrefix(string x)
{
    CheckPrefix(x);
    m_newickTreeFilePrefix = x;
    CheckNewickTreeFile();
};

#ifdef LAMARC_QA_TREE_DUMP
void UIVarsUserParameters::SetArgFilePrefix(string x)
{
    CheckPrefix(x);
    m_argFilePrefix = x;
    CheckArgFile();
};
#endif // LAMARC_QA_TREE_DUMP

void UIVarsUserParameters::SetDataFileName(string x)
{
    //This routine is not currently used, since the dataFileName is set in
    // the constructor and never changed.
    assert(false);
    if (x == "")
    {
        throw data_error("Expected the name of a file but got \"\".");
    }
    m_dataFileName = x;
};

void UIVarsUserParameters::SetResultsFileName(string x)
{
    if (x == "")
    {
        throw data_error("Expected the name of a file but got \"\".");
    }
    m_resultsFileName = x;
    std::ifstream testsum(m_resultsFileName.c_str(), std::ios::in);
    if(testsum)
    {
        string msg = "Warning:  your current settings will overwrite \""
            + m_resultsFileName
            + "\". Change the name of the output file or move the "
            + "existing file if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
};

void UIVarsUserParameters::SetXMLOutFileName(string x)
{
    if (x == "")
    {
        throw data_error("Expected the name of a file but got \"\".");
    }
    m_xmlOutFileName = x;
}

void UIVarsUserParameters::SetXMLReportFileName(string x)
{
    if (x == "")
    {
        throw data_error("Expected the name of a file but got \"\".");
    }
    m_xmlReportFileName = x;
}

void UIVarsUserParameters::SetProfilePrefix(string x)
{
    CheckPrefix(x);
    m_profilePrefix = x;
}

// problems: 8/02 daniel
// - will open directories as if they're sumfiles if told to do so
// - no persistence in asking for asking for a file to write to when overwriting is warned against... other case is fine

//LS DEBUG:  Daniel's first above problem is still true, and I have no idea
// what the second problem even means.

void UIVarsUserParameters::SetTreeSumInFileName(string x)
{
    if (x == "")
    {
        throw data_error("Expected the name of a file but got \"\".");
    }
    m_treeSumInFileName = x;
    CheckReadSumFile();
};

void UIVarsUserParameters::SetTreeSumOutFileName(string x)
{
    if (x == "")
    {
        throw data_error("Expected the name of a file but got \"\".");
    }
    m_treeSumOutFileName = x;
    // warn the user if an older version may be clobbered
    CheckWriteSumFile();
};

void UIVarsUserParameters::SetWriteCurveFiles(bool x)
{
    if (x && !m_writeCurveFiles)
    {
        //We're turning on curve files from being off, so check.
        CheckCurveFiles();
    }
    m_writeCurveFiles = x;
}

void UIVarsUserParameters::SetWriteReclocFiles(bool x)
{
    if (x && !m_writeReclocFiles)
    {
        //We're turning on recloc files from being off, so check.
        CheckReclocFiles();
    }
    m_writeReclocFiles = x;
}

void UIVarsUserParameters::SetWriteTraceFiles(bool x)
{
    if (x && !m_writeTraceFiles)
    {
        //We're turning on trace files from being off, so check.
        CheckTraceFiles();
    }
    m_writeTraceFiles = x;
}

void UIVarsUserParameters::SetWriteNewickTreeFiles(bool x)
{
    if (x && GetConstUIVars().forces.GetForceOnOff(force_MIG))
    {
        GetConstUIVars().GetUI()->AddWarning("Cannot write a newick tree while performing an analysis with migration.");
        return;
    }
    if (x && GetConstUIVars().forces.GetForceOnOff(force_DIVERGENCE))
    {
        GetConstUIVars().GetUI()->AddWarning("Cannot write a newick tree while performing an analysis with divergence.");
        return;
    }
    if (x && GetConstUIVars().forces.GetForceOnOff(force_REC))
    {
        GetConstUIVars().GetUI()->AddWarning("Cannot write a newick tree while performing an analysis with recombination.");
        return;
    }
    m_writeNewickTreeFiles = x;
    CheckNewickTreeFile();
}

#ifdef LAMARC_QA_TREE_DUMP
void UIVarsUserParameters::SetWriteArgFiles(bool x)
{
    m_writeArgFiles = x;
    CheckArgFile();
}

void UIVarsUserParameters::SetWriteManyArgs(bool x)
{
    m_writeManyArgs = x;
    CheckArgFile();
}
#endif // LAMARC_QA_TREE_DUMP

void UIVarsUserParameters::SetReadSumFile(bool x)
{
    m_readSumFile = x;
    CheckReadSumFile();
}

void UIVarsUserParameters::SetWriteSumFile(bool x)
{
    m_writeSumFile = x;
    CheckWriteSumFile();
}

void UIVarsUserParameters::SetProgramStartTime()
{
    m_programStartTime = GetTime();
}

void UIVarsUserParameters::SetRandomSeed(long seed)
{
    // We need the closest 4n+1.  And it probably has to be positive; we're
    // enforcing this latter blindly since we know it will work instead of
    // scratching our heads at the algorithm to be sure.
    m_randomSeed = 4 * static_cast<long>( abs(seed) / 4 ) + 1;
    if (m_randomSeed != seed)
    {
        string warn = "Warning:  Using " + ToString(m_randomSeed)
            + " as the random seed (the closest positive integer of the form 4n+1) instead of "
            + ToString(seed) + ".";
        GetConstUIVars().GetUI()->AddWarning(warn);
    }
    m_useSystemClock = false;
}

void UIVarsUserParameters::SetUseSystemClock(bool val)
{
    if(val)
    {
        // if we're setting this value to "true",
        // we need to generate the seed we'll
        // be using
        m_randomSeed = 4 * static_cast<long>( time(NULL) / 4 ) + 1;
        m_useSystemClock = true;
    }
    else
    {
        throw implementation_error("SetUseSystemClock not expected to get false argument");
    }
}

long UIVarsUserParameters::GetOldClockSeed() const
{
    assert(m_hasOldClockSeed);
    return m_oldClockSeed;
}

void UIVarsUserParameters::SetOldClockSeed(long seed)
{
    m_hasOldClockSeed = true;
    m_oldClockSeed = seed;
}

void UIVarsUserParameters::SetUseOldClockSeed(bool val)
{
    if(val)
    {
        m_randomSeed = m_oldClockSeed;
        m_useSystemClock = false;
    }
    else
    {
        throw implementation_error("SetUseOldClockSeed not expected to get false argument");
    }
}

void UIVarsUserParameters::CheckReadSumFile()
{
    if (m_readSumFile)
    {
        std::ifstream testsum(m_treeSumInFileName.c_str(), std::ios::in);
        if(!testsum)
        {
            string msg = "Warning:  cannot open or read file \""
                + m_treeSumInFileName
                + "\". Change the name of the input summary file or "
                + "fix permissions on the file.";
            GetConstUIVars().GetUI()->AddWarning(msg);
        }
        else
        {
            CheckBothSumFiles();
        }
    }
}

void UIVarsUserParameters::CheckWriteSumFile()
{
    if (m_writeSumFile)
    {
        if (!CheckBothSumFiles())
        {
            std::ifstream testsum(m_treeSumOutFileName.c_str(), std::ios::in);
            if(testsum)
            {
                string msg = "Warning:  your current settings will overwrite \""
                    + m_treeSumOutFileName
                    + "\". Change the name of the output summary file or move the "
                    + "existing file if that's not OK.";
                GetConstUIVars().GetUI()->AddWarning(msg);
            }
        }
    }
}

bool UIVarsUserParameters::CheckBothSumFiles()
{
    if (m_writeSumFile && m_readSumFile && (m_treeSumOutFileName == m_treeSumInFileName))
    {
        string msg = "Warning:  you are reading and writing to the same "
            "summary file.  LAMARC is designed to handle this situation, but "
            "oddnesses with your file system during a LAMARC run could cause "
            "data loss.";
        GetConstUIVars().GetUI()->AddWarning(msg);
        return true;
    }
    return false;
}

void UIVarsUserParameters::CheckNewickTreeFile()
{
    string oneNewickTreeFileName = m_newickTreeFilePrefix + "_";
    oneNewickTreeFileName += SpacesToUnderscores(GetConstUIVars().datapackplus.GetSimpleRegionName(0)) + ".txt";
    std::ifstream testsum(oneNewickTreeFileName.c_str(), std::ios::in);
    if(testsum && GetWriteNewickTreeFiles())
    {
        string msg = "Warning:  your current settings will overwrite \""
            + oneNewickTreeFileName
            + "\" and may also overwrite other files in the same directory with the "
            + "same prefix. Change the name of the Newick Tree file prefix or "
            + "move the existing file(s) if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
}

#ifdef LAMARC_QA_TREE_DUMP
void UIVarsUserParameters::CheckArgFile()
{
    string oneArgFileName = m_argFilePrefix + "_";
    oneArgFileName += SpacesToUnderscores(GetConstUIVars().datapackplus.GetSimpleRegionName(0)) + ".txt";
    std::ifstream testsum(oneArgFileName.c_str(), std::ios::in);
    if(testsum && GetWriteArgFiles())
    {
        string msg = "Warning:  your current settings will overwrite \""
            + oneArgFileName
            + "\" and may also overwrite other files in the same directory with the "
            + "same prefix. Change the name of the AREG file prefix or "
            + "move the existing file(s) if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
}
#endif // LAMARC_QA_TREE_DUMP

void UIVarsUserParameters::CheckCurveFiles()
{
    string oneCurveFileName = m_curveFilePrefix + "_reg1_Theta1.txt"; // EWFIX -- really? will this name always exist
    std::ifstream testsum(oneCurveFileName.c_str(), std::ios::in);
    if(testsum && m_writeCurveFiles && GetConstUIVars().chains.GetDoBayesianAnalysis())
    {
        string msg = "Warning:  your current settings will overwrite \""
            + oneCurveFileName
            + "\" and will probably overwrite other files in the same directory with "
            + "the same prefix. Change the name of the Bayesian curvefile prefix or "
            + "move the existing file(s) if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
}

void UIVarsUserParameters::CheckMapFiles()
{
    string oneMapFileName = m_mapFilePrefix + "_";
    const UIVarsTraitModels & tms = GetConstUIVars().traitmodels;
    vector<UIRegId> ids = tms.GetRegIDs();
    assert(!ids.empty());
    oneMapFileName += SpacesToUnderscores(tms.GetName(ids[0]));
    oneMapFileName += ".txt";
    std::ifstream testsum(oneMapFileName.c_str(), std::ios::in);
    if(testsum)
    {
        string msg = "Warning:  your current settings will overwrite \""
            + oneMapFileName
            + "\" and will probably overwrite other files in the same directory with "
            + "the same prefix. Change the name of the mapping prefix or "
            + "move the existing file(s) if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
}

void UIVarsUserParameters::CheckReclocFiles()
{
    string oneReclocFileName = m_reclocFilePrefix + "_";
    oneReclocFileName += SpacesToUnderscores(GetConstUIVars().datapackplus.GetSimpleRegionName(0)) + "_1.txt";
    std::ifstream testsum(oneReclocFileName.c_str(), std::ios::in);
    if(testsum && GetWriteReclocFiles())
    {
        string msg = "Warning:  your current settings will overwrite \""
            + oneReclocFileName
            + "\" and may also overwrite other files in the same directory with the "
            + "same prefix. Change the name of the recloc prefix or "
            + "move the existing file(s) if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
}

void UIVarsUserParameters::CheckTraceFiles()
{
    string oneTraceFileName = m_traceFilePrefix + "_";
    oneTraceFileName += SpacesToUnderscores(GetConstUIVars().datapackplus.GetSimpleRegionName(0)) + "_1.txt";
    std::ifstream testsum(oneTraceFileName.c_str(), std::ios::in);
    if(testsum && GetWriteTraceFiles())
    {
        string msg = "Warning:  your current settings will overwrite \""
            + oneTraceFileName
            + "\" and may also overwrite other files in the same directory with the "
            + "same prefix. Change the name of the tracefile prefix or "
            + "move the existing file(s) if that's not OK.";
        GetConstUIVars().GetUI()->AddWarning(msg);
    }
}

void UIVarsUserParameters::CheckPrefix(string x)
{
    if (x == "")
    {
        throw data_error("Expected a prefix but got \"\".");
    }
}

//____________________________________________________________________________________
