#if ! defined ( LIBOCELLI_ICON )
#define LIBOCELLI_ICON
//----------------------------------------------------------------------------
///
/// \file   icon.hpp
///
/// \brief  class for graphical icon
///
/// \date   20220919
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


//---Documentation------------------------------------------------------------


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


//---General--------------------------

//#include <lepto/list.h>

//--- Own ----------------------------

#include <ocelli/widget.hpp>
#include <ocelli/icon_catalog.h>
//#include <biwak/flash.h>
#include <biwak/flash_spi.hpp>
#include <lepto/log.h>
#include <cassert>
#include <biwak/graphic_types.h>

//---Declaration---------------------------------------------------------------

struct __attribute__((__packed__)) SRgba
{
   TRgb    pixel;
   uint8_t alpha;
};

struct __attribute__((__packed__)) SAlpha
{
   uint8_t alpha;
};

struct SIcon
{
   int width;
   int height;
   //SRgba data[];
   unsigned char data[];
};

struct SIconMono
{
   int width;
   int height;
   //SRgba data[];
   unsigned char data[];
};


class OIcon
{
   protected:
      int m_width;
      int m_height;
   public:
      OIcon()
         :m_width()
         ,m_height()
      {
      }
      constexpr OIcon( int width, int height)
          :m_width( width )
          ,m_height( height )
      {
      }
      virtual void draw(int x, int y) const = 0;
      int getWidth() const
      {
         return( m_width );
      }
      int getHeight() const
      {
         return( m_height );
      }
};


class OIconDrawer
{

public:

   static constexpr uint8_t blend(uint8_t fg, uint8_t bg, uint8_t alpha)
   {
      return  ( ( fg * alpha ) / 0xFF ) + ( ( bg * ( 0xFF - alpha ) ) / 0xFF );
   }

   static TRgb blend( const TRgb &fg, const TRgb &bg, uint8_t alpha)
   {
      return(
               TRgb{ blend(fg.red, bg.red, alpha),
                     blend(fg.green, bg.green, alpha),
                     blend(fg.blue, bg.blue, alpha)
               }
               );
   }


public:
   OIconDrawer()
   {
      assert( blend(0xFF, 0x00, 0xFF) == 0xFF );
      assert( blend(0xFF, 0x10, 0x00) == 0x10 );
   }
   static void draw(int x, int y, const SIcon &icon )
   {
      const TRgb &bg = OWidget::display().getBg();
      static TRgb line[48];
      SRgba *pixel;

      OWidget::display().setFrame(x, y, x+icon.width-1, y+icon.height-1);
      for(int i1=0; i1<icon.height; i1++)
      {
         for(int i2=0; i2<icon.width; i2++)
         {
            int offset=((i1*icon.width)+i2) * sizeof( SRgba );
            pixel=(SRgba *)&icon.data[offset];
            line[i2]=blend(pixel->pixel, bg, pixel->alpha);
         }
         OWidget::display().drawPixels(line, icon.width);

      }
   }

   static void draw(int x, int y, const SIconMono &icon, const TRgb &fg, const TRgb &bg)
   {
      static TRgb line[48];
      SAlpha *pixel;

      OWidget::display().setFrame(x, y, x+icon.width-1, y+icon.height-1);
      for(int i1=0; i1<icon.height; i1++)
      {
         for(int i2=0; i2<icon.width; i2++)
         {
            int offset=((i1*icon.width)+i2) * sizeof( SAlpha );
            pixel=(SAlpha *)&icon.data[offset];
            line[i2]=blend(fg, bg, pixel->alpha);
         }
         OWidget::display().drawPixels(line, icon.width);

      }
   }
};

struct SIconDesc
{
   EImageType m_type;
   //EImageSize m_size;
   EImageContent m_content;
   //flashEnumType m_reserved;
   address32_t m_headerAddress;

public:
   constexpr SIconDesc( EImageType type, /*EImageSize size,*/ EImageContent content
            , address32_t headerAddress )
      :m_type( type )
      //:m_size( size )
      ,m_content( content )
      //,m_reserved()
      ,m_headerAddress( headerAddress )
   {
   }

};

class OIconRef: public OIcon
{
   private:
      EImageFormat m_format;
      address_t m_dataAddress;
      static CFlash *m_pFlash;
      static constexpr int bufferLineWidth=240 / 10;
      static TRgb m_line[ bufferLineWidth ];
      static SRgba m_inLine[ bufferLineWidth ];

   public:
      OIconRef( )
         :m_format( )
         ,m_dataAddress(-1)
      {
      }
      constexpr OIconRef( EImageFormat format,
               int width, int height, address_t dataAddress )
         :OIcon( width, height )
         ,m_format( format )
         ,m_dataAddress( dataAddress )
      {
      }
      // Forget that i found it
      void drop()
      {
         m_dataAddress = -1;
         m_format = EImageFormat::Null;
      }
      static void setFlash(CFlash *flash)
      {
         m_pFlash=flash;
      }
      bool find( const SIconDesc& desc );
      bool find( ) const;
      static int usage();
      address_t dataAddress() const
      {
         return(m_dataAddress);
      }

      virtual void draw(int x, int y) const override
      {
         OWidget::display().setFrame(x, y, x + m_width - 1, y + m_height -1 );
         SRgba *pixel;
         const TRgb &bg = OWidget::display().getBg();
         int widthLeft;
         int offset;
         int currWidth;

         for(int i1=0; i1<m_height; i1++)
         {
            if( m_format == EImageFormat::RGB )
            {
               widthLeft=m_width;
               offset=0;
               while(widthLeft)
               {
                  currWidth=MIN( widthLeft, bufferLineWidth );
                  m_pFlash->readData( dataAddress() + (i1*(3*m_width)) + offset,
                                      m_line, 3*currWidth);
                  OWidget::display().drawPixels( m_line, currWidth );
                  offset+=3*currWidth;
                  widthLeft-=currWidth;
               }
            }
            else if( m_format == EImageFormat::RGBA )
            {
               widthLeft=m_width;
               offset=0;
               while(widthLeft)
               {
                  currWidth=MIN( widthLeft, bufferLineWidth );
                  m_pFlash->readData( dataAddress() + (i1*(sizeof(SRgba)*m_width)) + offset, m_inLine, sizeof(SRgba)*currWidth);
                  for(int i2=0; i2 < currWidth; i2++)
                  {
                     //int offset=((i1 * m_width)+i2) * sizeof( SRgba );
                     pixel=(SRgba *)&m_inLine[i2];
                     m_line[i2]=OIconDrawer::blend( pixel->pixel, bg, pixel->alpha);
                  }
                  OWidget::display().drawPixels(m_line, currWidth);
                  offset+= sizeof(SRgba) * currWidth;
                  widthLeft-=currWidth;
               }
            }
            else
            {
               lWarning("unknown icon format: %d", (int)m_format);
            }
         }
         return;
      }
};


//---fin-----------------------------------------------------------------------
#endif // ! ? LIBOCELLI_ICON
