#ifndef INCLUDED_BOBCAT_LOG_
#define INCLUDED_BOBCAT_LOG_

#include <iostream>

#include <fstream>
#include <memory>

#include <bobcat/logbuf>
#include <bobcat/level>
#include <bobcat/exception>

namespace FBB
{

enum LogManipulator
{
    FATAL,
    nl,                 // new line without new time stamp
    fnl,                // forced new line, even at low level specs.
};

class Log: private LogBuf, public std::ostream
{
                                                                    // opins:
    friend Log &operator<<(Log &log, LogManipulator manip);         //  2.cc
    friend Log &operator<<(Log &log,                                //  3.cc
                           std::ostream &(*fun)(std::ostream &str)); 
    friend Log &operator<<(Log &log,                                //  4.cc
                           std::ios_base &(*fun)(std::ios_base &base));
    template<typename Type>
    friend Log &operator<<(Log &log, Type const &type);             //   .f

    std::ofstream d_stream;
    size_t d_level;         // defined by setLevel: the minimum level 
                            // messages must have to be inserted

    struct Active
    {
        bool levelOK;
        bool opfunOK;       // WHAT'S opfun?

                            // log characters accepted by opfun.cc (?)
        std::string accept; // accepted log characters for opfun.cc
    };
    std::unique_ptr<Active> d_active;

                                                // Log object used by 
    static std::unique_ptr<Log> s_stream;       // initialize() and instance()

    public:
        static Log &instance();
        static Log &initialize(std::string const &filename,
                std::ios::openmode = std::ios::out | std::ios::app,
                char const *delim = " ");

        Log();
        Log(std::ostream &out,  char const *delim = " ");
        Log(std::string const &filename,
                std::ios::openmode = std::ios::out | std::ios::app,
                char const *delim = " ");

        void open(std::string const &filename,
                std::ios::openmode = std::ios::out | std::ios::app,
                char const *delim = " ");

        size_t level() const;                                           // .f
        std::ostream &level(size_t useLevel);
        void  setLevel(size_t newLevel);
        void  setTimestamp(TimeStamps timeStamp, char const *delim = " ");

        void  off();                                                    // .f
        void  on(size_t logLevel = 0);

        Log &operator()(char accept);

        void str(std::string const &str);
        std::string const &str() const;

    private:
        void init();
};

inline size_t Log::level() const
{
    return d_level;
}

inline void  Log::off()
{
    setActive(OFF);
}

inline void Log::str(std::string const &accept)
{
    d_active->accept = accept;
}

inline std::string const &Log::str() const
{
    return d_active->accept;
}

template<typename Type>
Log &operator<<(Log &log, Type const &type)
{
    if (log.d_active->levelOK or log.d_active->opfunOK)
        reinterpret_cast<std::ostream &>(log) << type;

    return log;
}

inline Log &operator<<(Log &str, level lvl)
{
    dynamic_cast<std::ostream &>(str) << lvl;
    return str;
}


//std::ostream &operator<<(std::ostream &str, LogManipulator);    // opins1.cc

} // FBB

std::ostream &operator<<(std::ostream &str, FBB::LogManipulator); // opins1.cc

#endif
