// $Id: ConverterUIMain.cpp,v 1.13 2018/01/03 21:32:56 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: Patrick Colacurcio, Peter Beerli, Mary Kuhner, Jon Yamato 
/* Authors: Patrick Colacurcio, Peter Beerli, Mary Kuhner, Jon Yamato 
and Joseph Felsenstein */


// This file contains the UI and the main() function for the converter program

// extensive revisions to allow UI and filereading to interact.
// Jon 2002/03/21 - 2002/?

#include <cassert>
#include <iostream>
#include <fstream>
#include <map>
#include <algorithm>

#include "Converter_ConverterUI.h"
#include "Converter_PhylipConverter.h"
#include "Converter_MigrateConverter.h"
#include "Converter_LamarcDS.h"
#include "Converter_DataSourceException.h"
#include "Converter_SpacingDS.h"
#include "Converter_HapConverter.h"
#include "Converter_UserFileUtil.h"
#include "Converter_SpaceConverter.h"
#include "random.h"
#include "constants.h" // for #define MENU
                       //     for datatype const strings (DNA, SNP, etc)
#include "stringx.h"   // for FromString() and IsInteger()
#include "Converter_types.h" // for map typedefs

#include "nomenuglobals.h" // to handle nomenu case

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

// THE MAIN FUNCTION
int main()
{
    try                                 // begin a big try-catch block
    {
#ifndef JSIM
        cout << "****************************************************************" << endl;
        cout << "****************************************************************" << endl;
        cout << " WARNING: PROGRAM DEPRECATED" << endl;
        cout << "****************************************************************" << endl;
        cout << "****************************************************************" << endl;
        cout << " This program, old_lam_conv, is no longer being maintained." << endl;
        cout << endl;
        cout << " To convert your Phylip and Migrate format files to the" << endl;
        cout << " lamarc XML format you should now be using lam_conv" << endl;
        cout << " or batch_lam_conv." << endl;
        cout << endl;
        cout << " If these programs do not work for you please send email to " << endl;
        cout << " lamarc@gs.washington.edu and describe the problem. " << endl;
        cout << "****************************************************************" << endl;
        cout << "****************************************************************" << endl;
#else
        // read the nomenu file if it exists
        std::ifstream nomenuinfile;
        nomenuinfile.open(convstr::JSIMPARMFILE.c_str());
        nomenuinfile >> convstr::GENETICDATAFILENAME;
        nomenuinfile >> convstr::OUTFILENAME;
        nomenuinfile >> convstr::HAPFILENAME;
        nomenuinfile >> convstr::MAPFILENAME;
        nomenuinfile >> convstr::GENETICDATATYPE;
        nomenuinfile >> convstr::GENETICDATAFORMAT;
        nomenuinfile >> convstr::GENETICDATAINTERLEAVED;
        nomenuinfile >> convstr::REGIONLENGTH;
        nomenuinfile >> convstr::REGIONOFFSET;
        nomenuinfile >> convstr::LOCUSMAPPOS;
        nomenuinfile >> convstr::MICROREGIONS;
        nomenuinfile.close();
#endif

        LamarcDS *dataStore = new LamarcDS;
        bool keepGoing = true;
        long numblocks = 0;

        //  UsePopRegionRelation();

        string mapfilename = GetMapFileName();
        cout << endl << "Begin processing of your genetic data files" << endl;

        while(keepGoing)
        {
            string fileName;
            bool interleaved;
            string buffer;
            string format;
            bool success = false;

            // Get the file type.
#ifndef JSIM
            format = getFormat();
#else
            format = convstr::GENETICDATAFORMAT;
            interleaved = convstr::GENETICDATAINTERLEAVED;
#endif

            if ( format == "Migrate" )
            {
                LamarcDS* emptyDS = new LamarcDS;  // For merging correctly...
                bool isError = false;

#ifndef JSIM
                interleaved = isInterleaved();
                UserFileUtil fileutil;
                fileName = getFileName(fileutil);

                cout << "FileName: " << fileName << endl;
                cout << endl;
#else
                fileName = convstr::GENETICDATAFILENAME;
#endif

                // Now create the converter
                try
                {
                    string datatype;
                    MigrateConverter converter(fileName,
                                               interleaved);

                    datatype = converter.GetDataType();
                    if (datatype == "")
                    {
                        datatype = getDataType();
                        converter.SetDataType(datatype);
                    }
                    if (datatype == lamarcstrings::MICROSAT || datatype == lamarcstrings::KALLELE)
                    {
                        converter.SetMarkersToRegions(AreMicrosRegions());
                    }
                    converter.ProcessData();

                    // Get the info out of it.
                    (*emptyDS).mergeTo(*dataStore);
                    converter.addConvertedLamarcDS(*emptyDS);

                }
                catch (ConverterBaseError& e)
                {
                    cout << endl << "An error was found while processing your file.  Here it is:" << endl;
                    cout << e.type() << ": " << e.what() << endl;
                    cout << "Your file has not been added." << endl << endl;
                    isError = true;
                    delete emptyDS;
                }

                if (!isError)
                {
                    // If everything merged okay, set the datastore to the previously empty DS.
                    LamarcDS *temp = dataStore;
                    dataStore = emptyDS;
                    delete temp;
                }
            }

            else if ( format == "Phylip")
            {
#ifndef JSIM
                //  Find if it's interleaved
                interleaved = isInterleaved();

                // finally, get the file name
                UserFileUtil fileutil;
                fileName = getFileName(fileutil);

#else
                fileName = convstr::GENETICDATAFILENAME;
#endif

                success = false;

#ifndef JSIM
                cout << "FileName: " << fileName << endl;
                cout << endl;
#endif

                LamarcDS *emptyDS = new LamarcDS;  // for correct merging.
                bool isError = false;

                // Now create the converter
                try
                {
                    PhylipConverter converter(fileName,
                                              interleaved);

                    string datatype = getDataType();
                    converter.SetDataType(datatype);
                    converter.ProcessData();

                    // Get the info out of it.
                    (*emptyDS).mergeTo(*dataStore);
                    converter.addConvertedLamarcDS(*emptyDS);
                    //      cin.clear();
                    //      cin.ignore(INT_MAX, '\n');

                }
                catch (ConverterBaseError& e)
                {
                    cout << endl << "An error was found while processing your file.  Here it is:" << endl;
                    cout << e.type() << ": " << e.what() << endl;
                    cout << "Your file has not been added." << endl << endl;
                    isError = true;
                    delete emptyDS;
                }
                if (!isError)
                {
                    // If everything merged okay, set the datastore to the previously empty DS.
                    LamarcDS *temp = dataStore;
                    dataStore = emptyDS;
                    delete temp;
                }
            }

            // Show the number of datablocks (units by regions) the datastore
            // contains.
            numblocks = dataStore->GetNUnits();
#ifndef JSIM
            if (numblocks == 1)
                cout << "\nFile now contains " << numblocks << " block\n" << endl;
            else
                cout << "\nFile now contains " << numblocks << " blocks\n" << endl;
#endif

#ifndef JSIM
            //  Another File
            while (!success)
            {
                cout << "Would you like to add another file? y/(n) :";
                getline(cin, buffer);

                if (ciStringEqual(buffer, string("y")))
                {
                    success = true;
                }
                else if (buffer == "" || ciStringEqual(buffer, string("n")))
                {
                    keepGoing = false;
                    success = true;
                }
                else
                {
                    cout << "You Entered: " << buffer << endl;
                    cout << "Enter either 'y' or 'n'." << endl;
                    buffer = "";
                }
            }
#else
            keepGoing = false;
            success = true;
#endif

            //      cout << "Here it is: " << endl << dataStore.getXML(0) << endl;
        }

        // Display the data from the file(s) and allow the user to (re-)group
        // them using the "Unit" paradigm.

#ifndef JSIM
        // first we need to convert the datastore to units and then add them
        // to the PopRegionRelation.

        if (numblocks < 1)
        {
            cout << "Since no data blocks have been successfully entered," << endl;
            cout << "the file converter gives up; it can't create an XML file without data." << endl;
            return 0;
        }

        PopRegionRelation table;
        long nregions = 0;
        RegionMap::iterator region;
        for(region = dataStore->getFirstRegion();
            region != dataStore->getLastRegion(); ++region)
        {
            string filename(region->first);
            unsigned long index = filename.rfind(string("&&---&&"));
            filename  = filename.substr(0,index);
            index = filename.find_last_of(string("/\\"));
            if (index != filename.length())
            {
                filename = filename.substr(index+1,filename.length());
            }

            ++nregions;
            PopMap::iterator pop;
            for(pop = region->second.getFirstPopulation();
                pop != region->second.getLastPopulation(); ++pop)
            {
                if (pop->second.IsGhost()) continue;
                string datatype = pop->second.getFirstIndividual()->
                    getDataType();
                // EWFIX.P5 REFACTOR -- same code appears in GCRegion.cpp
                char regionNameBuffer[13];
                if(sprintf(regionNameBuffer,"region_%05ld",nregions)!=12)
                {
                    assert(false);
                }
                string regionName(regionNameBuffer);
                // EWFIX.P5  -- note that using regionName is overwriting a previous name
                // for the region, which was set as the file unit name
                // but I DON'T KNOW WHY. Argh!
                Unit unit(nregions,regionName,
                          pop->first,pop->second.GetNumberOfOTUs(),
                          pop->second.getSequenceLength(),
                          filename, datatype, pop);
                table.AddUnit(unit);
            }
        }

        // now allow the user to interact with data
        do {
            table.DisplayPopRegionRelation();
            table.ChangeRegions();
            table.ChangePopulations();
        } while (!table.OverallValid());
        cout << "\nFinal result\n";
        table.DisplayPopRegionRelation();

        // now take the table results and write them back out to the datastore.
        try
        {
            dataStore->ReorderUsing(table.GetRegionalByPopMap());
        }
        catch(InvalidSequenceLengthError& e)
        {
            cout << endl << "An error was found while processing your";
            cout << " assignment of data to regions and populations.  Here";
            cout << " it is:" << endl << e.type() << ": " << e.what() << endl;
            exit(0);
        }
#endif

        try
        {
            // Now do haplotype processing, region by region, incorporating
            // user individual information
            SetHapInfo(*dataStore);

            // Now do the spacing parsing
            SetMapInfo(*dataStore,mapfilename);
        }
        catch (ConverterBaseError& e)
        {
            cout << endl << "An error was found while processing your";
            cout << " haplotype and/or mapping assignments:\n";
            cout << e.type() << ": " << e.what() << endl;
            exit(0);
        }

        string outFileName;
        keepGoing = true;
        bool needFileName = true;

#ifdef JSIM
        needFileName = false;
        outFileName = "outfile";
#endif
        int count=0;
        while (needFileName && count < 5)
        {
            count++;
            cout << "Enter the desired output file name: ";
            getline(cin, outFileName);

            //  Check to see if the file currently exists
            ifstream inFile( outFileName.c_str(), ios::in );
            if (inFile)
            {
                cout << "The file \"" << outFileName << "\" already ";
                cout << "exists." << endl << "Writing to this file will ";
                cout << "destroy its current contents." << endl;

                string query =  "Are you sure you want to overwrite this file?";

                needFileName = !(GetYesOrNo(query,false));
                inFile.close();
            }
            else
            {
                needFileName = false;
            }
            //  Okay, we have the file name
            //  Write the file.
            if (!needFileName)
            {
                ofstream outFile( outFileName.c_str(), ios::out );
                if (!outFile)
                {
                    cout << "Cannot open \"" << outFileName << "\" for output." << endl;
                    needFileName = true;
                }
                else
                {
                    outFile << (*dataStore).getXML(0) << endl;
                    cout << "Data Written." << endl;
                }
            }
            if (count >= 5 && needFileName)
                cout << "Giving up.  Check your rights for file writing in this "
                     << "directory and/or for the files you wish to write, and "
                     << "re-run the converter." << endl;
        }

#ifdef JSIM
        //The file name was read in earlier.
        ofstream outFile( outFileName.c_str(), ios::out );
        if (!outFile)
        {
            cerr << "Cannot open \"" << outFileName << "\" for output." << endl;
        }
        else
            outFile << (*dataStore).getXML(0) << endl;
#endif

        delete dataStore;

        return 0;
    } // end the big try-catch block
    catch (ConverterBaseError& e)
    {
        cout << endl << "Here is an uncaught error: " << e.type() << ": ";
        cout << e.what() << endl;
        exit(0);
    }
    catch (...)
    {
        cout << endl << "A system/compiler/linker error occured!" << endl;
        exit(0);
    }
}

//____________________________________________________________________________________
