/***************************** 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 <iostream>
#include <sstream>
#include <fstream>
#include <cstdio>
#include <assert.h>

#include "MvKeyProfile.h"
#include "MvVariant.h"
#include "MvMiscelaneous.h"

//-----------------------------------
// MvKey
//-----------------------------------

MvKey::MvKey() :
    name_(""),
    shortName_(""),
    description_(""),
    role_(NoRole),
    keyType_(NormalKey),
    precision_(0),
    valueType_(StringType),
    constant_(false),
    intMissingVal_(0),
    longMissingVal_(0),
    floatMissingVal_(0.),
    doubleMissingVal_(0.),
    missingValDefined_(false),
    readIntAsString_(true),
    editable_(true)
{
}

MvKey::MvKey(const string& n, const string& sn, string d) :
    name_(n),
    shortName_(sn),
    description_(d),
    role_(NoRole),
    keyType_(NormalKey),
    precision_(0),
    valueType_(StringType),
    constant_(false),
    intMissingVal_(0),
    longMissingVal_(0),
    floatMissingVal_(0.),
    doubleMissingVal_(0.),
    missingValDefined_(false),
    readIntAsString_(true),
    editable_(true)
{
}

MvKey::MvKey(const MvKey& o)
{
    name_              = o.name();
    shortName_         = o.shortName();
    description_       = o.description();
    role_              = o.role_;
    keyType_           = o.keyType_,
    precision_         = o.precision_;
    valueType_         = o.valueType_;
    constant_          = o.constant_;
    metaData_          = o.metaData();
    intMissingVal_     = o.intMissingVal_;
    longMissingVal_    = o.longMissingVal_;
    floatMissingVal_   = o.floatMissingVal_;
    doubleMissingVal_  = o.doubleMissingVal_;
    missingValDefined_ = o.missingValDefined_;
    readIntAsString_   = o.readIntAsString();
    editable_          = o.editable();
}

MvKey* MvKey::clone()
{
    MvKey* cp = new MvKey(*this);
    return cp;
}

void MvKey::setValueType(ValueType t, bool useMissingValue)
{
    if (valueType_ != t) {
        int n = valueNum();
        clearData();

        valueType_ = t;
        if (t == LongType) {
            long val=(useMissingValue)?longMissingVal_:0;
            longVal_ = std::vector<long>(n, val);
        }
        else if (t == IntType) {
            int val=(useMissingValue)?intMissingVal_:0;
            intVal_ = std::vector<int>(n, val);
        }
        else if (t == DoubleType) {
            double val=(useMissingValue)?doubleMissingVal_:0.;
            doubleVal_ = std::vector<double>(n, val);
        }
        else if (t == FloatType) {
            float val=(useMissingValue)?floatMissingVal_:0.;
            floatVal_ = std::vector<float>(n, val);
        }
        else if (t == StringType) {
            stringVal_ = std::vector<std::string>(n);
        }
    }
}

void MvKey::addStringValue(const string& v)
{
    assert(valueType_ == StringType);
    stringVal_.push_back(v);
}

void MvKey::setStringValue(int idx, const string& v)
{
    assert(valueType_ == StringType);
    if (idx >= 0 && idx < static_cast<int>(stringVal_.size())) {
        stringVal_[idx] = v;
    }
}

std::string MvKey::valueAsString(int idx) const
{
    if (valueType_ == StringType) {
        assert(idx >= 0 && idx < static_cast<int>(stringVal_.size()));
        if (constant_)
            return stringConstantValue();
        else
            return stringVal_[idx];
    }
    else {
        if (valueType_ == LongType) {
            assert(idx >= 0 && idx < static_cast<int>(longVal_.size()));
            return metview::toString((constant_ ? longConstantValue() : longVal_[idx]));
        }
        else if (valueType_ == IntType) {
            assert(idx >= 0 && idx < static_cast<int>(intVal_.size()));
            return metview::toString((constant_ ? intConstantValue() : intVal_[idx]));
        }
        else if (valueType_ == DoubleType) {
            assert(idx >= 0 && idx < static_cast<int>(doubleVal_.size()));
            return metview::toString((constant_ ? doubleConstantValue() : doubleVal_[idx]));
        }
        /*else if(valueType_ == FloatType)
        {
            if(idx >= 0 && idx < static_cast<int>(floatVal_.size()));
            return metview::toString((constant_?floatConstantValue():floatVal_[idx]));
        }*/
    }


    static std::string es;
    return es;
}

