//
/*
 * io_ambertraj.cpp
 *
 *  Created on: Apr 18, 2019
 *      Author: Shailesh
 */
#ifndef IO_NCBASE_H
#define IO_NCBASE_H

#include "io.h"
#include <vector>
#include <string>

#ifdef ENABLE_TRAJECTORY_NC
#include <netcdf.h>

bool NCCheckErr(int ncerr);

std::string NCGetAttrText(int ncid, int vid, const char *attribute);

std::string NCGetAttrText(int ncid, const char *attribute);

int NCGetDimInfo(int ncid, const char *attribute, unsigned int &length);

// For backwards compatibility only
int NCGetDimInfo(int ncid, const char *attribute, int &length);

class CNetcdfBase {
public:
    /// To get type of NetCDF trajectory
    enum NCTYPE {
        NC_UNKNOWN = 0, NC_AMBERTRAJ, NC_AMBERRESTART, NC_AMBERENSEMBLE
    };
    /// \return Type of given file.
    NCTYPE
    getNCConventions(const char*);
//#   ifndef NCAMBERTRAJ

//#   else
    CNetcdfBase():
        ncid_(-1),
        ncframe_(-1),
        coordVID_(-1),
        cellAngleVID_(-1),
        cellLengthVID_(-1),
        frameDID_(-1),
        atomDID_(-1),
        ncatom_(-1),
        ncatom3_(-1),
        spatialDID_(-1),
        labelDID_(-1),
        cell_spatialDID_(-1),
        cell_angularDID_(-1),
        spatialVID_(-1),
        cell_spatialVID_(-1),
        cell_angularVID_(-1)
    {
        start_[0] = 0;
        start_[1] = 0;
        start_[2] = 0;
        count_[0] = 0;
        count_[1] = 0;
        count_[2] = 0;
        ncdebug_ = -1;
    }

    /// \return NetCDF trajectory type based on conventions.
    NCTYPE
    getNCConventions ()
    {
        NCTYPE nctype = NC_UNKNOWN;
        std::string attrText = NCGetAttrText (ncid_, "Conventions");
        if (attrText == "AMBERENSEMBLE")
            nctype = NC_AMBERENSEMBLE;
        else if (attrText == "AMBER")
            nctype = NC_AMBERTRAJ;
        else if (attrText == "AMBERRESTART")
            nctype = NC_AMBERRESTART;
        else if (attrText.empty ())
            cerr << "Error: Could not get conventions from NetCDF file." << endl;
        else
        {
            cerr << "Error: NetCDF file has unrecognized conventions \""
                            << attrText.c_str () << "\"." << endl;
            cerr
            << "Error:   Expected \"AMBER\", \"AMBERRESTART\", or \"AMBERENSEMBLE\"."
            << endl;
        }
        return nctype;
    }
    /// Check NetCDF file conventions version.
    void
    checkConventionsVersion ()
    {
        std::string attrText = NCGetAttrText (ncid_, "ConventionVersion");
        if (attrText != "1.0")
            cout <<
            "Warning: NetCDF file has ConventionVersion that is not 1.0 ("
            << attrText.c_str () << ")\n" << endl;
    }
    /// Open NetCDF file for reading.
    int
    NC_openRead (std::string const& Name)
    {
        if (Name.empty ())
            return 1;
        if (NCCheckErr (nc_open (Name.c_str (), NC_NOWRITE, &ncid_)))
            return 1;
        return 0;
    }

