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

// MvTephiData.h - Dec2004/vk  - original file
// DataThermo.h  - Jan2014/fii - update to new Thermo application
//
//  The purpose of these classes is to clean and simplify Tephigram
//  code. Note that this is partly a Q&D solution as some methods are
//  duplicated (with a simplified interface) from class MvTephiFrame.
//
//  What is more, this class hierarchy made it much easier to add new
//  data types, like plain pressure levels and UKMO New Dynamics data.
//

#pragma once

#include "MvFieldSet.h"
#include "ParamsThermo.h"

class MvNetCDF;

//______________________________________________________________________
//                                                       MvThermoDataBase
//
//  Abstract base class for different thermoD input data types
//

class MvThermoDataBase
{
private:
    MvThermoDataBase();
    MvThermoDataBase(MvThermoDataBase&);

public:
    MvThermoDataBase(MvField&, const ThermoParams&);
    virtual ~MvThermoDataBase() {}

    // Initialise data values storage
    void initialiseDataMemory();

    // Reads and preprocesses one set of data, i.e. all fields valid at
    // the same time. Fields are sorted and thus we can trust the iterator.
    void readOneSetOfFields(MvFieldSetIterator&);

    //  Post-process and generate NetCDF output
    bool generate(MvNetCDF&);
    bool generateWind(MvNetCDF& netcdf);

    MvField returnToken() { return nextField_; }

    // Wind flag info
    bool windExist();

    // Title information
    std::string title(MvNetCDF&);

protected:
    virtual void computeLevelsTd() = 0;
    virtual void processPressure(double) = 0;

    // Re-scale values
    virtual void rescaleValues();

    // Get level value
    virtual int getLevel() { return (int)currField_.level(); }

    // Store parameter value
    virtual void collectValue(double);

    // Initialise a netCDF file: variables, dimensions and attributes
    bool ncInit(MvNetCDF&);

    // Add global attributes to the netcdf file
    bool ncGlobalAttributes(MvNetCDF&);

    // Get value from current field
    double getValueFromCurrentField();

    // Create a time key
    void generateTimeKey();

    // Check if LNSP field is required
    virtual bool needsLnsp() = 0;

    std::string formulaPrintName();

    // Variable members
    bool lnspFlag_{false};  // T: lnsp is available
    int counter_{0};        // Number of plots
    int tCount_{0};         // Temp count
    int dCount_{0};         // Dewpoint count
    int uCount_{0};
    int vCount_{0};  // Wind count
    int pCount_{0};  // Pressure/Levels count

    int nTIME_STR_{17};  // 17 - yyyymmddhhmmstep + 1 ('end of string')

    // Data values storage
    std::vector<double> T_;    // Temperature
    std::vector<double> Q_;    // Humidity
    std::vector<double> U_;    // U wind
    std::vector<double> V_;    // V wind
    std::vector<double> TD_;   // Dewpoint
    std::vector<double> P_;    // Pressure
    std::vector<double> PUV_;  // Pressure for wind (can be different from P_)
    double LNSP_{0.};

    std::string timeKey_;

    MvField currField_;
    MvField nextField_;

    const ThermoParams* pars_;
};

//______________________________________________________________________
//                                                 MvThermoDataHalfLevels
//  Class for ECMWF type level data

class MvThermoDataHalfLevels : public MvThermoDataBase
{
public:
    MvThermoDataHalfLevels(MvField& fieldToken, const ThermoParams& pars) :
        MvThermoDataBase(fieldToken, pars) {}

    virtual ~MvThermoDataHalfLevels() {}

protected:
    virtual void computeLevelsTd();
    virtual void processPressure(double);
    virtual bool needsLnsp() { return true; }
};

//______________________________________________________________________
//                                                 MvThermoDataFullLevels
//  Class for "simple" level data

class MvThermoDataFullLevels : public MvThermoDataBase
{
public:
    MvThermoDataFullLevels(MvField& fieldToken, const ThermoParams& pars) :
        MvThermoDataBase(fieldToken, pars) {}

    virtual ~MvThermoDataFullLevels() {}

protected:
    virtual void computeLevelsTd();
    virtual void processPressure(double);
    virtual bool needsLnsp() { return true; }
};

//______________________________________________________________________
//                                             MvThermoDataPressureLevels
//  Class for data on pressure levels

class MvThermoDataPressureLevels : public MvThermoDataBase
{
public:
    MvThermoDataPressureLevels(MvField&, const ThermoParams&);
    virtual ~MvThermoDataPressureLevels() {}

protected:
    virtual void rescaleValues();
    virtual void computeLevelsTd();
    virtual void processPressure(double);
    virtual bool needsLnsp() { return false; }
};

//______________________________________________________________________
//                                               MvThermoDataUkmoNDLevels
//  Class for UKMO "New Dynamics" data

class MvThermoDataUkmoNDLevels : public MvThermoDataBase
{
public:
    MvThermoDataUkmoNDLevels(MvField&, const ThermoParams&);
    virtual ~MvThermoDataUkmoNDLevels() {}

protected:
    virtual void computeLevelsTd();
    virtual void collectValue(double);
    virtual void processPressure(double);
    virtual void rescaleValues();

    // Level value in second octet
    virtual int getLevel() { return (int)currField_.level_L2(); }

    virtual bool needsLnsp() { return false; }
};

//______________________________________________________________________


MvThermoDataBase* MvThermoDataFactory(MvField&, const ThermoParams&);