const std::string& MvKey::stringConstantValue() const
{
    if (constant_) {
        if (stringVal_.size() > 0)
            return stringVal_[0];
    }
    static std::string es;
    return es;
}

void MvKey::addIntValue(int v)
{
    assert(valueType_ == IntType);
    intVal_.push_back(v);
}

void MvKey::setIntValue(int idx, int v)
{
    assert(valueType_ == IntType);
    if (idx >= 0 && idx < static_cast<int>(intVal_.size())) {
        intVal_[idx] = v;
    }
}

int MvKey::intConstantValue() const
{
    if (constant_) {
        if (intVal_.size() > 0)
            return intVal_[0];
    }
    return -9999999;
}

void MvKey::setIntRange(int v1, int v2)
{
    if (constant_)
        constant_ = false;

    intVal_.clear();
    for (int i = v1; i <= v2; i++)
        intVal_.push_back(i);
}

void MvKey::addLongValue(long v)
{
    assert(valueType_ == LongType);
    longVal_.push_back(v);
}

void MvKey::setLongValue(int idx, long v)
{
    assert(valueType_ == LongType);
    if (idx >= 0 && idx < static_cast<int>(longVal_.size())) {
        longVal_[idx] = v;
    }
}

long MvKey::longConstantValue() const
{
    if (constant_) {
        if (longVal_.size() > 0)
            return longVal_[0];
    }
    return -9999999;
}

void MvKey::setLongRange(long v1, long v2)
{
    if (constant_)
        constant_ = false;

    longVal_.clear();
    for (long i = v1; i <= v2; i++)
        longVal_.push_back(i);
}

void MvKey::addDoubleValue(double v)
{
    doubleVal_.push_back(v);
}

void MvKey::setDoubleValue(int idx, double v)
{
    if (idx >= 0 && idx < static_cast<int>(doubleVal_.size())) {
        doubleVal_[idx] = v;
    }
}

double MvKey::doubleConstantValue() const
{
    if (constant_) {
        if (doubleVal_.size() > 0)
            return doubleVal_[0];
    }
    return -9999999.;
}

void MvKey::addValue(const MvVariant& var)
{
    if (valueType_ == IntType) {
        addIntValue(var.toInt());
    }
    if (valueType_ == LongType) {
        addLongValue(var.toLong());
    }
    else if (valueType_ == FloatType) {
        addDoubleValue(var.toDouble());
    }
    else if (valueType_ == DoubleType) {
        addDoubleValue(var.toDouble());
    }
    else if (valueType_ == StringType) {
        addStringValue(var.toString());
    }
    else {
        addDoubleValue(var.toDouble());
    }
}

void MvKey::setValue(int chunkStart, int chunkSize, MvKey* key)
{
    int chunkEnd = chunkStart + chunkSize;
    assert(!constant_);
    if (constant_)
        return;

    if (valueType_ == IntType) {
        for (int i = chunkStart; i < chunkEnd; i++)
            intVal_[i] = key->intVal_[i - chunkStart];
    }
    if (valueType_ == LongType) {
        for (int i = chunkStart; i < chunkEnd; i++)
            longVal_[i] = key->longVal_[i - chunkStart];
    }
    else if (valueType_ == FloatType) {
        for (int i = chunkStart; i < chunkEnd; i++)
            floatVal_[i] = key->floatVal_[i - chunkStart];
    }
    else if (valueType_ == DoubleType) {
        for (int i = chunkStart; i < chunkEnd; i++)
            doubleVal_[i] = key->doubleVal_[i - chunkStart];
    }
    else if (valueType_ == StringType) {
        for (int i = chunkStart; i < chunkEnd; i++)
            stringVal_[i] = key->stringVal_[i - chunkStart];
    }
}

