/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/


#include "MvTable.h"


// ---------------------------------------------------------------------------
// MvTable::MvTable
// constructors
// ---------------------------------------------------------------------------

MvTable::MvTable() :
    numColumns_(0),
    doubleMissingValue_(-9999.0),
    stringMissingValue_("")
{
}


// ---------------------------------------------------------------------------
// MvTable::setReaderParameters
// returns s pointer to the column with the given name (or NULL if not there)
// ---------------------------------------------------------------------------

bool MvTable::setReaderParameters(request* r)
{
    // get the parameters, one by one
    // if we get the request from mtable.cc, then we are guaranteed that the
    // parameter names will be upper case


    const char* value = get_value(r, "PATH", 0);
    if (value)
        reader_.setPath(value);


    value = get_value(r, "TABLE_DELIMITER", 0);
    if (value)
        reader_.setDelimiter(value[0]);


    value = get_value(r, "TABLE_COMBINE_DELIMITERS", 0);
    if (value) {
        if (strlen(value) == 2 && toupper(value[0]) == 'O' && toupper(value[1]) == 'N')
            reader_.setConsecutiveDelimitersAsOne(true);
        else
            reader_.setConsecutiveDelimitersAsOne(false);
    }

    value = get_value(r, "TABLE_HEADER_ROW", 0);
    if (value)
        reader_.setHeaderRow(atoi(value));


    value = get_value(r, "TABLE_DATA_ROW_OFFSET", 0);
    if (value)
        reader_.setDataRowOffset(atoi(value));


    // has the user specified a set of rows which contain meta-data?

    int num_user_meta_data_rows = count_values(r, "TABLE_META_DATA_ROWS");

    if (num_user_meta_data_rows > 0) {
        std::vector<int> mrows;

        for (int i = 0; i < num_user_meta_data_rows; i++) {
            int index = atoi(get_value(r, "TABLE_META_DATA_ROWS", i));
            mrows.push_back(index);  // user gives 1-base indexes, but we use 0-based internally
        }
        reader_.setUserMetaDataRows(mrows);
    }


    // has the user specified a particular subset of columns?

    int num_col_indexes = count_values(r, "TABLE_COLUMNS");
    int num_col_types = count_values(r, "TABLE_COLUMN_TYPES");

    if (num_col_indexes > 0) {
        if (num_col_types > 0 && num_col_types != num_col_indexes) {
            marslog(LOG_EROR, "Table column indexes (%d) and types (%d) must have same number of elements if both are specified.", num_col_indexes, num_col_types);
            return false;
        }

        userColumnIndexes.clear();

        for (int i = 0; i < num_col_indexes; i++) {
            int index = atoi(get_value(r, "TABLE_COLUMNS", i));
            userColumnIndexes.push_back(index - 1);  // user gives 1-base indexes, but we use 0-based internally
        }
    }


    // has the user specified a particular set of column types?

    if (num_col_types > 0) {
        userColumnTypes.clear();

        for (int i = 0; i < num_col_types; i++) {
            TableReader::eTableReaderFieldType type = !strcasecmp(get_value(r, "TABLE_COLUMN_TYPES", i), "STRING") ? TableReader::TABFIELD_STRING : TableReader::TABFIELD_NUMBER;
            userColumnTypes.push_back(type);
        }
    }


    return true;
}


/* // not needed?
// takes a path as its argument
MvTable::MvTable (const char *name)
{
    MvTable::MvTable();

    std::string path(name);      // give the path to the file reader object
    reader_.setPath(path);
}
*/


// ---------------------------------------------------------------------------
// MvTable::column
// returnss pointer to the column with the given name (or NULL if not there)
// ---------------------------------------------------------------------------
MvTableColumn* MvTable::column(std::string& name)
{
    // find which column has the name we're looking for

    for (auto& column : columns_) {
        if (column.name() == name)
            return &column;  // found it - return a pointer to it
    }


    // if we got to here, then we did not find the column

    return nullptr;
}


// ---------------------------------------------------------------------------
// MvTable::read
// reads the current table using the current settings
// ---------------------------------------------------------------------------

bool MvTable::read()
{
    std::string errorMsg;
    bool ret;
    std::vector<int> allColumnIndexes;
    std::vector<int>* columnIndexes;
    std::vector<TableReader::eTableReaderFieldType> allTypes;


    // get the meta-data so we know what fields are there

    // reader_.setHeaderRow(false);
    // reader_.setDelimiter(' ');
    // reader_.setSkipRows(5);
    // reader_.setConsecutiveDelimitersAsOne(true);

    // reader_.setHeaderRow(true);
    // reader_.setDelimiter(',');
    ret = reader_.getMetaData(errorMsg);

    if (!ret) {
        marslog(LOG_EROR, "%s", errorMsg.c_str());
        return false;
    }


    // what are the column types and names?

    std::vector<TableReader::eTableReaderFieldType>& types = reader_.fieldTypes();
    std::vector<std::string>& colNames = reader_.fieldNames();


    // if the user has not specified a subset of columns, then we just generate
    // a vector containing all the column indexes that are in the file

    if (userColumnIndexes.size() == 0) {
        allColumnIndexes.clear();

        for (size_t i = 0; i < types.size(); i++)
            allColumnIndexes.push_back(i);

        columnIndexes = &allColumnIndexes;  // this is the one we will use
    }
    else {
        columnIndexes = &userColumnIndexes;  // otherwise, use what the user specified
    }


    // if the user specified the types of certain columns, then generate a vector
    // containing types for all columns, but with the user's types over-riding
    // the automatic ones

    allTypes = types;  // create a copy of the automatic type vector

    for (size_t i = 0; i < userColumnTypes.size(); i++) {
        // which index does this correspond to?

        int index = (userColumnIndexes.size() != 0) ? userColumnIndexes[i] : i;

        allTypes[index] = userColumnTypes[i];
    }


    numColumns_ = columnIndexes->size();
    columns_.resize(numColumns_);


    // for each column, tell the table reader where to put the data

    for (int colnum = 0; colnum < numColumns_; colnum++) {
        int index = (*columnIndexes)[colnum];
        MvTableColumn& col = columns_[colnum];


        if (index < 0 || (size_t)index >= types.size()) {
            marslog(LOG_EROR, "Column index %d is outside the range of columns (%d) in the file.", index + 1, types.size() + 1);
            return false;
        }


        if (allTypes[index] == TableReader::TABFIELD_NUMBER) {
            reader_.setFieldContainer(index, col.name(), col.dVals(), doubleMissingValue());
            col.setType(MvTableColumn::COL_NUMBER);
        }
        else {
            reader_.setFieldContainer(index, col.name(), col.sVals(), stringMissingValue());
            col.setType(MvTableColumn::COL_STRING);
        }

        if (!colNames[colnum].empty())
            col.setName(colNames[colnum]);
    }


    // store the user meta-data

    userMetaData_ = reader_.userMetaData();


    // read the data from the file
    ret = reader_.read(errorMsg);


    if (!ret) {
        marslog(LOG_EROR, "%s", errorMsg.c_str());
        return false;
    }


    return ret;
}
