#ifndef INCLUDED_BOBCAT_TABLESUPPORT_
#define INCLUDED_BOBCAT_TABLESUPPORT_

#include <ostream>
#include <vector>
#include <unordered_map>
#include <numeric>

#include <bobcat/align>

namespace FBB
{

class TableSupport
{
    public:
        struct Field
        {
            size_t width;       // width of a field
            size_t type;        // handlingtype (ColumnType)
        };

        enum ColumnType
        {
            SKIP        = 0,
            USE         = 1 << 0,
            LEFT_FULL   = 1 << 1,
            RIGHT_FULL  = 1 << 2,
            LEFT_MID    = 1 << 3,
            RIGHT_MID   = 1 << 4
        };

                // define the columns to provide with a (partial) hline
        struct HLine
        {
            size_t d_row;
            size_t d_begin;
            size_t d_end;
            size_t d_type;  
        
            HLine(size_t row, size_t begin, size_t end);
            HLine(ColumnType margins, size_t row, size_t begin, size_t end);
        };

        class const_iterator: public std::iterator<std::input_iterator_tag, 
                                                                        Field>
        {
            TableSupport const &d_support;

                // iterators to vectors in d_elements. The elements of the 
                // vectors contain the types of the column (SKIP, USE, etc)
                // to determine the width of an element call 
                // separator.width(iterator)
            std::vector<size_t> const *d_vector;
            std::vector<size_t>::const_iterator d_iter;
            mutable Field d_field;

            static std::vector<size_t> s_empty;

            public:
                const_iterator(TableSupport const &support, size_t row, 
                                                            bool begin);

                const_iterator &operator++();                           // .f
                bool operator==(const_iterator const &other) const;     // .f
                bool operator!=(const_iterator const &other) const;     // .f

                Field const &operator*() const;                         // .f
                Field const *operator->() const;
        };

    private:
        std::ostream *d_streamPtr;
        size_t d_nRows;                     // # of rows (defined after def())
        size_t d_nColumns;                  // # of cols
        std::vector<Align> const *d_align;  // pointer to alignment info,
                                            // passed to this by setParam,
                                            // (used by Table/TableBuf)
        size_t d_tableWidth;                // total table width
        std::vector<std::string> d_sep;     // a vector of separators. 0:
                                            // before the leftmost col.

                                            // Element types per row per
                                            // column, wrt hlines
        typedef std::unordered_map<size_t, std::vector<size_t>> UMsizeVector;
        typedef  UMsizeVector::value_type UMValue;
        typedef  UMsizeVector::const_iterator UMIter;

        UMsizeVector d_elements;

    public:
        TableSupport();
        TableSupport(TableSupport &&tmp);

        TableSupport &operator=(TableSupport &&tmp);

        virtual ~TableSupport();            // empty

        void setParam(std::ostream &ostr, size_t rows, size_t nColumns,
                        std::vector<Align> const &align);

        void hline() const;         // called after the last data row      1.f

            // called by Table/TableBuf for each row (0: before 1st data row)
        void hline(size_t row) const;                                   // 2.f

            // same for columns
        void vline() const;                                             // 1.f
        void vline(size_t col) const;                                   // 2.f

        size_t width() const;               // total width of the table     .f

    protected:
                                            
        const_iterator begin(size_t row) const;                         // .f
        const_iterator end(size_t row) const;                           // .f

        size_t colWidth(size_t col) const;  // width of a column and:      .f
        size_t sepWidth(size_t col) const;  // of a separator              .f
                                            // (0: leftmost)

        size_t nColumns() const;            // number of columns           .f
        size_t nRows() const;               // number of rows              .f

        std::ostream &out() const;          // stream to insert into       .f
                                            // the table

                                            // col. alignmnts (not separators)
        std::vector<Align> const &align() const;                        // .f
        std::vector<std::string> const &sep() const;    // separators      .f

    private:
            // called for each row (0: before 1st data row)
        virtual void v_hline(size_t row) const;
        virtual void v_hline() const;         // called after the last data row

            // same for columns
        virtual void v_vline(size_t col) const;
        virtual void v_vline() const;


        size_t width(size_t idx) const;

            // return indices in d_elements of left separator element, etc.
        static size_t leftSeparator(size_t column);                     // .f
        static size_t element(size_t column);                           // .f
        static size_t rightSeparator(size_t column);                    // .f

        static void leftType(size_t *target, size_t type);              // .f
        static void rightType(size_t *target, size_t type);             // .f

    friend const_iterator;
    friend TableSupport &operator<<(TableSupport &support, size_t);
    friend TableSupport &operator<<(TableSupport &support, 
                                                std::string const &sep);
    friend TableSupport &operator<<(TableSupport &support, 
                                                HLine const &hline);
};

#include "align.f"
#include "begin.f"
#include "colwidth.f"
#include "end.f"
#include "hline1.f"
#include "hline2.f"
#include "ncolumns.f"
#include "nrows.f"
#include "opeq.f"
#include "opinc.f"
#include "opneq.f"
#include "opstar.f"
#include "out.f"
#include "sep.f"
#include "sepwidth.f"
#include "vline1.f"
#include "vline2.f"
#include "width.f"

    // Free Functions

#include "opbitor.f"        // ColumnType | ColumnType

TableSupport &operator<<(TableSupport &support, size_t);
TableSupport &operator<<(TableSupport &support, std::string const &sep);
TableSupport &operator<<(TableSupport &support, 
                                        TableSupport::HLine const &hline);
} // FBB


#endif
