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

 Copyright 2017 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 "MvBufrFilter.h"

#include <assert.h>

#include "MvKeyProfile.h"

bool BufrFilterValueCondition::update(const std::string& key, double value)
{
    if (key == key_) {
        match_ = false;
        for (std::size_t i = 0; i < values_.size(); i++)
            if (values_[i] == value) {
                match_ = true;
                return true;
            }
        return true;
    }
    return false;
}

bool BufrFilterNotValueCondition::update(const std::string& key, double value)
{
    if (BufrFilterValueCondition::update(key, value)) {
        match_ = !match_;
        return true;
    }
    return false;
}

bool BufrFilterRangeCondition::update(const std::string& key, double value)
{
    if (key == key_) {
        match_ = (value >= start_ && value <= end_);
        return true;
    }
    return false;
}

bool BufrFilterNotRangeCondition::update(const std::string& key, double value)
{
    if (BufrFilterRangeCondition::update(key, value)) {
        match_ = !match_;
        return true;
    }
    return false;
}

#if 0
bool BufrFilterRankCondition::update(const std::string& key,int rank)
{
    if(key == key_)
    {
        match_=false;
        for(std::size_t i=0; i < ranks_.size(); i++)
            if(values_[i] == value)
            {
                match_=true;
                return true;
            }
        return true;
    }
    return false;
}
#endif


BufrFilterConditionGroup::~BufrFilterConditionGroup()
{
    for (std::size_t i = 0; i < cond_.size(); i++)
        delete cond_[i];
}

//Takes ownership of the condition
void BufrFilterConditionGroup::addCondition(BufrFilterCondition* cond)
{
    assert(cond);
    cond_.push_back(cond);
}

void BufrFilterConditionGroup::add(const std::string& key, double value)
{
    for (std::size_t i = 0; i < cond_.size(); i++) {
        if (cond_[i]->update(key, value)) {
            updateMatchStatus();
            return;
        }
    }
}

void BufrFilterConditionGroup::updateMatchStatus()
{
    bool all = true;
    for (std::size_t i = 0; i < cond_.size(); i++) {
        if (!cond_[i]->match()) {
            all = false;
            break;
        }
    }
    allMatch_ = all;
}

BufrFilterExtract::BufrFilterExtract(std::vector<BufrParam*> pars)
{
    for (std::size_t i = 0; i < pars.size(); i++) {
        items_[pars[i]->key()] = pars[i];
    }
}

bool BufrFilterExtract::isValueSet(const std::string& key) const
{
    std::map<std::string, BufrParam*>::const_iterator it = items_.find(key);
    if (it != items_.end())
        return it->second->isValueSet();

    return false;
}

void BufrFilterExtract::setValue(const std::string& key, double val)
{
    std::map<std::string, BufrParam*>::const_iterator it = items_.find(key);
    if (it != items_.end())
        it->second->setDoubleValue(val);
}

double BufrFilterExtract::value(const std::string& key)
{
    std::map<std::string, BufrParam*>::const_iterator it = items_.find(key);
    if (it != items_.end())
        return it->second->doubleValue();
    return -9999;
}

void BufrFilterExtract::reset()
{
    for (std::map<std::string, BufrParam*>::iterator it = items_.begin(); it != items_.end(); ++it) {
        it->second->resetValue();
    }
}

bool BufrFilterExtract::isComplete() const
{
    for (std::map<std::string, BufrParam*>::const_iterator it = items_.begin(); it != items_.end(); ++it) {
        if (!it->second->isValueSet())
            return false;
    }
    return true;
}

bool BufrFilterExtract::isEmpty() const
{
    for (std::map<std::string, BufrParam*>::const_iterator it = items_.begin(); it != items_.end(); ++it) {
        if (it->second->isValueSet())
            return false;
    }
    return true;
}

MvBufrFilter::MvBufrFilter() :
    extract_(0),
    result_(0)
{
    std::vector<BufrParam*> pars;
    pars.push_back(new DoubleBufrParam("airTemperature"));
    extract_ = new BufrFilterExtract(pars);

    std::vector<double> vals;
    vals.push_back(70000);
    BufrFilterCondition* cond = new BufrFilterValueCondition("pressure", vals);
    coordCondition_.addCondition(cond);

    result_    = new MvKeyProfile("bufr");
    MvKey* mvk = new MvKey("index", "index");
    mvk->setValueType(MvKey::IntType);
    result_->addKey(mvk);

    mvk = new MvKey("pressure", "pressure");
    mvk->setValueType(MvKey::DoubleType);
    result_->addKey(mvk);

    mvk = new MvKey("airTemperature", "airTemperature");
    mvk->setValueType(MvKey::DoubleType);
    result_->addKey(mvk);
}

void MvBufrFilter::filter(MvObs& obs)
{
    //-- coordinate interval start index in the msg
    int startIndex = 0;

    //-- (implicitly) force descriptors to be expanded
    obs.setFirstDescriptor();

    //std::size_t keyNum=obs.keyNum();

    while (obs.setNextDescriptor()) {
        //obs.setNextDescriptor();

        //The keyname is
        std::string keyName = MvObs::keyWithoutOccurrenceTag(obs.currentKey());
        double keyValue     = obs.currentValue();

        //Check conditions
        //dateOption_.add(keyName,keyValue);
        //identOption_.add(keyName,keyValue);
        //locationOption_.add(keyName,keyValue);
        //latitudeValue
        coordCondition_.add(keyName, keyValue);

        //customOption_.add(keyName,keyValue);

        //extractValues_.add(keyName,keyValue);

        //All conditions match
        //if(dateOption_.matches() && identOption_.matches() && locationOption_.matches() &&
        //    coordOption_.matches() && customOption_.matches())
        if (coordCondition_.match()) {
            if (extract_->isComplete()) {
                addToResult();
            }
            extract_->setValue(keyName, keyValue);
        }
        else {
            if (!extract_->isEmpty())
                addToResult();
        }

        //cont = obs.setNextDescriptor();
    }
}

void MvBufrFilter::addToResult()
{
    assert(result_);
    for (std::size_t i = 0; i < result_->size(); i++) {
        MvKey* rk = result_->at(i);
        assert(rk);
        std::string name = rk->name();
        if (name == "index")
            rk->addIntValue(1);
        else if (name == "subset")
            rk->addIntValue(1);
        else if (name == "date")
            rk->addIntValue(1);
        else if (name == "time")
            rk->addIntValue(1);
        else if (name == "level")
            rk->addIntValue(1);
        else {
            rk->addDoubleValue(extract_->value(name));
        }
    }

    extract_->reset();
}
