#include "include/logger.hpp"

namespace qOS {
    /*cstat -MISRAC++2008-0-1-4_b*/
    const char * const logger::endl = "\r\n";
    #if ( Q_LOGGER_COLORED == 1 )
        const char * const logger::end = "\x1B[0m\r\n";
        const char * const logger::nrm = "\x1B[0m";
        const char * const logger::red = "\x1B[31m";
        const char * const logger::grn = "\x1B[32m";
        const char * const logger::yel = "\x1B[33m";
        const char * const logger::blu = "\x1B[34m";
        const char * const logger::mag = "\x1B[35m";
        const char * const logger::cyn = "\x1B[36m";
        const char * const logger::wht = "\x1B[37m";
    #else
        const char * const logger::end = "\r\n";
        const char * const logger::nrm = "";
        const char * const logger::red = "";
        const char * const logger::grn = "";
        const char * const logger::yel = "";
        const char * const logger::blu = "";
        const char * const logger::mag = "";
        const char * const logger::cyn = "";
        const char * const logger::wht = "";
    #endif
    /*cstat +MISRAC++2008-0-1-4_b*/
    /* cppcheck-suppress noConstructor */
    namespace logger {
         _logger& _logger_out = _logger::getInstance(); // skipcq: CXX-W2011, CXX-W2009
        const lout_base dec( 10U );
        const lout_base hex( 16U );
        const lout_base oct( 8U );
        const lout_base bin( 2U );

        void setOutputFcn( util::putChar_t fcn )
        {
            if ( nullptr != fcn ) {
                _logger_out.writeChar = fcn;
            }
        }

        /*cstat -MISRAC++2008-0-1-7*/
        ChainLoggerProxy out( const logSeverity s, const source_location &loc ) noexcept
        {
            _logger_out.toLog( "[ " );
            _logger_out.toLog( dec );
            _logger_out.toLog( clock::getTick() );
            _logger_out.toLog( "] " );
            _logger_out.toLog( _logger_out.s_str[ s ] );

            if ( s == logSeverity::debug ) {
                _logger_out.toLog( "( " );
                _logger_out.toLog( loc.function_name() );
                _logger_out.toLog( ":" );
                _logger_out.toLog( loc.line() );
                _logger_out.toLog( "): " );
            }
            else if ( s == logSeverity::verbose ) {
                _logger_out.toLog( loc.file_name() );
                _logger_out.toLog( " ( " );
                _logger_out.toLog( loc.function_name() );
                _logger_out.toLog( ":" );
                _logger_out.toLog( loc.line() );
                _logger_out.toLog( "): " );
            }
            return ChainLoggerProxy(_logger_out);
        }
        /*cstat +MISRAC++2008-0-1-7*/

        void _logger::writeNumStr( void ) noexcept
        {
            if ( '\0' != preFix[ 0 ] ) {
                (void)util::outputString( writeChar, preFix ); // skipcq: CXX-C1000
            }
            (void)util::outputString( writeChar, buffer ); // skipcq: CXX-C1000
            writeChar( nullptr, ' ' );
        }

        _logger& _logger::getInstance( void ) noexcept
        {
            static _logger instance;
            return instance;
        }

        void _logger::toLog( const char& v )
        {
            writeChar( nullptr, v );
        }

        void _logger::toLog( const char * s )
        {
            (void)util::outputString( writeChar, s );
        }

        void _logger::toLog( const void * const p )
        {
            /*cstat -CERT-INT36-C*/
            (void)util::unsignedToString( reinterpret_cast<uintptr_t>( p ), buffer, 16U ); // skipcq: CXX-C1000
            /*cstat +CERT-INT36-C*/
            (void)util::outputString( writeChar, "p@0x" );
            (void)util::outputString( writeChar, buffer ); // skipcq: CXX-C1000
            if ( n > 0U ) {
                (void)util::outputString( writeChar, " = [ " );
                (void)util::printXData( writeChar, const_cast<void*>( p ), n, false );
                n  = 0U;
                writeChar( nullptr, ']' );
            }
            writeChar( nullptr, ' ' );
        }

        void _logger::toLog( const float32_t& v )
        {
            (void)util::floatToString( v, buffer, precision ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar, buffer ); // skipcq: CXX-C1000
            writeChar( nullptr, ' ' );
        }

        void _logger::toLog( const float64_t& v )
        {
            (void)util::doubleToString( v, buffer, precision ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar, buffer ); // skipcq: CXX-C1000
            writeChar( nullptr, ' ' );
        }

        void _logger::toLog( const lout_base& f )
        {
            base = f.base;
            switch( f.base ) {
                case 2U:
                    (void)util::strcpy( preFix, "0b", sizeof(preFix) ); // skipcq: CXX-C1000
                    break;
                case 8U:
                    (void)util::strcpy( preFix, "0", sizeof(preFix) ); // skipcq: CXX-C1000
                    break;
                case 16U:
                    (void)util::strcpy( preFix, "0x", sizeof(preFix) ); // skipcq: CXX-C1000
                    break;
                default:
                    (void)memset( preFix, 0, sizeof(preFix) ); // skipcq: CXX-C1000
                    break;
            }
        }

