/***************************** 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 <list>
#include <map>
#include <vector>

#ifdef ECCODES_UI
#include "grib_api.h"
#endif

#include "MvMessageMetaData.h"
#include "MvVariant.h"

#ifdef METVIEW
// for off_t
#include "mars.h"
#endif

class MvKeyProfile;

class GribItem
{
public:
    void setPos(const std::string& s) { pos_ = s; }
    const std::string& pos() const { return pos_; }
    void setName(const std::string& s) { name_ = s; }
    const std::string& name() const { return name_; }
    void setType(const std::string& s) { type_ = s; }
    const std::string& type() const { return type_; }

    template <typename T>
    void setValue(T val, const std::string& strVal = {})
    {
        value_ = MvVariant(val);
        strValue_ = (strVal.empty()) ? value_.toString() : strVal;
    }
    //    void setValue(long val, const std::string& strVal ={}) {
    //        value_ = val; strValue_ = strVal.empty()?val:strVal;}
    //    void setValue(double val, const std::string& strVal ={}) {
    //        value_ = val; strValue_ = strVal.empty()?val:strVal;}
    //    void setValue(const std::string& val) {
    //        value_ = val; strValue_ = val;}
    const MvVariant& value() const { return value_; }
    const std::string& strValue() const { return strValue_; }
    void setDescription(const std::string& s) { description_ = s; }
    const std::string& description() const { return description_; }
    void addAlias(const std::string& s) { alias_.push_back(s); }
    const std::vector<std::string>& alias() const { return alias_; }
    void addArrayData(const std::string& s) { array_.push_back(s); }
    const std::vector<std::string>& arrayData() const { return array_; }

protected:
    std::string pos_;
    std::string name_;
    std::string type_;
    MvVariant value_;
    std::string strValue_;
    std::string description_;
    std::vector<std::string> alias_;
    std::vector<std::string> array_;
};

class GribSection;
typedef std::list<GribSection*>::const_iterator GribSectionIterator;

class GribSection
{
public:
    GribSection() = default;
    ~GribSection();
    void name(const std::string& s) { name_ = s; }
    const std::string& name() const { return name_; }
    void lenght(int i) { lenght_ = i; }
    int lenght() const { return lenght_; }
    void offset(int i) { offset_ = i; }
    int offset() const { return offset_; }
    void addItem(GribItem* i) { item_.push_back(i); }
    int itemNum() const { return item_.size(); }
    const std::vector<GribItem*>& item() const { return item_; }

protected:
    std::string name_;
    int lenght_;
    int offset_;
    std::vector<GribItem*> item_;
};

class GribWmoDump
{
public:
    GribWmoDump() {}
    ~GribWmoDump();
    void clear();
    bool read(const std::string&, int);
    int sectionNum() const { return section_.size(); }
    const std::string& text() const { return text_; }
    const std::vector<GribSection*>& section() const { return section_; }

protected:
    void parse(std::stringstream&);

    std::vector<GribSection*> section_;
    std::string text_;
};

class GribStdDump
{
public:
    GribStdDump() {}
    ~GribStdDump();
    void clear();
    bool read(const std::string&, int);
    int itemNum() const { return item_.size(); }
    const std::string& text() const { return text_; }
    const std::vector<GribItem*>& item() const { return item_; }

protected:
    void parse(std::stringstream&);

    std::vector<GribItem*> item_;
    std::string text_;
};

class GribMvDump
{
public:
    GribMvDump();
    ~GribMvDump();
    void clear();
    bool read(const std::string&, int);
    int itemNum() const { return item_.size(); }
    const std::vector<GribItem*>& item() { return item_; }

protected:
    std::vector<GribItem*> item_;
    static std::map<int, std::string> keyMap_;
};

class GribTableDump
{
public:
    bool read(const std::string&, int msgCnt,
              std::vector<std::string>& tablesMasterDir, std::vector<std::string>& tablesLocalDir);

protected:
    static std::vector<std::string> definitionsDir_;
};

class GribValueDump
{
public:
    GribValueDump() = default;
    ~GribValueDump();
    void clear();
    bool read(const std::string&, int, std::string&, std::string&);
    double* latitude() { return latitude_; }
    double* longitude() { return longitude_; }
    double* value() { return value_; }
    int num() const { return num_; }
    int decimalPlaces() { return decimalPlaces_; }

protected:
    void readFailed(FILE*, grib_handle*);

    double* latitude_{nullptr};
    double* longitude_{nullptr};
    double* value_{nullptr};
    int num_{0};
    int decimalPlaces_{0};
    std::string gridType_;
    static const int maxNum_;
};


class GribPlDump
{
public:
    GribPlDump() = default;
    ~GribPlDump() = default;
    void clear();
    bool read(const std::string&, int);
    const std::vector<int>& values() const {return vals_;}
    int N() const {return N_;}
    const std::string& gridType() const {return gridType_;}
    bool isOctahedral() const {return octahedral_;}
    int num() const { return static_cast<int>(vals_.size()); }

protected:
    void readFailed(FILE* fp, grib_handle* gh);
    std::vector<int> vals_;
    int N_{0};
    bool octahedral_{false};
    std::string gridType_;
};

class GribPvDump
{
public:
    GribPvDump() = default;
    ~GribPvDump() = default;
    void clear();
    bool read(const std::string&, int);
    const std::vector<float>& values() const {return vals_;}
    const std::vector<float>& a() const {return a_;}
    const std::vector<float>& b() const {return b_;}
    const std::vector<float>& ph() const {return ph_;}
    const std::vector<float>& pf() const {return pf_;}
    float psRef() const {return psRef_;}
    int num() const { return static_cast<int>(vals_.size()); }
    int levelNum() const;

protected:
    void compute();
    void readFailed(FILE* fp, grib_handle* gh);

    std::vector<float> vals_;
    std::vector<float> a_;
    std::vector<float> b_;
    std::vector<float> ph_;
    std::vector<float> pf_;
    const float psRef_{101325};
};

class GribMetaData : public MvMessageMetaData
{
public:
    GribMetaData();
    ~GribMetaData();
    void setFileName(std::string);
    int computeTotalMessageNum();
    int getGribCount();
    void getKeyList(int, std::string, MvKeyProfile*);
    void getKeyList(int, std::string, std::list<std::string>&);
    void getKeyProfileForFirstMessage(MvKeyProfile* prof);

    void setTotalMessageNum(int);
    long gribapiVersionNumber();
    void setFilter(const std::vector<off_t>&, const std::vector<int>&);
    void updateFilterCnt(const std::vector<off_t>& offset, const std::vector<int>& cnt);
    bool hasMultiMessage() { return hasMultiMessage_; }

protected:
    void clear();

private:
    // void readMessages(MvKeyProfile *);
    void readMessage(MvKeyProfile*, grib_handle*);
    void setDateStructure();

    bool hasMultiMessage_;
};
