//----------------------------------------------------------------------------
//
// \brief   functions for printing text with small footprint
//
//----------------------------------------------------------------------------


//---Includes-----------------------------------------------------------------


#include <lepto/print.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>     // malloc
#include <lepto/lepto.h>


//---Implementation-----------------------------------------------------------


class CPrinter
{
   private:

   public:
      FILE* m_stream;
      char* m_dest;
      int m_last;
      int m_base;
      int m_count;
      bool m_signed;

      CPrinter( FILE * stream, char *__s, size_t __n )
         :m_stream( stream )
         ,m_dest( __s )
         ,m_last( __n - 1 )
         ,m_base( 10 )
         ,m_count( 0 )
         ,m_signed( true )
      {};

      int getDigits( uint32_t  val )
      {
         int digits=1;
         while( ( val /= m_base ) )
         {
            digits++;
         }
         return( digits );
      }

      void addCh(const char c)
      {
         if(m_stream)
         {
            fputc(c, m_stream);
         }
         if( m_dest )
         {
            if( m_count >= m_last )
            {
               return;
            }
            *m_dest++=c;
         }
         m_count++;
      };

      int printInt( uint32_t val )
      {
         int digits;
         char c;
         if(m_signed)
         {
            if((int)val<0)
            {
               addCh('-');
               val=-val;
            }
         }
         digits=getDigits( val );
         int maxLen=MIN(digits, m_last - m_count );
         for(int i1=0; i1< maxLen; i1++)
         {
            int divider = 1;
            for(int i2=0; i2 < (digits-1-i1 ); i2++)
               divider*=m_base;
            int digit=(val / divider) & 0xF;
            if( digit <= 9 )
               c='0'+digit;
            else
               c=('A'-0xA)+digit;
            addCh(c);
            val-=(digit*divider);
         }
         return(m_count);
      }

      int vprintf(const char *format, va_list args)
      {
         int pos=0;

         while( format[pos] && ( m_count < m_last ) )
         {
            switch(format[pos])
            {
               case '%':
               {
                  bool done=false;
                  m_base=10;
                  m_signed=true;

                  while(!done)
                  {
                     pos++;
                     switch(format[pos])
                     {
                        case 'X':
                        case 'x':
                        case 'p':
                           m_base=16;
                           m_signed=false;
                        case 'd':
                           printInt( va_arg(args, uint32_t) );
                           done=true;
                           break;
                        case 's':
                        {
                           const char *str=va_arg(args, const char *);
                           if(m_dest)
                           {
                              while( ( m_count < m_last ) && *str )
                              {
                                 addCh( *str++ );
                              }
                           }
                           if(m_stream)
                           {
                              fputs( str, m_stream );
                           }
                           m_count+=strlen(str);
                           done=true;
                           break;
                        }
                        case 'c':
                        {
                           char _ch=va_arg(args, int);
                           addCh( _ch );
                           done=true;
                           break;
                        }
                        case '%':
                        {
                           addCh( format[pos] );
                           done=true;
                           break;
                        }
                        // Fields to be ignored
                        case '0'...'9':
                        case 'l':
                           continue;
                           break;
                        default:
                           addCh( '%' );
                           addCh( format[pos] );
                           done=true;
                           break;
                     }
                  }
                  break;
               }
               case '\n':
                  addCh( format[pos] );
                  addCh( '\r' );
                  break;
               default:
                  addCh( format[pos] );
                  break;
            }
            pos++;
         }
         if( m_dest )
         {
            *m_dest++=0;
         }

         return(m_count);
      };
};

int lepto_vfprintf ( FILE * stream, const char * format, va_list args )
{
   CPrinter p(stream, nullptr, 6000);

   return( p.vprintf( format, args) );
}

int lepto_vsnprintf(char *__s, size_t __n, const char *format, va_list args)
{
   CPrinter p(nullptr, __s, __n);

   return( p.vprintf( format, args) );
}

int lepto_dummy_vfprintf ( FILE * stream, const char * format, va_list args )
{
   return( 1 );
}

int lepto_dummy_vsnprintf(char *__s, size_t __n, const char *format, va_list args)
{
   return( 1 );
}


int l_vsnprintf(char *__s, size_t __n, const char *__fmt, va_list ap) __attribute__((alias("lepto_vsnprintf")));
int l_vfprintf ( FILE * stream, const char * format, va_list arg ) __attribute__((alias("lepto_vfprintf")));


void hexDump(const void *_buf, int size, bool printHeader /*=true*/)
{
   uint8_t *buf=(uint8_t *)_buf;
   if( printHeader )
   {
      printf("    | -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F\n");
      printf("----|------------------------------------------------\n");
   }
   int rows=size/0x10;
   int off=0;
   if( size % 0x10 )
   {
      rows++;
   }

   for(int i1=0; i1<rows; i1++)
   {
      int line=0;
      printf(" %X- | ", i1);
      for(int i2=0; (i2<0x10) && ( off < size); i2++)
      {
         printf("%02X ", buf[off]);
         off++;
         line++;
      }
      off-=line;
      printf(" | ");

      for(int i2=0; (i2<0x10) && ( off < size); i2++)
      {
         if( ( buf[off] >= 0x20 ) && ( buf[off] < 0xFF ) )
         {
            printf("%c", buf[off]);
         }
         else
         {
            printf(".");
         }
         off++;
      }

      printf("\n");
   }

   return;
}


int lepto_snprintf(char* dest, size_t len, const char *format, ... )
{
   va_list argp;
   va_start(argp, format);

   int ilen=lepto_vsnprintf(dest, len, format, argp);

   va_end(argp);

   return( ilen );
}


int lepto_dummy_snprintf(char* dest, size_t len, const char *format, ... )
{
    return(1);
}


//---fin----------------------------------------------------------------------