void MvKey::clearData()
{
    stringVal_.clear();
    intVal_.clear();
    longVal_.clear();
    floatVal_.clear();
    doubleVal_.clear();
}

const string& MvKey::metaData(const string& key) const
{
    static string es;
    map<string, string>::const_iterator it = metaData_.find(key);
    return (it != metaData_.end()) ? it->second : es;
}

void MvKey::setMetaData(const string& key, const string& value)
{
    metaData_[key] = value;
}


void MvKey::clearMetaData()
{
    metaData_.clear();
}

int MvKey::valueNum() const
{
    if (valueType_ == StringType) {
        return static_cast<int>(stringVal_.size());
    }
    else if (valueType_ == IntType) {
        return static_cast<int>(intVal_.size());
    }
    else if (valueType_ == LongType) {
        return static_cast<int>(longVal_.size());
    }
    else if (valueType_ == FloatType) {
        return static_cast<int>(floatVal_.size());
    }
    else if (valueType_ == DoubleType) {
        return static_cast<int>(doubleVal_.size());
    }

    return 0;
}

void MvKey::preAllocate(int num)
{
    assert(num > 0);

    if (valueType_ == StringType) {
        stringVal_ = std::vector<std::string>(num);
    }
    else if (valueType_ == IntType) {
        intVal_ = std::vector<int>(num, 0);
    }
    else if (valueType_ == LongType) {
        longVal_ = std::vector<long>(num, 0);
    }
    else if (valueType_ == FloatType) {
        floatVal_ = std::vector<float>(num, 0.);
    }
    else if (valueType_ == DoubleType) {
        doubleVal_ = std::vector<double>(num, 0.);
    }
}

bool MvKey::operator==(const MvKey& o) const
{
    return name_ == o.name_ &&
           shortName_ == o.shortName_ &&
           description_ == o.description_;
}

bool MvKey::operator!=(const MvKey& o) const
{
    return !(o == *this);
}


//-----------------------------------
// MvKeyProfile
//-----------------------------------

MvKeyProfile::MvKeyProfile(const MvKeyProfile& copy)
{
    name_          = copy.name_;
    systemProfile_ = copy.systemProfile_;
    for (vector<MvKey*>::const_iterator it = copy.begin(); it != copy.end(); it++) {
        MvKey* key = (*it)->clone();
        push_back(key);
    }
    errorRows_ = copy.errorRows_;
}

MvKeyProfile::~MvKeyProfile()
{
    clear();
}

MvKeyProfile* MvKeyProfile::clone()
{
    MvKeyProfile* cp = new MvKeyProfile(*this);
    return cp;
}

void MvKeyProfile::clear()
{
    for (vector<MvKey*>::iterator it = begin(); it != end(); it++) {
        delete (*it);
    }
    vector<MvKey*>::clear();
    errorRows_.clear();
}

void MvKeyProfile::addKey(MvKey* key)
{
    push_back(key);
}

MvKey* MvKeyProfile::addKey()
{
    MvKey* key = new MvKey;
    push_back(key);
    return key;
}

void MvKeyProfile::insertKey(int before, MvKey* key)
{
    if (before >= static_cast<int>(size()))
        push_back(key);
    else if (before <= 0)
        insert(begin(), key);
    else {
        vector<MvKey*>::iterator it = begin() + before;
        insert(it, key);
    }
}

void MvKeyProfile::deleteKey(int index)
{
    vector<MvKey*>::iterator it = begin() + index;
    delete (*it);
    erase(it);
}

void MvKeyProfile::swap(int i, int j)
{
    std::swap(at(i), at(j));
}

void MvKeyProfile::reposition(int indexFrom, int indexTo)
{
    if (indexFrom == indexTo)
        return;

    if (indexFrom > indexTo) {
        for (int i = indexFrom; i > indexTo; i--) {
            std::swap(at(i), at(i - 1));
        }
    }
    else {
        for (int i = indexFrom; i < indexTo; i++) {
            std::swap(at(i), at(i + 1));
        }
    }
}

