/*
 * logapi.hpp
 *
 *  Created on: 23 мар. 2017 г.
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
 */

#ifndef INTFACES_LOGAPI_HPP_
#define INTFACES_LOGAPI_HPP_

/*
 * LogAPI объявляет классы для журналирования XXX_Log
 * журналы могут фильтровать сообщения по уровню - level.
 *    уровень может быть константным или динамическим - var/const_log_level
 * журалы печатают в принтеры Log_GlobalPrinters, и могут фильтровать вывод на
 *  принтеры различным образом:
 *  - по маске отключения принтеров - Log_GlobalPrinters
 *  - маскировать принтеры по уровню сообщения - LogFilter_Leveled
 *  фильтры маски могут быть как локальными - принадлежать экземпляру журнала,
 *     так и глобальными - принадлежать классу журнала.
 *  Определены шаблоны глобальных фильров:
 *      GlobalDebug_filter - простоф масочный фильтр
 *      GlobalLog_filter   - фильтр по уровню сообщений
 *  Журналы с глобальным фильтром именюутся SystemXxxx. предопределены:
 *   System_Log   - глобальный журнал с настраиваемым уровнем
 *   System_Debug - глобальный журнал с константным уровнем, и простым масочным фильтром
 *  Журналы с собственным фильтром:
 *   Log - с настраиваемым уровнем
 *   Debug_Log - с константным уровнем
 *
 *  Для создания нужного журнала используется шаблон GlobalLog<уровень, фильтр>
 *    позволяет создать нужный вариант журнала.
 *
 *  Принтеры журналов сведены в глобальный класс Log_GlobalPrinters. они имеют нумерацию vpXXXX
 *  через АПИ этого класса они устанавливаются и оключаются. класс реализует эксклюзивный доступ
 *  нитки к принтерам
 * */

#define LOGAPI  1

#include "print_hal.hpp"


//************************************************************************************
//                                  Loggers
//************************************************************************************
typedef HAL_OUTBlock_Device log_printer;

//* это реестр принтеров для вывода отладочных сообщений
class Log_GlobalPrinters{
public:

    Log_GlobalPrinters();
    //* тут осуществляет инициализация ресурсов ОС для принтеров
    static void init_os(void);

    static const unsigned printers_limit = 4;
    //* это предопределенные системные принтеры
    enum printers_ids{
      vpDebug = 0
      , vpBT, vpEMP
      , vpUser
      , vpAppend = -1   //* чтобы добавить принтер в любой доступный id
      , vpFail   = -1   //* возвращается при неудаче назначения принтера
    };
    typedef int printer_id;

    enum printer_pin{
        ppDebug = (1<<vpDebug)
      , ppBT    = (1<<vpBT)
      , ppEMP   = (1<<vpEMP)
      , ppAll   = ~0
      , ppAny   = 0
    };
    typedef unsigned printers_set;
    //* \arg loger - NULL удаляет требуемый принтер
    static
    printer_id assign(log_printer* loger, printer_id id);
    static printers_set printers_avail;

    static int puts(printers_set disabled, const char* s);
    static int printf(printers_set disabled, const char* fmt, ...);
    static int vprintf(printers_set disabled, const char* fmt, unsigned nargs, va_list arp);
    static inline
    int vprintf(printers_set disabled, const char* fmt, va_list arp){
      return vprintf(disabled, fmt, 0, arp);
    }
    static void flush(void);

protected:
    static log_printer* printers[printers_limit];

public:
    struct var_printers_set {
      printers_set     value;

      inline
      void assign(const var_printers_set& src){
        value = src.value;
      };
    };

    //* этот шаблон генерит глобальные переменные printers_set
    template<int dummy>
    struct global_printers_set {
      static printers_set     value;

      inline
      void assign(const global_printers_set<dummy>& src){};

      inline
      void assign(const printers_set x){ value = x;};
    };
};



