//-----------------------------------------------------------------------------
///
/// \brief  A WS2814 LED bar
///
///         [...]
///
/// \date   20200322
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


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


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

#include <biwak/ws2814.h>
#include <biwak/timer.hpp>
#include <stdlib.h>           // malloc
#include <memory.h>           // memset
#include <biwak/exception.h>


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


/* static */
constexpr uint8_t CWs2814::rgbWheel [][3];

CWs2814::CWs2814( CTimer &timer, int ledCount )
   :m_timer(timer)
   ,m_ledCount(ledCount)
   ,m_rgbPos(0)
{
   // The additional LEDs are needed as reset/trigger pause
   // 4 are already 116us, >50us are neccessary according to data sheet
   // But even 8 additional pseudo-LEDs(ca. 235us) are not enough.
   // LED-Revision issue?
   //m_dataSize=( ( ledCount + 10 ) * bitsPerLed );
   m_dataSize=( ( ledCount ) * bitsPerLed ) + 1 /*Zero-out*/;
   int period;
   m_data=(TIMER_TYPE *)malloc( sizeof(m_data[0])*m_dataSize );
   if(!m_data)
   {
      // "OOM"
      exception( );
   }

   m_timer.updateFrequency( 800000 - 700000 );
   period=m_timer.getPeriod();

   //int div=3;
   //m_trueValue=(period*(div-1))/div;
   //m_falseValue=period/div;

   // T0: 0.4us High, 0.85us low
   // T1: 0.8us High, 0.45us low
   // 1,25us/3,125 = 0,4; period: 19,2
   m_falseValue=(period*1000) / 3125;
   // 1,25us/1,562 =  0,80025608; period: 38,412292
   m_trueValue=(period*1000) / 1562;

   // T0: 0.35us High, 0.8us low
   // T1: 0.7us High, 0.6us low
   // 1,25us/3,125 = 0,4; period: 19,2
   m_falseValue=(period*1000) / 3125;
   // 1,25us/1,562 =  0,80025608; period: 38,412292
   m_trueValue=(period*1000) / 1562;

   // Way more stable for 48Mhz:
   m_falseValue=15;
   m_trueValue=45;

   // T0H=0,3ns; T1H=0,95ns
   m_falseValue=( 100 / ( USECS_PER_SEC / (timerClockFrequency() / 1000 ) ) );
   m_trueValue= ( 900 / ( USECS_PER_SEC / (timerClockFrequency() / 1000 ) ) );

   //m_falseValue=(period*1000) / 3571;
   //m_falseValue= period / 4;
   //m_trueValue=period-m_falseValue;

   memset( m_data, 0, m_dataSize*sizeof(m_data[0]) );
   updateDmaMap();

   return;
}


void CWs2814::writeValue( TIMER_TYPE *data, uint8_t value)
{
   for(int i1=0; i1<8; i1++)
   {
      data[i1]=( value >> ( 7-i1 ) ) & 1 ? m_trueValue : m_falseValue;
   }
}


void CWs2814::writePixel( int number, uint8_t r, uint8_t g, uint8_t b)
{
   writeValue( &m_data[number * bitsPerLed], g);
   writeValue( &m_data[number * bitsPerLed +8 ], r);
   writeValue( &m_data[number * bitsPerLed +16 ], b);
}


void CWs2814::updateDmaMap(  )
{
   int rgbWheelSize=sizeof(rgbWheel)/sizeof(rgbWheel[0]);
   // Diff just limits the maximum power of the LEDs. 8 is ok for direct eye
   // contact. 4 is already heavy in evening ambient.
   int div=8;

   for(int i1=0; i1<m_ledCount; i1++)
   {
      int index=m_rgbPos;
      index=(m_rgbPos+i1)%rgbWheelSize;

      writePixel( i1,
#if 1
               rgbWheel[index][0] / div, rgbWheel[index][1] / div, rgbWheel[index][2] / div );
#else
               // 0, 0xff, 0); /*red*/
               // 0xff , 0, 0); /*blue*/
               // 0 , 0, 0xff); /*green*/
               // 0 , 0xff, 0xff); /*Yellow*/
               0x1 , 0x1, 0); /*Yellow*/
#endif

   }
   m_rgbPos++;
   m_rgbPos%=rgbWheelSize;
   return;
}

void CWs2814::white(  )
{
   for(int i1=0; i1<m_ledCount; i1++)
   {
      writePixel( i1, 0xFF, 0xFF, 0xFF);
   }
   return;
}

void CWs2814::black(  )
{
   for(int i1=0; i1<m_ledCount; i1++)
   {
      writePixel( i1, 0x00, 0x00, 0x00);
   }
   return;
}

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