/***************************** 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 *************************************/

#pragma once

#include "MvBufrObs.h"

#include <string>
#include <vector>
#include <map>

class MvKeyProfile;

class BufrParam
{
public:
    enum Type
    {
        IntType,
        DoubleType,
        StringType,
        NoType
    };

    const std::string& key() const { return key_; }
    virtual Type type() const = 0;
    virtual void resetValue() = 0;
    bool isValueSet() const { return isSet_; }

    virtual int intValue() const { return 0; }
    virtual double doubleValue() const { return 0.; }
    virtual const std::string& stringValue() const
    {
        static std::string empty;
        return empty;
    }
    virtual void setIntValue(int) {}
    virtual void setDoubleValue(double) {}
    virtual void stringValue(const std::string&) {}

protected:
    BufrParam(const std::string& key) :
        key_(key),
        isSet_(false) {}

    std::string key_;
    bool isSet_;
};

class IntBufrParam : public BufrParam
{
public:
    IntBufrParam(const std::string& key) :
        BufrParam(key),
        value_(-9999) {}
    Type type() const { return IntType; }
    int intValue() const { return value_; }
    void setIntValue(int v)
    {
        value_ = v;
        isSet_ = true;
    }
    void resetValue()
    {
        value_ = -9999;
        isSet_ = false;
    }

protected:
    int value_;
};

class DoubleBufrParam : public BufrParam
{
public:
    DoubleBufrParam(const std::string& key) :
        BufrParam(key),
        value_(-9999.) {}
    Type type() const { return DoubleType; }
    double doubleValue() const { return value_; }
    void setDoubleValue(double v)
    {
        value_ = v;
        isSet_ = true;
    }
    void resetValue()
    {
        value_ = -9999.;
        isSet_ = false;
    }

protected:
    double value_;
};

class StringBufrParam : public BufrParam
{
public:
    StringBufrParam(const std::string& key) :
        BufrParam(key) {}
    Type type() const { return StringType; }
    const std::string& stringValue() const { return value_; }
    void setStringValue(const std::string& v)
    {
        value_ = v;
        isSet_ = true;
    }
    void resetValue()
    {
        value_.clear();
        isSet_ = false;
    }

protected:
    std::string value_;
};

class BufrFilterCondition
{
public:
    BufrFilterCondition(const std::string& key) :
        key_(key),
        match_(false) {}
    virtual ~BufrFilterCondition() {}

    bool match() const { return match_; }
    virtual bool update(const std::string& key, double value) = 0;

protected:
    std::string key_;
    bool match_;
};


class BufrFilterValueCondition : public BufrFilterCondition
{
public:
    BufrFilterValueCondition(const std::string& key, const std::vector<double>& v) :
        BufrFilterCondition(key),
        values_(v) {}
    bool update(const std::string& key, double value);

protected:
    std::vector<double> values_;
};

class BufrFilterNotValueCondition : public BufrFilterValueCondition
{
public:
    BufrFilterNotValueCondition(const std::string& key, const std::vector<double>& v) :
        BufrFilterValueCondition(key, v) {}
    bool update(const std::string& key, double value);
};

class BufrFilterRangeCondition : public BufrFilterCondition
{
public:
    BufrFilterRangeCondition(const std::string& key, double start, double end) :
        BufrFilterCondition(key),
        start_(start),
        end_(end) {}
    bool update(const std::string& key, double value);

protected:
    double start_;
    double end_;
};

class BufrFilterNotRangeCondition : public BufrFilterRangeCondition
{
public:
    BufrFilterNotRangeCondition(const std::string& key, double start, double end) :
        BufrFilterRangeCondition(key, start, end) {}
    bool update(const std::string& key, double value);
};

#if 0
class BufrFilterRankCondition : public BufrFilterCondition
{
public:
  BufrFilterRankCondition(const std::string& key,const std::vector<int>& ranks) :
         BufrFilterCondition(key), ranks_(ranks) {}
  virtual ~BufrFilterRankCondition() {}

  bool match() const {return match_;}
  bool update(const std::string& key)=0;

protected:
  std::string key_;
  bool match_;
  std::vector<int> ranks_;
};
#endif

class BufrFilterConditionGroup
{
public:
    BufrFilterConditionGroup() :
        allMatch_(false) {}
    ~BufrFilterConditionGroup();

    bool match() const { return allMatch_; }
    void addCondition(BufrFilterCondition*);
    void add(const std::string& key, double value);

protected:
    void updateMatchStatus();

    std::vector<BufrFilterCondition*> cond_;
    bool allMatch_;
};

class BufrFilterExtract
{
public:
    BufrFilterExtract(std::vector<BufrParam*>);

    bool isValueSet(const std::string& key) const;
    void setValue(const std::string& key, double val);
    double value(const std::string& key);
    void reset();
    bool isComplete() const;
    bool isEmpty() const;

    std::map<std::string, BufrParam*> items_;
    std::vector<double> values_;
    std::vector<std::string> keys_;
};

class MvBufrFilter
{
public:
    MvBufrFilter();
    MvBufrFilter(const MvBufrFilter&) = delete;
    MvBufrFilter& operator=(const MvBufrFilter&) = delete;
    void filter(MvObs& anObs);

protected:
    void addToResult();

    BufrFilterConditionGroup coordCondition_;
    BufrFilterExtract* extract_{nullptr};
    MvKeyProfile* result_{nullptr};
};
