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

#pragma once

#include <iostream>
#include <map>
#include <vector>
#include <list>

class MvVariant;

class MvKey
{
public:
    MvKey() = default;
    MvKey(const std::string& name, const std::string& shortName, std::string description = std::string(""));
    MvKey(const MvKey&);

    MvKey* clone();

    enum Role
    {
        IndexRole,
        MessageIndexRole,
        SubsetIndexRole,
        RankRole,
        LatitudeRole,
        LongitudeRole,
        DateRole,
        TimeRole,
        LevelRole,
        StationIdRole,
        NoRole
    };

    enum KeyType
    {
        NormalKey,
        EccodesKey
    };

    void setRole(Role role) { role_ = role; }
    Role role() const { return role_; }

    void setKeyType(KeyType keyType) { keyType_ = keyType; }
    KeyType keyType() const { return keyType_; }

    enum ValueType
    {
        StringType,
        IntType,
        LongType,
        FloatType,
        DoubleType
    };
    void setValueType(ValueType, bool useMissingValue = false);
    ValueType valueType() const { return valueType_; }

    const std::string& shortName() const { return shortName_; }
    const std::string& name() const { return name_; }
    const std::map<std::string, std::string>& metaData() const { return metaData_; }
    const std::string& metaData(const std::string&) const;
    const std::string& description() const { return description_; }

    void setShortName(const std::string& s) { shortName_ = s; }
    void setName(const std::string& s) { name_ = s; }
    void setMetaData(const std::string&, const std::string&);
    void setDescription(const std::string& d) { description_ = d; }

    int valuePrecision() const { return precision_; }
    void setValuePrecision(int p) { precision_ = p; }

    // TODO: refactor the usage of the "value" functions
    void addValue(const std::string& v) { addStringValue(v); }
    const std::vector<std::string>& value() const { return stringVal_; }
    void setValue(int idx, const std::string& v) { setStringValue(idx, v); }
    std::string valueAsString(int idx) const;

    void addStringValue(const std::string&);
    const std::vector<std::string>& stringValue() const { return stringVal_; }
    const std::string& stringConstantValue() const;
    void setStringValue(int, const std::string&);

    void addIntValue(int);
    const std::vector<int>& intValue() const { return intVal_; }
    int intConstantValue() const;
    void setIntValue(int, int);
    void setIntRange(int, int);
    void setIntMissingValue(int v)
    {
        intMissingVal_ = v;
        missingValDefined_ = true;
    }
    int intMissingValue() const { return intMissingVal_; }

    void addLongValue(long);
    const std::vector<long>& longValue() const { return longVal_; }
    long longConstantValue() const;
    void setLongValue(int, long);
    void setLongRange(long, long);
    void setLongMissingValue(long v)
    {
        longMissingVal_ = v;
        missingValDefined_ = true;
    }
    long longMissingValue() const { return longMissingVal_; }

    void addDoubleValue(double);
    const std::vector<double>& doubleValue() const { return doubleVal_; }
    double doubleConstantValue() const;
    void setDoubleValue(int, double);
    void setDoubleMissingValue(double v)
    {
        doubleMissingVal_ = v;
        missingValDefined_ = true;
    }
    double doubleMissingValue() const { return doubleMissingVal_; }

    void addValue(const MvVariant&);
    void setValue(int chunkStart, int chunkSize, MvKey* key);

    int valueNum() const;
    void clearMetaData();
    void clearData();

    // const std::map<std::string,int>& counter() const {return counter_;}

    bool readIntAsString() const { return readIntAsString_; }
    void setReadIntAsString(bool b) { readIntAsString_ = b; }
    bool editable() const { return editable_; }
    void setEditable(bool b) { editable_ = b; }
    void setConstant(bool b) { constant_ = b; }
    bool isConstant() const { return constant_; }
    bool isMissingValueDefined() const { return missingValDefined_; }
    void preAllocate(int);

    bool operator==(const MvKey&) const;
    bool operator!=(const MvKey&) const;

private:
    void addToCounter(const std::string&);

    std::string name_;
    std::string shortName_;
    std::string description_;
    std::map<std::string, std::string> metaData_;

    Role role_{NoRole};
    KeyType keyType_{NormalKey};
    int precision_{0};
    ValueType valueType_{StringType};
    bool constant_{false};
    std::vector<std::string> stringVal_;
    std::vector<int> intVal_;
    std::vector<long> longVal_;
    std::vector<float> floatVal_;
    std::vector<double> doubleVal_;
    int intMissingVal_{0};
    long longMissingVal_{0};
    float floatMissingVal_{0.};
    double doubleMissingVal_{0.};
    bool missingValDefined_{false};

    // std::map<std::string,int> counter_;
    bool readIntAsString_{true};
    bool editable_{true};  // if false only shortname is editable
};


class MvKeyProfile : public std::vector<MvKey*>
{
public:
    MvKeyProfile(std::string n) :
        name_(n) {}
    MvKeyProfile(const MvKeyProfile&);
    ~MvKeyProfile();

    MvKeyProfile* clone();
    void clear();
    MvKey* addKey();
    void addKey(MvKey*);
    void insertKey(int before, MvKey* key);
    void deleteKey(int);
    void swap(int, int);
    void reposition(int, int);
    void reposition(const std::vector<int>&);
    MvKey* key(std::string) const;
    MvKey* key(MvKey::Role role) const;
    MvKey* key(MvKey::Role role, int occurence) const;
    std::string name() { return name_; }
    void setName(std::string n) { name_ = n; }
    int valueNum(int);
    int valueNum() const;
    void clearKeyData();
    void setSystemProfile(bool b) { systemProfile_ = b; }
    bool systemProfile() { return systemProfile_; }
    void expand(std::vector<MvKeyProfile*> ref);
    void markErrorRow(int i) { errorRows_.push_back(i); }
    bool isErrorRow(int i) const;
    void preAllocate(int);
    void setValuesInChunk(int chunkStart, int chunkSize, MvKeyProfile* prof);

    const std::string& metaData(const std::string&) const;
    void setMetaData(const std::string&, const std::string&);
    void clearMetaData();

    bool operator==(const MvKeyProfile&) const;
    bool operator!=(const MvKeyProfile&) const;

private:
    std::string name_;
    bool systemProfile_{false};
    std::vector<int> errorRows_;
    std::map<std::string, std::string> metaData_;
};