void MvKeyProfile::reposition(const vector<int>& pos)
{
    if (size() != pos.size())
        return;

    //Create a copy
    vector<MvKey*> profCopy;
    for (size_t i = 0; i < size(); i++)
        profCopy.push_back(at(i));

    for (size_t i = 0; i < size(); i++)
        (*this)[i] = profCopy[pos[i]];
}

MvKey* MvKeyProfile::key(string keyName) const
{
    for (vector<MvKey*>::const_iterator it = begin(); it != end(); ++it) {
        if ((*it)->name() == keyName) {
            return *it;
        }
    }
    return 0;
}

MvKey* MvKeyProfile::key(MvKey::Role role) const
{
    for (vector<MvKey*>::const_iterator it = begin(); it != end(); ++it) {
        if ((*it)->role() == role) {
            return *it;
        }
    }
    return 0;
}

MvKey* MvKeyProfile::key(MvKey::Role role, int occurence) const
{
    int cnt = 0;
    for (vector<MvKey*>::const_iterator it = begin(); it != end(); ++it) {
        if ((*it)->role() == role) {
            if (cnt == occurence)
                return *it;
            cnt++;
        }
    }
    return 0;
}


int MvKeyProfile::valueNum(int index)
{
    if (index >= 0 && index < static_cast<int>(size())) {
        return at(index)->value().size();
    }
    return 0;
}

int MvKeyProfile::valueNum() const
{
    std::size_t num = size();
    for (std::size_t i = 0; i < num; i++) {
        MvKey* k = at(i);
        if (!k->isConstant()) {
            return k->valueNum();
        }
    }

    if (size() == 0)
        return 0;

    //everything is constant
    return 1;
}

void MvKeyProfile::clearKeyData()
{
    for (vector<MvKey*>::iterator it = begin(); it != end(); ++it) {
        (*it)->clearData();
    }
    errorRows_.clear();
}

void MvKeyProfile::expand(std::vector<MvKeyProfile*> ref)
{
    for (std::vector<MvKey*>::iterator it = begin(); it != end(); it++) {
        const std::string& name = (*it)->name();
        for (std::vector<MvKeyProfile*>::const_iterator profIt = ref.begin(); profIt != ref.end(); ++profIt) {
            if (MvKey* k = (*profIt)->key(name)) {
                delete *it;
                (*it) = k->clone();
                break;
            }
        }
    }
}

bool MvKeyProfile::isErrorRow(int row) const
{
    size_t n = errorRows_.size();
    for (size_t i = 0; i < n; i++)
        if (errorRows_[i] == row)
            return true;

    return false;
}

void MvKeyProfile::preAllocate(int num)
{
    assert(num >= 0);
    for (size_t i = 0; i < size(); i++) {
        at(i)->preAllocate(num);
    }
}

void MvKeyProfile::setValuesInChunk(int chunkStart, int chunkSize, MvKeyProfile* chunkProf)
{
    assert(chunkProf);
    assert(chunkStart >= 0);
    //assert(chunkStart+chunkSize < )
    assert(size() == chunkProf->size());

    for (size_t i = 0; i < chunkProf->errorRows_.size(); i++) {
        errorRows_.push_back(chunkProf->errorRows_[i]);
    }

    for (size_t i = 0; i < size(); i++) {
        if (at(i)->name() != "MV_Index")
            at(i)->setValue(chunkStart, chunkSize, chunkProf->at(i));
    }
}

const string& MvKeyProfile::metaData(const string& key) const
{
    static string es;
    map<string, string>::const_iterator it = metaData_.find(key);
    return (it != metaData_.end()) ? it->second : es;
}

void MvKeyProfile::setMetaData(const string& key, const string& value)
{
    metaData_[key] = value;
}


void MvKeyProfile::clearMetaData()
{
    metaData_.clear();
}

bool MvKeyProfile::operator==(const MvKeyProfile& o) const
{
    if (o.size() != size())
        return false;

    for (size_t i = 0; i < size(); i++) {
        if (*at(i) != *(o.at(i)))
            return false;
    }

    return true;
}

bool MvKeyProfile::operator!=(const MvKeyProfile& p) const
{
    return !(p == *this);
}
