// -*- C++ -*-
//
// This file is part of HepMC
// Copyright (C) 2014-2023 The HepMC collaboration (see AUTHORS for details)
//
#ifndef HEPMC3_WRITERASCII_H
#define HEPMC3_WRITERASCII_H
///
/// @file  WriterAscii.h
/// @brief Definition of class \b WriterAscii
///
/// @class HepMC3::WriterAscii
/// @brief GenEvent I/O serialization for structured text files
///
/// @ingroup IO
///
#include "HepMC3/Writer.h"
#include "HepMC3/GenEvent.h"
#include "HepMC3/GenRunInfo.h"
#include <string>
#include <fstream>

namespace HepMC3 {

class WriterAscii : public Writer {
public:

    /// @brief Constructor
    /// @warning If file already exists, it will be cleared before writing
    WriterAscii(const std::string& filename,
                std::shared_ptr<GenRunInfo> run = std::shared_ptr<GenRunInfo>());

    /// @brief Constructor from ostream
    WriterAscii(std::ostream& stream,
                std::shared_ptr<GenRunInfo> run = std::shared_ptr<GenRunInfo>());
    /// @brief Constructor from temp ostream
    WriterAscii(std::shared_ptr<std::ostream> s_stream,
                std::shared_ptr<GenRunInfo> run = std::shared_ptr<GenRunInfo>());

    /// @brief Destructor
    ~WriterAscii();

    /// @brief Write event to file
    ///
    /// @param[in] evt Event to be serialized
    void write_event(const GenEvent& evt)  override;

    /// @brief Write the GenRunInfo object to file.
    void write_run_info();

    /// @brief Return status of the stream
    bool failed()  override;

    /// @brief Close file stream
    void close()  override;

    /// @brief Set output precision
    ///
    /// So far available range is [2,24]. Default is 16.
    void set_precision(const int& prec );
    /// @brief Return output precision
    int precision() const;
private:

    /// @name Buffer management
    /// @{

    /// @brief Attempts to allocate buffer of the chosen size
    ///
    /// This function can be called manually by the user or will be called
    /// before first read/write operation
    ///
    /// @note If buffer size is too large it will be divided by 2 until it is
    /// small enough for system to allocate
    void allocate_buffer();

    /// @brief Set buffer size (in bytes)
    ///
    /// Default is 256kb. Minimum is 256b.
    /// Size can only be changed before first read/write operation.
    void set_buffer_size(const size_t& size );

    /// @brief Escape '\' and '\n' characters in string
    static std::string escape(const std::string& s);

    /// Inline function flushing buffer to output stream when close to buffer capacity
    void flush();

    /// Inline function forcing flush to the output stream
    void forced_flush();

    /// @}


    /// @name Write helpers
    /// @{

    /// @brief Inline function for writing strings
    ///
    /// Since strings can be long (maybe even longer than buffer) they have to be dealt
    /// with separately.
    void write_string( const std::string &str );

    /// @brief Write vertex
    ///
    /// Helper routine for writing single vertex to file
    void write_vertex(const ConstGenVertexPtr& v);

    /// @brief Write particle
    ///
    /// Helper routine for writing single particle to file
    void write_particle(const ConstGenParticlePtr& p, int second_field);

    /// @}

private:

    std::ofstream m_file; //!< Output file
    std::shared_ptr<std::ostream> m_shared_stream  = nullptr;///< Output temp. stream
    std::ostream* m_stream = nullptr; //!< Output stream

    int m_precision = 16; //!< Output precision
    char* m_buffer = nullptr;  //!< Stream buffer
    char* m_cursor = nullptr;  //!< Cursor inside stream buffer
    unsigned long m_buffer_size = 262144; //!< Buffer size
    std::string m_float_printf_specifier; //!< the specifier of printf used for floats
    std::string m_particle_printf_specifier; //!< the specifier of printf used for floats
    std::string m_vertex_short_printf_specifier; //!< the specifier of printf used for zero vertices
    std::string m_vertex_long_printf_specifier; //!< the specifier of printf used for vertices
};


} // namespace HepMC3

#endif
