// $Id: spreadsheet.cpp,v 1.5 2018/01/03 21:33:02 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: Mary Kuhner, Jon Yamato, and Joseph Felsenstein */


#include <cassert>
#include <fstream>
#include <iostream>

#include "registry.h"
#include "spreadsheet.h"
#include "stringx.h"
#include "tinyxml.h"
#include "tixml_base.h"
#include "vectorx.h"

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

std::string makeFileName(std::string filePrefix, std::string paramName)
{
    return (filePrefix + "_" + paramName + ".csv");
}

size_t getNumSlices(TiXmlElement * estimateElem)
{
    TiXmlElement * profElem = ti_requiredChild(estimateElem,"profile");
    std::vector<TiXmlElement*> slices = ti_requiredChildren(profElem,"profile-slice");
    return slices.size();
}

void addFirstCol(   StringVec2d &   table,
                    TiXmlElement *  estimateElem,
                    std::string &   profileTypeOut,
                    std::string &   analysisTypeOut)
{
    TiXmlElement * profElem = ti_requiredChild(estimateElem,"profile");
    profileTypeOut = ti_attributeValue(profElem,"type");
    analysisTypeOut= ti_attributeValue(profElem,"analysis");

    std::string profileIndexLabel = profileTypeOut == "fixed" ? "multiplier" : "percentile";
    table[0][0] = "\"" + profileIndexLabel + "\"";

    std::vector<TiXmlElement*> slices = ti_requiredChildren(profElem,"profile-slice");
    std::vector<TiXmlElement*>::const_iterator siter;
    size_t index;
    for(siter = slices.begin(), index = 1; siter != slices.end(); siter++, index++)
    {
        std::string labelVal = "";

        TiXmlElement * perc = ti_optionalChild(*siter,profileIndexLabel);
        if(perc != NULL)
        {
            labelVal = ti_attributeValue(perc,"value");
        }
        if(ti_hasAttribute(*siter,"special"))
        {
            labelVal = ti_attributeValue(*siter,"special");
        }
        table[index][0] = labelVal;
    }
}

void addEstimate(   StringVec2d &   table,
                    size_t          estIndex,
                    TiXmlElement *  estimateElem,
                    std::string     paramName,
                    std::string     regionName,
                    std::string     expectedProfileType,
                    std::string     expectedAnalysisType)
{
    TiXmlElement * profElem = ti_requiredChild(estimateElem,"profile");
    std::string profileType = ti_attributeValue(profElem,"type");
    std::string analysisType = ti_attributeValue(profElem,"analysis");
    assert(profileType == expectedProfileType);     // EWFIX.BUG.838 -- should throw
    assert(analysisType == expectedAnalysisType);   // EWFIX.BUG.838 -- should throw

    size_t paramIndex = estIndex * 2 + 1;
    size_t probIndex  = paramIndex + 1;

    std::string probTag   = (expectedAnalysisType == "bayesian") ? "point-probability" : "log-likelihood";
    std::string probLabel = (expectedAnalysisType == "bayesian") ? "PointProb" : "LnLike";

    table[0][paramIndex] = "\"" + paramName + ":" + regionName + "\"";
    table[0][probIndex ] = "\"" + probLabel + "(" + paramName + ":" + regionName + ")\"";

    std::vector<TiXmlElement*> slices = ti_requiredChildren(profElem,"profile-slice");
    std::vector<TiXmlElement*>::const_iterator siter;
    size_t sliceIndex;
    for(siter = slices.begin(), sliceIndex = 1; siter != slices.end(); siter++, sliceIndex++)
    {
        TiXmlElement * paramElem = ti_requiredChild(*siter,"param-value");
        std::string paramVal = ti_attributeValue(paramElem,"value");

        TiXmlElement * probElem = ti_requiredChild(*siter,probTag);
        std::string probVal = ti_attributeValue(probElem,"value");

        table[sliceIndex][paramIndex] = paramVal;
        table[sliceIndex][probIndex]  = probVal;
    }
}

