//==============================================================
// Name        : PerM
// Author      : Yangho Chen at University of southern California.
// Version     : 0.4.0
// Copyright   : Open source
// Description : C++, Ansi-style
//============================================================================
#include "PairedReadsMapping.h"
#include "ReadsMapping.h"
#include "Genome_Index_TableQ.h"
#include "PairedReadsSet.h"
#include "ReadInBitsSet.h"
#include "ReadInBits.h"
#include "ParameterList.h"
#include "Filename.h"
#include "chdir.h"
#include "stdafx.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;

time_t startt, endt;

bool retriveReadSetsAndSettings\
(ParameterList& P, vector<string>& readSetsList1, vector<string>& readSetsList2)
{
    // (1) check reference
    if (!P.checkRefValidity()) {
        return(false);
    }
    // (2) check reads files
    if (getReadSetsFilenames(P, readSetsList1, readSetsList2)) {
        if ((int)readSetsList1.size() > 0) {
            const char* firstReadSetFileName = readSetsList1.at(0).c_str();
            P.bMappedSOLiDRead = P.bMappedSOLiDRead ||\
                                 is_colorspace_reads(P.readsFileFormat) ||\
                                 is_colorspace_reads(firstReadSetFileName);
            P.cFileFormatSymbol = getReadsFileFormatSymbol(firstReadSetFileName, P.readsFileFormat);
            P.readLength = getReadLength(firstReadSetFileName, P.cFileFormatSymbol);
        } else {
            LOG_INFO("Info %d: Cannot get read set from the read list.\n", ERROR_LOG);
            return(false);
        }
    } else if (atoi(P.readsFile) >= (int)MIN_READ_LENGTH) {
        P.bMakeIndex = true;
        P.bSaveIndex = true;
        P.bMappedSOLiDRead = is_colorspace_reads(P.readsFileFormat);
        P.readLength = atoi(P.readsFile); // Must call P.truncatReadLength() later.
    } else if (atoi(P.readsFile) > 0) {
        LOG_INFO("Info %d: Incorrect reads file or the read length is too short\n", ERROR_LOG);
        return(false);
    } else {
        return(false); // can not open the read file
    }
    P.truncatReadLength(); // truncated reads according to options or mapping long reads
    // (3) select seed according to the setting
    P.seedId = selectSeed(P);
    // (4) avoid to use the same name
    if (readSetsList1.size() > 1)
        P.outputFileN[0] = '\0';
    // (5) TODO remove the mask repeat process
    P.bMaskedMathRepeat \
        = (!P.bMatePairedReads && !P.bMappedLongRead && P.bExcludeAmbiguousReads);

    bool validSetting = true;
    validSetting &= printOptWarning4PairedEndOpts(P);
    validSetting &= printOptWarning4PairedEndOpts(P);
    return(validSetting);
}

string get_Index_Path(ParameterList P)
{
    if (hasTheExtName(P.refFile, ".index") || P.refFormat == "index" ) {
        return(string(P.refFile));
    } else {
        string indexPath = default_index_path(getBasename\
                                              (P.refFile), P.bMappedSOLiDRead, P.seedId, P.readLength);
        return(indexPath);
    }
}

void setQueryIndexSubThreshold(CGenome_Index_TableQ& indexTable, unsigned int subDiffThreshold)
{
    const unsigned int MAX_SUB_WHEN_QUERY_TABLE = 20;
    if (subDiffThreshold > MAX_SUB_WHEN_QUERY_TABLE) {
        indexTable.uiSubDiffThreshold = MAX_SUB_WHEN_QUERY_TABLE;
    } else {
        indexTable.uiSubDiffThreshold = subDiffThreshold;
    }
}

bool buildIndexTable(CGenome_Index_TableQ& indexTable, ParameterList& P)
{
    if (P.refFormat == "index" || hasTheExtName(P.refFile, ".index")) {
        LOG_INFO("\nInfo %d: Index file %s has incorrect format\n", ERROR_LOG, P.refFile);
    } else if (indexTable.getSeqFromFasta(P.refFile, P.refFormat)) {
        bool bMaskedMathRepeat = P.bMaskedMathRepeat && (atoi(P.readsFile) != 0);
        bool bMakeIndexSucessful = indexTable.make_index_table(P.anchorLength, P.seedId, P.bMappedSOLiDRead, bMaskedMathRepeat);
        if (bMakeIndexSucessful) {
            if (P.bSaveIndex) {
                const bool bPrintErrMsg = true;
                indexTable.save_index_table(P.indexFileN, bPrintErrMsg);
            }
            indexTable.bExcludeAmbiguous = P.bExcludeAmbiguousReads;
            setQueryIndexSubThreshold(indexTable, P.subDiffThreshold);
            return(true);
        } else {
            LOG_INFO("\nInfo %d: Faile to build index file %s.\n", ERROR_LOG, P.indexFileN);
        }
    }
    return(false);
}

/*
 * The main function construct or read in the index of a set of reference genome.
 * It maps the reads
 */
int main(int argc, const char* argv[])
{
    // testLongBases2Colors();
    // (1) Get parameters.
    ParameterList P = getParameterList(argc, argv);
    if (argc <= 2) return 0;
    vector<string> readSetsList1, readSetsList2;
    bool validSetting = retriveReadSetsAndSettings(P, readSetsList1, readSetsList2);
    if (validSetting) {
        P.printSetting();
    } else {
        LOG_INFO("\nInfo %d: Invalid or confusing command. Check the opt\n", CONFIG_LOG);
#ifdef WIN32
        STRIKE_KEY2CONTINUE;
#endif
        return(-1);
    }
    // (2) Build or read index table
    CGenome_Index_TableQ indexTable;
    indexTable.uiSubDiffThreshold = (unsigned int)P.subDiffThreshold;
    string indexPath = get_Index_Path(P);
    const bool bPrintWarning = false;
    if (P.bMakeIndex || indexTable.read_index_table(indexPath.c_str(), bPrintWarning) == false) {
        if (buildIndexTable(indexTable, P)  == false ) {
            return(-1);
        }
    }
    // testGenome_Index_TableQ(&indexTable);
    // testMappingLongRead(&indexTable);

    // (3) Mapped reads.
    if ( P.bMatePairedReads && !P.bMappedLongRead ) {
        if (readSetsList1.size() > 0 && (readSetsList1.size() == readSetsList2.size())) {
            TIME_INFO(parallelMappingPairedReads\
                      (readSetsList1, readSetsList2, indexTable, P),"Mapping paired reads\n");
        } else {
            TIME_INFO(parallelMappingPairedReads\
                      (readSetsList1, indexTable, P), "Mapping paired reads");
        }
    } else if (P.bMatePairedReads && P.bMappedLongRead ) {
        TIME_INFO(parallelMappingPairedLongReads(readSetsList1, readSetsList2, indexTable, P),\
                  "Mapped paired-ended long reads");
    } else if ( !P.bMatePairedReads && P.bMappedLongRead ) {
        TIME_INFO(parallelMappingLongReads(readSetsList1, indexTable, P),\
                  "Mapped single-ended long reads");
    } else if ( !P.bMatePairedReads && !P.bMappedLongRead ) {
        TIME_INFO(parallelMapping(readSetsList1, indexTable, P),\
                  "Mapped single-ended reads");
    }
    cout << endl;
    return 0;
}