//* this MUST be equal to verbose levels verboselog.h
enum Log_LEVEL_id{
      vANY = -1
    , vERROR, vWARN, vNOTE, vINFO, vVERB
    , vDEBUG, vTRACE , vDUMP
    , vTOTAL
    , vOFF   = vTOTAL, vNONE = vOFF
};

struct var_loglevels_map {
    unsigned char value[vTOTAL];

    static
    void select(unsigned char* target
        , Log_LEVEL_id level, Log_GlobalPrinters::printers_set x);

    void select(Log_LEVEL_id level, Log_GlobalPrinters::printers_set x);

    inline
    void assign(const var_loglevels_map& src){
      memcpy(value, src.value, sizeof(value));
    };

    inline
    void assign(const unsigned char* src){
      memcpy(value, src, sizeof(value));
    };
};

//* этот шаблон генерит глобальные переменные loglevels_map
template<int dummy>
struct global_loglevels_map {
  static
  unsigned char value[vTOTAL];

  void select(Log_LEVEL_id level, Log_GlobalPrinters::printers_set x){
    var_loglevels_map::select(value, level, x);
  };

  inline
  void assign(const global_loglevels_map<dummy>& src){};

  template <int dummy2>
  inline
  void assign(const global_loglevels_map<dummy2>& src){
    memcpy(value, src.value, sizeof(value));
  }

  inline
  void assign(const unsigned char* src){
    if (src != value)
    memcpy(value, src, sizeof(value));
  };
};


template<typename Tdisables>
class LogFilter_Simple {
  public:
    typedef Log_GlobalPrinters Tprinters;
    typedef Tprinters::printers_set printers_set;
    typedef Tdisables disables_t;
    Tdisables         printer_disabled;

    printers_set     enables(printers_set x, bool onoff){
        if (!onoff){
          printer_disabled.value |= x;
        }
        else {
          printer_disabled.value &= ~x;
        }
        return ~printer_disabled.value;
    };

    printers_set     select(printers_set x){
      printer_disabled.value = ~x;
      return ~printer_disabled.value;
    }

    printers_set disabled(void) const {
      return printer_disabled.value;
    };

    printers_set disabled(Log_LEVEL_id level) const {
      return printer_disabled.value;
    };

    void printers_level(Log_LEVEL_id level, printers_set x){
    };

    void assign(const LogFilter_Simple<Tdisables>& origin){
      printer_disabled.assign(origin.printer_disabled);
    };
};

template<typename Tdisables, typename Tmap >
class LogFilter_Leveled
    : public  LogFilter_Simple<Tdisables>
{
  protected:
    Tmap       disables_map;
  public:
    typedef Tmap map_t;
    typedef LogFilter_Simple<Tdisables> inherited;
    typedef Log_GlobalPrinters::printers_set printers_set;

    using inherited::printer_disabled;
    using inherited::disabled;

    const map_t& map(void) const {return disables_map;};

    printers_set disabled(Log_LEVEL_id level) const {
      return printer_disabled.value | disables_map.value[level];
    };

    void printers_level(Log_LEVEL_id level, printers_set x){
      disables_map.select(level, x);
    };

    template<typename srcDisables, typename srcMap>
    void assign(const LogFilter_Leveled<srcDisables, srcMap>& origin){
      printer_disabled.assign(origin.printer_disabled.value);
      disables_map.assign(origin.map().value);
    }

};

class GlobalLog_base {
public:
    typedef Log_GlobalPrinters printers_t;
    static Log_GlobalPrinters io;

    typedef printers_t::printers_set printers_set;

    static inline
    int puts(printers_set disabled, const char* s){
      return io.puts(disabled, s);
    };

    static inline
    int vprintf(printers_set disabled, const char* fmt, unsigned nargs, va_list arp){
      return io.vprintf(disabled, fmt, nargs, arp);
    };

    static inline
    int vprintf(printers_set disabled, const char* fmt, va_list arp){
      return io.vprintf(disabled, fmt, arp);
    };