        void _logger::toLog( const mem& m )
        {
            n = m.n;
        }

        void _logger::toLog( const pre& m )
        {
            precision = m.precision;
        }

        void _logger::toLog( const task& t )
        {
            (void)util::unsignedToString( t.getID(), buffer, 10 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , "T{ \"" );
            (void)util::outputString( writeChar , t.getName() );
            (void)util::outputString( writeChar , "\", " );
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , ", " );
            (void)util::outputString( writeChar , ( taskState::ENABLED_STATE == t.getState() )? "enabled" : "disabled" );
            (void)util::outputString( writeChar , " } " );
        }

        void _logger::toLog( const qOS::timer& t )
        {
            (void)util::outputString( writeChar , "t{ E:" );
            (void)util::unsignedToString( t.elapsed(), buffer, 10 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , ", R: " );
            (void)util::unsignedToString( t.remaining(), buffer, 10 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , " } " );
        }
        /*cstat -CERT-INT36-C*/
        void _logger::toLog( const qOS::stateMachine& sm )
        {
            (void)util::outputString( writeChar , "SM{ T: 0x" );
            (void)util::unsignedToString( reinterpret_cast<uintptr_t>( &sm.getTop() ), buffer, 16 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , ", C: 0x" );
            (void)util::unsignedToString( reinterpret_cast<uintptr_t>( sm.getCurrent() ), buffer, 16 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , " } " );
        }

        void _logger::toLog( const qOS::sm::state& s )
        {
            (void)util::outputString( writeChar , "s{ 0x" );
            (void)util::unsignedToString( reinterpret_cast<uintptr_t>( &s ), buffer, 16 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , " } " );
        }

        void _logger::toLog( const qOS::trigger& t )
        {
            static const char *str[ static_cast<int>( qOS::input::event::MAX_EVENTS ) + 1 ] = { // skipcq: CXX-W2066
                "None ", "byTimeElapsed ", "byNotificationQueued ", "byNotificationSimple ",
                "byQueueReceiver ", "byQueueFull ", "byQueueCount ", "byQueueEmpty ",
                "byEventFlags ", "bySchedulingRelease ", "byNoReadyTasks "
            };
            (void)util::outputString( writeChar , str[ static_cast<int>( t ) ] ); // skipcq: CXX-C1000
        }

        void _logger::toLog( const qOS::input::channel& in )
        {
            static const char *str[ static_cast<int>( qOS::input::event::MAX_EVENTS ) ] = { // skipcq: CXX-W2066
                "NONE ", "EXCEPTION ", "ON_CHANGE ", "FALLING_EDGE ", "RISING_EDGE ",
                "PULSATION_DOUBLE ", "PULSATION_TRIPLE ", "PULSATION_MULTI ",
                "HIGH_THRESHOLD ", "LOW_THRESHOLD ", "IN_BAND ", "STEADY_IN_HIGH ",
                "STEADY_IN_LOW ", "STEADY_IN_BAND ", "DELTA ", "STEP_UP ", "STEP_DOWN "
            };
            const auto e = in.getEvent();
            (void)util::outputString( writeChar , "in{ 0x" );
            (void)util::unsignedToString( reinterpret_cast<uintptr_t>( &in ), buffer, 16 ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , ( qOS::input::type::ANALOG_CHANNEL == in.getType() ) ? ", ANALOG, C: " : ", DIGITAL, C: " );
            /*cstat -MISRAC++2008-5-0-8*/
            (void)util::unsignedToString( static_cast<unsigned_t>( in.getChannel() ), buffer, 10 ); // skipcq: CXX-C1000
            /*cstat +MISRAC++2008-5-0-8*/
            (void)util::outputString( writeChar , buffer ); // skipcq: CXX-C1000
            (void)util::outputString( writeChar , " } " );
            if ( qOS::input::event::NONE != e ) {
                (void)util::outputString( writeChar , str[ static_cast<int>( e ) ] ); // skipcq: CXX-C1000
            }
        }

        /*cstat +CERT-INT36-C*/
        void _logger::toLog( const qOS::string & s )
        {
            (void)util::outputString( writeChar, s.c_str() );
        }

        ChainLoggerProxy::~ChainLoggerProxy()
        {
            (void)util::outputString( parent.writeChar, logger::end );
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const char& v )
        {
            parent.toLog( v );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const char * s )
        {
            parent.toLog( s );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const void * const p )
        {
            parent.toLog( p );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const lout_base& f )
        {
            parent.toLog( f );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const mem& m )
        {
            parent.toLog( m );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const pre& m )
        {
            parent.toLog( m );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const task& t )
        {
            parent.toLog( t );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const qOS::timer& t )
        {
            parent.toLog( t );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const qOS::stateMachine& sm )
        {
            parent.toLog( sm );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const qOS::sm::state& s )
        {
            parent.toLog( s );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const qOS::trigger& t )
        {
            parent.toLog( t );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const qOS::input::channel& in )
        {
            parent.toLog( in );
            return *this;
        }
        ChainLoggerProxy& ChainLoggerProxy::operator<<( const qOS::string & s )
        {
            parent.toLog( s );
            return *this;
        }
    }

}