    int
    setupCoords ()
    {

        // Get atoms info
        atomDID_ = NCGetDimInfo (ncid_, "atom", ncatom_);
        if (atomDID_ == -1)
            return 1;
        ncatom3_ = ncatom_ * 3;
        // Get coord info
        coordVID_ = -1;
        if (nc_inq_varid (ncid_, "coordinates", &coordVID_) == NC_NOERR)
        {
            if (ncdebug_ > 0)
                cout << "\tNetCDF file has coordinates." << endl;
            std::string attrText = NCGetAttrText (ncid_, coordVID_, "units");
            if (attrText != "angstrom")
                cout
                <<
                "Warning: NetCDF file has length units of "
                << attrText.c_str () << " - expected angstrom." << endl;
        }
        // Get spatial info
        int spatial;
        spatialDID_ = NCGetDimInfo (ncid_, "spatial", spatial);
        if (spatialDID_ == -1)
            return 1;
        if (spatial != 3)
        {
            cerr << "Error: Expected 3 spatial dimensions, got " << spatial << endl;
            return 1;
        }
        if (NCCheckErr (nc_inq_varid (ncid_, "spatial", &spatialVID_)))
        {
            cout
            << "Warning: Could not get spatial VID. File may not be Amber NetCDF compliant."
            << endl;
            cout << "Warning: Assuming spatial variables are 'x', 'y', 'z'" << endl;
        }
        else
        {
            start_[0] = 0;
            count_[0] = 3;
            char xyz[3];
            if (NCCheckErr (
                            nc_get_vara_text (ncid_, spatialVID_, start_, count_, xyz)))
            {
                cout << "Error: Getting spatial variables." << endl;
                return 1;
            }
            if (xyz[0] != 'x' || xyz[1] != 'y' || xyz[2] != 'z')
            {
                cout
                << "Error: NetCDF spatial variables are '" << xyz[0] << "', '"
                << xyz[1] << "', '" << xyz[2] << "', not 'x', 'y', 'z'"
                << endl;
                return 1;
            }
        }
        // Return a error if no coords
        if (coordVID_ == -1)
        {
            cerr << "Error: NetCDF file has no coordinates" << endl;
            return 1;
        }

        return 0;
    }

    void NC_close ()
    {
        if (ncid_ == -1)
            return;
        bool err = NCCheckErr (nc_close (ncid_));
        if (ncdebug_ > 0 && !err)
            cout << "Successfully closed ncid" << ncid_ << endl;
        ncid_ = -1;
    }
    /// \return Title of NetCDF file.
    std::string
    getNcTitle () const
    {
        return NCGetAttrText (ncid_, "title");
    }
    /// Read - Set up frame dimension ID and number of frames.
    int
    setupFrameDim ()
    {
        frameDID_ = NCGetDimInfo (ncid_, "frame", ncframe_);
        if (frameDID_ == -1)
            return 1;
        return 0;
    }


    inline int Ncid() const
    { return ncid_;}
    inline int Ncatom() const
    { return ncatom_;}
    inline int Ncatom3() const
    { return ncatom3_;}
    inline int Ncframe() const
    { return ncframe_;}
    inline int CoordVID() const
    { return coordVID_;}

    bool HasCoords() const
    { return (coordVID_ != -1);}

protected:
    size_t start_[4];///< Array starting indices
    size_t count_[4];///< Array counts
    int ncid_;///< NetCDF file ID
    int ncframe_;///< Total number of frames in file
    int coordVID_;///< Coordinates variable ID.
    int cellAngleVID_;///< Box angles variable ID.
    int cellLengthVID_;///< Box lengths variable ID.
    int ncdebug_;
private:

    int frameDID_;///< Frames dimension ID
    int atomDID_;///< Atom dimension ID
    int ncatom_;///< Number of atoms
    int ncatom3_;///< Number of coordinates (# atoms * 3)
    int spatialDID_;///< Spatial dimension ID (3)
    int labelDID_;///< Box angle labels dimension ID (alpha, beta, gamma)
    int cell_spatialDID_;///< Box lengths dimension ID
    int cell_angularDID_;///< Box angles dimension ID
    int spatialVID_;///< Spatial (x, y, z) variable ID
    int cell_spatialVID_;///< Box lengths variable ID
    int cell_angularVID_;///< Box angles variable ID
//#endif /* NCAMBERTRAJ */
};

#endif

#endif // end block if nc is enabled