void reorderEstimates(std::vector<TiXmlElement*> & estimates)
// if there is an "overall" estimate, move it to the front
{
    std::vector<TiXmlElement*>::iterator eiter;
    std::vector<TiXmlElement*>::iterator overall = estimates.end();
    for(eiter = estimates.begin(); eiter != estimates.end(); eiter++)
    {
        TiXmlElement * estElem = *eiter;
        std::string typeVal = ti_attributeValue(estElem,"type");    // single region or overall
        if(typeVal == "overall")
        {
            overall = eiter;
            break;
        }
    }

    if(overall != estimates.end())
    {
        TiXmlElement * overallElem = *eiter;
        estimates.erase(eiter);
        estimates.insert(estimates.begin(),overallElem);
    }
}

void WriteOneProfileSpread( std::string     filePrefix,
                            TiXmlElement *  forceElem,
                            TiXmlElement *  paramElem)
{
    std::string forceName = ti_attributeValue(forceElem,"short-name");
    std::string paramName = ti_attributeValue(paramElem,"short-name");

    string::size_type i = paramName.find("/");
    while (i != string::npos)
    {
        paramName.replace(i,1,"+");
        i = paramName.find("/");
    }

    std::string fileName  = makeFileName(filePrefix,paramName);

    std::ofstream profileStream;
    profileStream.open(fileName.c_str(),std::ios::out);

    // open file
    std::vector<TiXmlElement*> estimates = ti_requiredChildren(paramElem,"estimate");
    size_t numEstimates = estimates.size();

    assert(estimates.begin() != estimates.end());
    size_t numSlices = getNumSlices(*(estimates.begin()));

    size_t rowCount = 1 + numSlices;         // header + slices
    size_t colCount = 1 + 2 * numEstimates;  // multiplier/percentile + for each estimate, param and prob/like

    reorderEstimates(estimates);

    StringVec2d table = CreateVec2d(rowCount,colCount,std::string(""));
    std::string expectedProfileType;
    std::string expectedAnalysisType;
    addFirstCol(table,*(estimates.begin()),expectedProfileType,expectedAnalysisType);

    std::vector<TiXmlElement*>::const_iterator eiter;
    size_t index;
    for(eiter = estimates.begin(), index=0; eiter != estimates.end(); eiter++,index++)
    {
        TiXmlElement * estElem = *eiter;
        std::string regionName;
        std::string typeVal = ti_attributeValue(estElem,"type");    // single region or overall
        if(typeVal == "overall")
        {
            regionName = "overall";
        }
        else
        {
            regionName = ti_attributeValue(estElem,"region-name");
        }
        addEstimate(table,index,*eiter,paramName,regionName,expectedProfileType,expectedAnalysisType);
    }

    // header lines
    std::string intervalTypes = (expectedAnalysisType == "bayesian")
        ? "credibility intervals"
        : "confidence intervals";
    // parameter name
    profileStream << "Profile Information" << std::endl;
    profileStream << "Parameter: " << paramName << std::endl;
    profileStream << "Analysis type: " << expectedAnalysisType
                  << " which produces " << intervalTypes << std::endl;
    profileStream << "Data points are from " << expectedProfileType << " profiling." << std::endl;

    size_t row;
    size_t col;
    for(row = 0; row < rowCount; row++)
    {
        for(col = 0; col < colCount; col++)
        {
            if(col != 0)
            {
                profileStream << ",";
            }
            profileStream << table[row][col];
        }
        profileStream << std::endl;
    }

    profileStream.close();
    registry.GetUserParameters().AddProfileName(fileName);
}

void WriteProfileSpreads(std::string filePrefix, TiXmlElement * reportTop)
{

    // <lamarc-run-report>
    //     <parameters>
    //         <force>
    //             <parameter>
    TiXmlElement * params = ti_requiredChild(reportTop,"parameters");
    std::vector<TiXmlElement *> forces = ti_requiredChildren(params,"force");
    std::vector<TiXmlElement *>::const_iterator fiter;
    for(fiter = forces.begin(); fiter != forces.end(); fiter++)
    {
        std::vector<TiXmlElement *> params = ti_requiredChildren(*fiter,"parameter");
        std::vector<TiXmlElement *>::const_iterator piter;
        for(piter = params.begin(); piter != params.end(); piter++)
        {
            std::string profType = ti_attributeValue((*piter),"type");
            if(profType == "percentile" || profType == "fixed")
            {
                WriteOneProfileSpread(filePrefix,*fiter,*piter);
            }
        }
    }
}

//____________________________________________________________________________________
