//----------------------------------------------------------------------------
///
/// \file   clock.hpp
///
/// \brief  class for graphical clock widget
///
/// \date   20220619
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


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


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


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

#include <math.h>

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

#include <ocelli/clock.hpp>
#include <lepto/log.h>


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


struct SClockStyle
{
   bool filledDots;
   TRgb dotColor;
   TRgb msecColor;
   TRgb secColor;
   int dotRadius;

   TRgb minColor;
   int minDotRadius;

   TRgb hourColor;
   int hourDotRadius;

   TRgb markerColor;
};

#define RGBMAX    0xFF

const TRgb midLightBlue = {.red=RGBMAX/4, .green=RGBMAX/4,  .blue=RGBMAX};

const SClockStyle style=
{
   .filledDots=true,
   .dotColor=darkBlue,
   .msecColor=white,

   .secColor=green,
   .dotRadius=3,

   .minColor=cyan,
   .minDotRadius=3,

   .hourColor=yellow,
   .hourDotRadius=3,

   .markerColor=midLightBlue,
};


OClock::OClock(int x1, int y1, int x2, int y2, const char *text)
   :OWidget(x1, y1, x2, y2)
   ,m_text(text)
   ,m_state("RUNNING")
   // \xDC = Ü
   // \xDF = ß
   ,m_topic("WEI\xDFW\xDCRSCHT")
{
   calcDots(secDots, (width()/2) - (style.minDotRadius+1));
   calcDots(minDots, (width()/2) - (style.minDotRadius+1) -
         (width()/16) );

}


void OClock::calcDots(OPoint *dots, int radius)
{
    int x0= centerX();
    int y0= centerY(); // + 16;

    for(int i1=0; i1<60; i1++)
    {
       float pos=( ((2*M_PI)/60)*i1);
       dots[i1]={ (int)(x0+(sin(pos)*radius)), (int)(y0+(-cos(pos)*radius) ) };
    }
}


void OClock::updateSecDot(int sec, eKind kind) const
{
   const TRgb *color;
   lAssert(sec<60);
   switch(kind)
   {
      case (eKind::sec):
         color = &style.secColor;
         break;
      case (eKind::subSec):
         color = &style.msecColor;
         break;
      case (eKind::reset):
      default:
         color = ( ! (sec%5) ) ? &style.markerColor : &style.dotColor;
         break;
   }
   m_display->drawCircle(
            secDots[sec].x,secDots[sec].y, style.dotRadius, style.filledDots,
              *color );
}


void OClock::updateMinDot(int min, eKind kind) const
{
   lAssert(min<60);
   if( kind == eKind::reset )
   {
      m_display->drawCircle(
                     minDots[min].x,minDots[min].y,
                     style.minDotRadius, style.filledDots,black);

      m_display->drawCircle(minDots[min].x,minDots[min].y, 1, true , grey);
   }
   else
   {
      m_display->drawCircle(minDots[min].x, minDots[min].y,
                        style.minDotRadius, style.filledDots,style.minColor);
   }
}

void OClock::draw() /* virtual override */
{
   m_display->setFg( red );
   m_display->setBg( darkDarkViolet );
   m_display->clear( black );
   m_display->setDrawBackground(true);

   m_display->drawCircle( centerX() , centerY(),
               (width()/2) - (style.minDotRadius+1) - (width()/16) - 6 ,
               true, darkDarkViolet );

   m_display->setFont( getFont11() );
   m_display->drawText( centerX() , (centerY()/1) - ( (centerY()*8) / 16 ) + 4 ,
                        m_text, eTextFlagsHCenter | eTextFlagsVCenter );

   for(int i1=0; i1<60; i1++)
   {
      updateSecDot(i1, eKind::reset);
      updateMinDot(i1, eKind::reset);
   }

   drawTopic( );
   drawState( );
   drawIcon( );

   return;
}

void OClock::update(int hour, int min, int sec, int subSec)
{
   bool updateSubSec=false;
   bool updateSec=false;

   m_display->setBg( black );

   if( min != m_min )
   {
      if(m_min>=0)
      {
         updateMinDot(m_min, eKind::reset);
      }
      m_min=min;
      updateMinDot(m_min, eKind::min);
   }

   if ( ( subSec == m_sec ) || ( sec == m_subSec ) )
   {
        updateSubSec = updateSec = true;
   }

   if( sec != m_sec )
   {
      updateSec=true;
   }

   if( subSec != m_subSec )
   {
      updateSubSec=true;
   }

   if( updateSec )
   {
         updateSecDot(m_sec, eKind::reset);
   }

   if( updateSubSec )
   {
         updateSecDot(m_subSec, eKind::reset);
   }

   if( updateSec )
   {
      m_sec=sec;
      updateSecDot(m_sec, eKind::sec);
   }

   if( updateSubSec )
   {
      m_subSec=subSec;
      updateSecDot(m_subSec, eKind::subSec);
   }

   if( updateSec )
   {
      char buf[10];
      snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour, m_min, m_sec);
      m_display->setFg( cyan );
      m_display->setFont( getFont8());
      m_display->setDrawBackground( true );
      m_display->drawText( centerX() , centerY()+16-(24),
                           buf, eTextFlagsHCenter | eTextFlagsVCenter );
   }
}

void OClock::drawTopic( ) const
{
   m_display->setFg(yellow);
   m_display->setBg( darkDarkViolet );
   m_display->setFont( getFont11() );
   m_display->drawText( centerX() , centerY()+16,
                        m_topic, eTextFlagsHCenter | eTextFlagsVCenter );
}

void OClock::drawState( ) const
{
   m_display->setFg( green );
   m_display->setBg( darkDarkViolet );
   m_display->setFont( getFont8());
   m_display->setDrawBackground( true );
   m_display->drawText( centerX() , centerY()+16+(24),
                        m_state, eTextFlagsHCenter | eTextFlagsVCenter );
}

void OClock::setTopic(const char *topic)
{
   m_topic = topic;
}

void OClock::setState( const char *state )
{
   m_state = state;
}

void OClock::drawIcon(  ) const
{
   if ( m_pIcon )
   {
      m_pIcon->draw(120-24, 40);
   }
}



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