    static int printf(printers_set disabled, const char* fmt, ...);

    static inline
    void flush(void){io.flush();};
};


template<typename Tfilter>
class GlobalLog_filtered: public GlobalLog_base
{
public:
  typedef Tfilter filter_t;

  Tfilter print_filter;

  printers_set     printer_enables(printers_set x, bool onoff){
    return print_filter.enables(x,onoff);
  };

  printers_set     printer_select(printers_set x){
    return print_filter.select(x);
  };

  inline
  printers_set    disabled(Log_LEVEL_id level){return print_filter.disabled(level);};
  inline
  printers_set    disabled(){return print_filter.disabled();};

public:
  typedef GlobalLog_base inherited;
  GlobalLog_filtered(){};
  GlobalLog_filtered(printers_set x){
    print_filter.select(x);
  };
  GlobalLog_filtered(const GlobalLog_filtered<Tfilter>& x){
    print_filter.assign(x.print_filter);
  };

public:
    using inherited::puts;
    using inherited::printf;
    using inherited::vprintf;

    inline
    int puts(const char* s){
      return io.puts(disabled(), s);
    };

    inline
    int vprintf(const char* fmt, unsigned nargs, va_list arp){
      return io.vprintf(disabled(), fmt, nargs, arp);
    };

    inline
    int vprintf(const char* fmt, va_list arp){
      return io.vprintf(disabled(), fmt, arp);
    };

    int printf(const char* fmt, ...){
      va_list arp;
      va_start(arp, fmt);
      i32 result = io.vprintf(disabled(), fmt, arp);
      va_end(arp);
      return result;
    };

public:
    int puts(Log_LEVEL_id level, const char* s){
      return io.puts(disabled(level), s);
    };

    int vprintf(Log_LEVEL_id level, const char* fmt, unsigned nargs, va_list arp){
      return io.vprintf(disabled(level), fmt, nargs, arp);
    };

    int vprintf(Log_LEVEL_id level, const char* fmt, va_list arp){
      return io.vprintf(disabled(level), fmt, arp);
    }


public:
    // forces log always, same as print
    i32 log(const char* fmt, ...){
      va_list arp;
      va_start(arp, fmt);
      i32 result = io.vprintf(disabled(), fmt, arp);
      va_end(arp);
      return result;
    };

    inline
    i32 logs(const char* s){
        return puts(s);
    };

};



struct var_log_level{
  Log_LEVEL_id value;
};

template<Log_LEVEL_id id>
struct const_log_level{
  static const Log_LEVEL_id value = id;
};


//* это база для журнала отбирающего сообщения по уровню
//*   - статическому, во время компиляции
//*   - или динамическому во время исполнения
//* Tlevel - should have field value
template<typename Tlevel, typename Tfilter>
class GlobalLog : public GlobalLog_filtered<Tfilter>
{
  public:
    typedef GlobalLog_filtered<Tfilter> inherited;

    Tlevel level;
    GlobalLog():inherited(){};

    inline
    bool isLog(Log_LEVEL_id verb) const {
      return (level.value >= verb);
    };

    using inherited::log;
    using inherited::logs;
    using inherited::puts;
    using inherited::printf;
    using inherited::vprintf;

    inline
    i32 log(Log_LEVEL_id verb, const char* fmt, ...){
      if (isLog(verb)){
        va_list arp;
        va_start(arp, fmt);
        i32 result = vprintf(verb, fmt, arp);
        va_end(arp);
        return result;
      }
      else
        return 0;
    };

    inline
    i32 logs(Log_LEVEL_id verb, const char* s){
      if (isLog(verb))
        return puts(verb, s);
      else
        return 0;
    };

    template <Log_LEVEL_id verb>
    inline
    i32 log(const char* fmt, ...){
      if (isLog(verb)){
        va_list arp;
        va_start(arp, fmt);
        i32 result = vprintf(verb, fmt, arp);
        va_end(arp);
        return result;
      }
      else
        return 0;
    }

