/***************************** 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
{
    auto it = items_.find(key);
    if (it != items_.end())
        return it->second->isValueSet();

    return false;
}

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

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

void BufrFilterExtract::reset()
{
    for (auto it = items_.begin(); it != items_.end(); ++it) {
        it->second->resetValue();
    }
}

bool BufrFilterExtract::isComplete() const
{
    for (auto it = items_.begin(); it != items_.end(); ++it) {
        if (!it->second->isValueSet())
            return false;
    }
    return true;
}

bool BufrFilterExtract::isEmpty() const
{
    for (auto it = items_.begin(); it != items_.end(); ++it) {
        if (it->second->isValueSet())
            return false;
    }
    return true;
}

MvBufrFilter::MvBufrFilter()
{
    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)
{
    //-- (implicitly) force descriptors to be expanded
    obs.setFirstDescriptor();

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

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

        coordCondition_.add(keyName, keyValue);
        if (coordCondition_.match()) {
            if (extract_->isComplete()) {
                addToResult();
            }
            extract_->setValue(keyName, keyValue);
        }
        else {
            if (!extract_->isEmpty())
                addToResult();
        }
    }
}

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();
}
