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

#include "eccodes.h"

#include "MvVariant.h"

namespace metview
{
class MvBufrEdition;
}

class MvEccBufrMessage;
class BufrMetaData;

using namespace metview;

class MvBufrCodeTable
{
public:
    int element() const { return element_; }
    const std::string& value(int code) const;
    static MvBufrCodeTable* find(int element, MvEccBufrMessage* msg);

protected:
    MvBufrCodeTable(int element, MvBufrEdition* edition, const std::string& path);
    static MvBufrCodeTable* make(int element, MvEccBufrMessage* msg);
    static std::string buildFileName(int element, const std::string& path);
    void load(const std::string& path);

    int element_;
    MvBufrEdition* edition_;
    std::map<int, std::string> items_;

    static std::vector<MvBufrCodeTable*> tables_;
};

class MvBufrFlagTable;

class MvBufrFlagInfo
{
    friend class MvBufrFlagTable;

public:
    MvBufrFlagInfo() = default;

    int num() const { return flags_.size(); }
    const std::string& bits() const { return bits_; }
    const std::string& bitPosition(int) const;
    const std::string& description(int) const;

protected:
    std::string bits_;
    std::vector<std::pair<std::string, std::string> > flags_;
};


class MvBufrFlagTable
{
public:
    int element() const { return element_; }
    void values(int code, int width, MvBufrFlagInfo&) const;
    static MvBufrFlagTable* find(int element, MvEccBufrMessage* msg);

protected:
    MvBufrFlagTable(int element, MvBufrEdition* edition, const std::string& path);
    static MvBufrFlagTable* make(int element, MvEccBufrMessage* msg);
    static std::string buildFileName(int element, const std::string& path);
    static void getBits(unsigned int x, int width, std::vector<int>& v);
    void load(const std::string& path);

    int element_;
    MvBufrEdition* edition_;
    std::map<int, std::string> items_;

    static std::vector<MvBufrFlagTable*> tables_;
};


class MvEccBufrData
{
};

class MvEccBufrMessage
{
public:
    MvEccBufrMessage(codes_bufr_header* bh, int index);
    MvEccBufrMessage(codes_handle* h, int index);
    ~MvEccBufrMessage();

    int index() const { return index_; }
    bool isHeaderValid() const { return headerValid_; }
    bool isDataValid() const { return dataValid_; }
    void setDataValid(bool b) { dataValid_ = b; }
    int bufrEditionNumber() const { return bufrEditionNumber_; }
    long dataCategory() const { return dataCategory_; }
    long dataSubCategory() const { return dataSubCategory_; }
    long rdbType() const { return rdbType_; }
    int masterTablesNumber() const;
    int masterTablesVersionNumber() const;
    int localTablesVersionNumber() const;
    int centre() const;
    const std::string& centreAsStr() const;
    int subCentre() const;
    long subsetNum() const { return subsetNum_; }
    bool isCompressed() const { return compressed_; }
    bool hasSection2() const { return hasSection2_; }
    MvBufrEdition* edition() const { return edition_; }
    const std::vector<std::string>& headerErrors() const { return headerErrors_; }
    const std::vector<std::string>& dataErrors() const { return dataErrors_; }
    void setDataErrors(const std::vector<std::string>& e) { dataErrors_ = e; }
    long offset() const { return offset_; }

    void tablesDirs(std::vector<std::string>& tablesMasterDir, std::vector<std::string>& tablesLocalDir);
    const std::vector<int>& unexpandedDesc();
    void setFileInfo(BufrMetaData* f) { fileInfo_ = f; }

protected:
    void getUnexpanded();
    void replace(std::string& str, const std::string& pattern, const std::string& newPattern);
    void replace(std::string& str, const std::string& pattern, int);
    void checkPar(int, const std::string& name);
    bool codesCheck(const char* call, const char* file, int line, int e, const char* msg);

    int index_;  // message index within the file. Starts at 0!
    bool headerValid_;
    bool dataValid_;
    int bufrEditionNumber_;
    long dataCategory_;
    long dataSubCategory_;
    long rdbType_;
    long subsetNum_;
    bool compressed_;
    bool hasSection2_;
    MvBufrEdition* edition_;
    std::vector<int> unexpanded_;
    std::vector<std::string> headerErrors_;
    std::vector<std::string> dataErrors_;

    BufrMetaData* fileInfo_;
    static std::vector<std::string> definitionsDir_;

    long offset_;
    int totalLenght_;
};

class MvEccBufr
{
public:
    enum ElementDefType
    {
        UnknownElement,
        LocalElement,
        WmoElement
    };

    MvEccBufr(const std::string&);
    int messageNum() const { return messageNum_; }
    MvEccBufrMessage* message(int);
    const std::vector<MvEccBufrMessage*>& messages() const { return msg_; }
    int scan();

    static ElementDefType elementDefType(int);
    static bool codesCheck(const char* call, const char* file, int line, int e, const char* msg);

protected:
    int computeMessageNum();

    std::string fileName_;
    int messageNum_;
    std::vector<MvEccBufrMessage*> msg_;
};