    template <Log_LEVEL_id verb>
    inline
    i32 logs(const char* s){
      if (isLog(verb))
        return puts(verb, s);
      else
        return 0;
    }

public:
    //много кода использует это
    inline
    i32 show(const char* s){
      return logs<vINFO>(s);
    }

    inline
    i32 showf(const char* fmt, ...){
      if (isLog(vINFO)){
        va_list arp;
        va_start(arp, fmt);
        i32 result = vprintf(vINFO, fmt, arp);
        va_end(arp);
        return result;
      }
      else
        return 0;
    }

    inline
    i32 logsDebug(const char* s){
      return logs<vDEBUG>(s);
    }

    inline
    i32 logDebug(const char* fmt, ...){
      if (isLog(vDEBUG)){
        va_list arp;
        va_start(arp, fmt);
        i32 result = vprintf(vDEBUG, fmt, arp);
        va_end(arp);
        return result;
      }
      else
        return 0;
    }

    inline
    i32 logWarning(const char* fmt, ...){
      if (isLog(vWARN)){
        va_list arp;
        va_start(arp, fmt);
        i32 result = vprintf(vWARN, fmt, arp);
        va_end(arp);
        return result;
      }
      else
        return 0;
    }

};

template< int dummy>
class GlobalDebug_filter
    : public LogFilter_Simple<Log_GlobalPrinters::global_printers_set<dummy> >
{
};

template< int dummy>
class GlobalLog_filter
    : public LogFilter_Leveled<
                struct Log_GlobalPrinters::global_printers_set<dummy>
              , struct global_loglevels_map<dummy>
              >
{};

enum SystemLog_id{
    slid_Debug = 0
  , slid_Log
  , slid_EMP
  , slid_DSE
};

class SystemDebug_filter : public GlobalDebug_filter<slid_Debug> {};
class SystemLog_filter : public GlobalLog_filter<slid_Log> {};

class VarLog_filter
    : public LogFilter_Leveled<
                  struct Log_GlobalPrinters::var_printers_set
                , struct var_loglevels_map>
{
};

class VarDebug_filter
    : public LogFilter_Simple<Log_GlobalPrinters::var_printers_set>
{
public:
  VarDebug_filter(){
    select(Log_GlobalPrinters::ppDebug);
  };
};


//* отладочные сообщения отбираются во время компиляции
template <Log_LEVEL_id id>
class System_Debug
: public GlobalLog< const_log_level<id>, SystemDebug_filter >
{
public:
  typedef GlobalLog< const_log_level<id>, SystemDebug_filter > inherited;

  void assign(const inherited& src){};
  void assign(const System_Debug& src){};
};

//* отладочные сообщения отбираются во время компиляции
template <Log_LEVEL_id tid>
class Debug_Log
: public GlobalLog< const_log_level<tid>, VarDebug_filter >
{
public:
  typedef GlobalLog< const_log_level<tid>, VarDebug_filter > inherited;
};

//* сообщения системного журнала отбираются во время исполнения
//* по динамически задаваемому уровню
class System_Log : public GlobalLog<var_log_level, SystemLog_filter>
{
public:
  typedef GlobalLog<var_log_level, SystemLog_filter> inherited;

  System_Log();
  System_Log(Log_LEVEL_id print_level);
  void assign(const inherited& src){};
  void assign(const System_Log& src){};
};

class Log : public GlobalLog<var_log_level, VarLog_filter>
{
public:
  typedef GlobalLog<var_log_level, VarLog_filter> inherited;

  Log();
  Log(Log_LEVEL_id print_level);
};

class Device_Log : public System_Log
{
public:
  typedef System_Log inherited;
};
//typedef class Debug_Log<vDEBUG> Debug_Simple_Log;



#endif /* INTFACES_LOGAPI_HPP_ */
