// Copyright (c) 2009-2016 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.


// Maintainer: joaander

#ifndef __DCDDUMPWRITER_H__
#define __DCDDUMPWRITER_H__

#include "Analyzer.h"
#include "ParticleGroup.h"

#include <string>
#include <boost/shared_ptr.hpp>
#include <fstream>

/*! \file DCDDumpWriter.h
    \brief Declares the DCDDumpWriter class
*/

#ifdef NVCC
#error This header cannot be compiled by nvcc
#endif

// The DCD Dump writer is based on code from the molfile plugin to VMD
// and is use under the following license

// University of Illinois Open Source License
// Copyright 2003 Theoretical and Computational Biophysics Group,
// All rights reserved.

// Developed by:       Theoretical and Computational Biophysics Group
//             University of Illinois at Urbana-Champaign
//            http://www.ks.uiuc.edu/

//! Analyzer for writing out DCD dump files
/*! DCDDumpWriter writes out the current position of all particles to a DCD file
    every time analyze() is called. Use it to create a DCD trajectory for loading
    into VMD.

    On the first call to analyze() \a fname is created with a dcd header. If the file already exists,
    it is overwritten.

    Due to a limitation in the DCD format, the time step period between calls to
    analyze() \b must be specified up front. If analyze() detects that this period is
    not being maintained, it will print a warning but continue.
    \ingroup analyzers
*/
class DCDDumpWriter : public Analyzer
    {
    public:
        //! Construct the writer
        DCDDumpWriter(boost::shared_ptr<SystemDefinition> sysdef,
                      const std::string &fname,
                      unsigned int period,
                      boost::shared_ptr<ParticleGroup> group,
                      bool overwrite=false);

        //! Destructor
        ~DCDDumpWriter();

        //! Write out the data for the current timestep
        void analyze(unsigned int timestep);

        //! Set whether coordinates should be written out wrapped or unwrapped.
        void setUnwrapFull(bool enable)
            {
            m_unwrap_full = enable;
            }

        //! Set whether rigid body coordinates should be written out wrapped or unwrapped.
        void setUnwrapRigid(bool enable)
            {
            m_unwrap_rigid = enable;
            }

        //! Set whether the z-component should be overwritten by the orientation angle
        void setAngleZ(bool enable)
            {
            m_angle = enable;
            }

    private:
        std::string m_fname;                //!< The file name we are writing to
        unsigned int m_start_timestep;      //!< First time step written to the file
        unsigned int m_period;              //!< Time step period bewteen writes
        boost::shared_ptr<ParticleGroup> m_group; //!< Group of particles to write to the DCD file
        unsigned int m_num_frames_written;  //!< Count the number of frames written to the file
        unsigned int m_last_written_step;   //!< Last timestep written in a a file we are appending to
        bool m_appending;                   //!< True if this instance is appending to an existing DCD file
        bool m_unwrap_full;                 //!< True if coordinates should be written out fully unwrapped in the box
        bool m_unwrap_rigid;                //!< True if rigid bodies should be written out unwrapped
        bool m_angle;                       //!< True if the z-component should be set to the orientation angle

        bool m_overwrite;                   //!< True if file should be overwritten
        bool m_is_initialized;              //!< True if file IO has been initialized
        unsigned int m_nglobal;             //!< Initial number of particles

        float *m_staging_buffer;            //!< Buffer for staging particle positions in tag order
        std::fstream m_file;                //!< The file object

        // helper functions

        //! Initalizes the file header
        void write_file_header(std::fstream &file);
        //! Writes the frame header
        void write_frame_header(std::fstream &file);
        //! Writes the particle positions for a frame
        void write_frame_data(std::fstream &file, const SnapshotParticleData<Scalar>& snapshot);
        //! Updates the file header
        void write_updated_header(std::fstream &file, unsigned int timestep);
        //! Initializes the output file for writing
        void initFileIO(unsigned int timestep);

    };

//! Exports the DCDDumpWriter class to python
void export_DCDDumpWriter();

#endif
