#ifndef INCLUDED_BOBCAT_CSVTABLE_
#define INCLUDED_BOBCAT_CSVTABLE_

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <memory>

#include <bobcat/fmt>
#include <bobcat/csvtabins>
#include <bobcat/csvtabdef>

// save the stream's original formatting values.  column values are formatted
// by width and position, all other formatting flags (like hex, setfill) are
// kept until altered.  when switching streams the next stream continues with
// the formatting flags of the current stream, and the current stream's flags
// are reset to their original values. When CSVTable ceases to exists the
// current stream's flags are reset to their original values.


namespace FBB
{

class CSVTable
{
    std::vector<FMT> d_format;          // column format specifications

                                                // stream used by the 2nd/3rd 
    std::unique_ptr<std::ofstream> d_strPtr;    // constructors

    std::ostream *d_out;                // stream to write to
    std::string d_sep;                  // separator between columns

    std::ios::fmtflags d_flags;         // the original stream flags
    unsigned d_precision;
    char d_fillChar;

    unsigned d_idx;                     // start/continue at d_idx

    public:
        CSVTable(std::ostream &out = std::cout,     //                  1.cc
                 std::string const &sep = ", ");
        CSVTable(std::ofstream &&tmp,               //                  2.cc
                 std::string const &sep = ", ");
        CSVTable(std::string const &fname,          //                  3.cc
                 std::string const &sep = ", ",
                 std::ios::openmode mode = std::ios::out);

        CSVTable(CSVTable const &other) = delete;
        CSVTable(CSVTable &&tmp);                   //                  4.cc

        CSVTable &operator=(CSVTable &&tmp) = default;

        ~CSVTable();


        std::vector<FMT> const &columns() const;    //                      .f


                                // fmt(...) requires 
                                // idx <= d_format.size()
        CSVTabDef fmt(unsigned idx = 0);            // define fields      1.cc
                                                    // using insertions


        void fmt(std::string const &fields,         // define fields      2.cc
                 unsigned idx = 0);                 // using ,-separated
                                                    // trimmed fields

        unsigned idx() const;                       //                     .f

        CSVTabIns more(unsigned idx = ~0U);         //                    1.cc

        void more(std::string const &text, unsigned idx = ~0U);   //      2.cc


                                                    // insert trimmed comma-
                                                    // separated text elements
                                                    // (no comma 
                                                    // after the last d_format
                                                    // field) in subsequent 
                                                    // columns fields using 
        void operator()(std::string const &text);   // the default formats.

                                                    // insert one line, 
                                                    // starting at idx (or
        CSVTabIns row(unsigned idx = ~0U);          // at d_idx if ~0U)   1.cc

                                                    // same, continuing at idx
                                                    // if idx < d_idx then
                                                    // missing columns remain
                                                    // empty
        void row(std::string const &text, unsigned idx = ~0U);  //        2.cc

                                                    
        operator CSVTabIns();                       // row();              .f

        void stream(std::ostream &out);             // switch stream      1.cc

        void stream(std::ofstream &&tmp);           // switch stream      2.cc

        void stream(std::string const &fname,       // switch stream by name
                    std::ios::openmode mode = std::ios::out);       //    3.cc

        unsigned size() const;                      // #table columns       .f
        FMT const &operator[](unsigned idx) const;              //   opindex.f

        void sep(std::string const &separator);     //                     1.f
        std::string const &sep() const;             //                     2.f

        std::ostream &stream();                     // returns d_out       3.f

    private:

                                                    // retrieve the stream's
        void streamFlags();                         // current flags

                                                    // reset the stream's 
        void streamReset();                         // original settings

                                                    // reset at the begin of
        void rowStreamReset();                      // rows

        void check(unsigned idx) const;             // check valid initial idx

                                                    // limit idx to 
        unsigned limit(unsigned idx) const;         // d_format.size()     .f

                                                    // check/update column idx
        unsigned checkInsertIdx(unsigned idx);      // (calls rowStreamReset)

        int preStreamSwitch();                      // called by stream(...)
        void postStreamSwitch(int fillChar);
};

inline std::vector<FMT> const &CSVTable::columns() const
{
    return d_format;
};
inline unsigned CSVTable::idx() const
{
    return d_idx;
}
inline CSVTable::operator CSVTabIns()
{
    return row();
}
inline  FMT const &CSVTable::operator[](unsigned idx) const
{
    return d_format[idx];
}
    // insert fields values into d_out
inline void CSVTable::operator()(std::string const &text)
{
    row(text, ~0U);
}
inline unsigned CSVTable::size() const
{
    return d_format.size();
}
inline std::ostream &CSVTable::stream()
{
    return *d_out;
}

inline std::string const &CSVTable::sep() const
{
    return d_sep;
}
inline void CSVTable::sep(std::string const &separator)
{
    d_sep = separator;
}

} // FBB        

#endif
