//-----------------------------------------------------------------------------
///
/// \brief  Display Elektronik
///
///         SPI, Chip-Select and Data-Command wires needed.
///
/// \date   20181026
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


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


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

#include <string.h>     // strlen
//#include <unistd.h>     // sleep

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

#include <biwak/stm32/gdisplay_dem.h>
#include <biwak/output.hpp>
#include <biwak/biwak.h>
#include <biwak/timer.hpp>      // delay
#include <biwak/spi.hpp>
#include <lepto/log.h>
#include <biwak/font.h>

#include <time.h>
#include <stdlib.h>

//---Defines-------------------------------------------------------------------


#define NH_CS_ACTIVE       false
#define NH_CS_INACTIVE     true
#define NH_DC_INSTRUCTION  false
#define NH_DC_DATA         true

#define wait_ms(a) usleep( a * USECS_PER_MSEC )
#define mswait(a) usleep( a * USECS_PER_MSEC )


const CGraphicDisplayDem::TCommandData demInitInstructions[]=
{
   { 0xEA, 0x00},                 /* Reset Power Control 1                */
    { 0xEB, 0x20},                 /* Power Control 2                      */
    { 0xEC, 0x0C},                 /* Power Control 3                      */
    { 0xED, 0xC4},                 /* Power Control 4                      */
    { 0xE8, 0x40},                 /* Source OPON_N                        */
    { 0xE9, 0x38},                 /* Source OPON_I                        */
    { 0xF1, 0x01},                 /*                                      */
    { 0xF2, 0x10},                 /*                                      */
    { 0x27, 0xA3},                 /* Display Control 2                    */

    /* Power On sequence ---------------------------------------------------------*/
    { 0x1B, 0x1B},                 /* Power Control 2                      */
    { 0x1A, 0x01},                 /* Power Control 1                      */
    { 0x24, 0x2F},                 /* Vcom Control 2                       */
    { 0x25, 0x57},                 /* Vcom Control 3                       */
    { 0x23, 0x8D},                 /* Vcom Control 1                       */

    /* Gamma settings  -----------------------------------------------------------*/
    { 0x40,0x00},   //   default setup
    { 0x41,0x00},   //
    { 0x42,0x01},   //
    { 0x43,0x13},   //
    { 0x44,0x10},   //
    { 0x45,0x26},   //
    { 0x46,0x08},   //
    { 0x47,0x51},   //
    { 0x48,0x02},   //
    { 0x49,0x12},   //
    { 0x4A,0x18},   //
    { 0x4B,0x19},   //
    { 0x4C,0x14},   //
    { 0x50,0x19},   //
    { 0x51,0x2F},   //
    { 0x52,0x2C},   //
    { 0x53,0x3E},   //
    { 0x54,0x3F},   //
    { 0x55,0x3F},   //
    { 0x56,0x2E},   //
    { 0x57,0x77},   //
    { 0x58,0x0B},   //
    { 0x59,0x06},   //
    { 0x5A,0x07},   //
    { 0x5B,0x0D},   //
    { 0x5C,0x1D},   //
    { 0x5D,0xCC},   //
   { 0x0,0x0},   //
};


#define DEM_CS_ON_DELAY    0x500
#define DEM_CS_OFF_DELAY   0x500

// #define DEM_CS_ON_DELAY    0x0
// #define DEM_CS_OFF_DELAY   0x0
#define DEM_ID          0x70
#define DEM_DATA        0x2
#define DEM_READ        0x1
#define DEM_WRITE       0x0
#define busyDelay       00



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


/// \brief  Le contructeur
///
CGraphicDisplayDem::CGraphicDisplayDem(CSpi &_spi, EGpioPeripheral eGpioPeripheralCs, EGpioPin eGpioPinCs,
                                 EGpioPeripheral eGpioPeripheralDc, EGpioPin eGpioPinDc )
   :CGraphicDisplay(240, 320)
   ,spi(_spi)
   ,outCs(eGpioPeripheralCs, eGpioPinCs)
   ,outDc(eGpioPeripheralDc, eGpioPinDc)
{
   busyCounter=0;
   m_drawMode=false;

   outDc.setValue(false);
   resetDisplay();

   return;
}


CGraphicDisplayDem::~CGraphicDisplayDem()
{

}


void CGraphicDisplayDem::resetDisplay()
{
   outCs.setValue(NH_CS_INACTIVE);

   while(0)
   {
      int id=readRegister(0x00);

      // log may be disabled; won't compile
      (void)id;
      lInfo( "ID: %d", id);

      //writeRegister( random(), random());

      usleep(100*USECS_PER_MSEC);
   };

   writeInsArray(demInitInstructions);
   //writeIns(0xAF);

   // Fade display from black to blue
   #if 0
      for(int i1=0; i1< (0xff>>2)/2; i1++)
      {
         char brightness=i1*2;
         clear( {.blue=brightness, .green=brightness / 4, .red=brightness / 4} );
      }
   #endif

#if 1
   /* Power + Osc ---------------------------------------------------------------*/
   writeRegister( 0x18, 0x36);                 /* OSC Control 1                        */
   writeRegister( 0x19, 0x01);                 /* OSC Control 2                        */
   writeRegister( 0x01, 0x00);                 /* Display Mode Control                 */
   writeRegister( 0x1F, 0x88);                 /* Power Control 6                      */
   wait_ms(5);                           /* Delay 5 ms                           */
   writeRegister( 0x1F, 0x80);                 /* Power Control 6                      */
   wait_ms(5);                         /* Delay 5 ms                           */
   writeRegister( 0x1F, 0x90);                 /* Power Control 6                      */
   wait_ms(5);                           /* Delay 5 ms                           */
   writeRegister( 0x1F, 0xD0);                 /* Power Control 6                      */
   wait_ms(5);                           /* Delay 5 ms                           */

   //writeRegister( 0x17, 0x05);                 /* Colmod 16Bit/Pixel                   */
   writeRegister( 0x17, 0x06);                 /* Colmod 18Bit/Pixel                   */

   writeRegister( 0x36, 0x00);                 /* Panel Characteristic                 */
   writeRegister( 0x28, 0x38);                 /* Display Control 3                    */
   wait_ms(40);
   writeRegister( 0x28, 0x3C);                 /* Display Control 3                    */

   writeRegister( 0x01, 0x00);                 /* Display Mode Control                 */

   writeRegister( 0x16, 0x08 );
#endif
   wait_ms(40);
   setWindow(0,0,240,320);
   // We have to clean the display, display contains only random data after reset
   clear( black );
   return;
}


void CGraphicDisplayDem::enableCs()
{
   outCs.setValue(NH_CS_ACTIVE);

   if(DEM_CS_ON_DELAY)
      usleep(DEM_CS_ON_DELAY);
}


void CGraphicDisplayDem::disableCs()
{
   outCs.setValue(NH_CS_INACTIVE);
   if(DEM_CS_OFF_DELAY)
      usleep(DEM_CS_OFF_DELAY);
}


/// \brief  checks if busy flag of display is set
///
///         only clear() causes busy to be set some time (evaluated by tests).
///         This check could be dynamically only be run if clear() (or at least
///         any instruction) was called before.
void CGraphicDisplayDem::checkBusy()
{
   //busyCounter=0;
   //disableCs();
}


void CGraphicDisplayDem::writeCommand (const char command)
{
   checkBusy();
   enableCs();

   spi.writeData( DEM_ID, 1 );
   spi.writeData( (uint32_t)command, 1  );

   disableCs();
   return;
}

void CGraphicDisplayDem::writeData (const char data)
{
   checkBusy();
   enableCs();

   spi.writeData( DEM_ID | DEM_DATA, 1 );
   spi.writeData( data, 1 );

   disableCs();

   return;
}

int CGraphicDisplayDem::readData ()
{
   checkBusy();
   enableCs();

   //spi.writeData( DEM_ID | DEM_DATA | DEM_READ );
   spi.writeData( DEM_ID  | DEM_READ, 1 );
   int ret=spi.readData( );

   disableCs();

   return(ret);
}


void CGraphicDisplayDem::writeRegister (const char reg, const char data)
{
   writeCommand(reg);
   writeData(data);
   return;
}


int CGraphicDisplayDem::readRegister (const char reg)
{
   writeCommand(reg);
   int ret=readData();
   return(ret);
}


void CGraphicDisplayDem::writeInsArray (const TCommandData *pData)
{
   int i1=0;
   outCs.setValue(NH_CS_ACTIVE);
   for(; pData[i1].command; i1++)
   {
      writeRegister(pData[i1].command, pData[i1].data);
   }
   outCs.setValue(NH_CS_INACTIVE);

   return;
}
/*
void CGraphicDisplayDem::writeData (const char *pData, int len)
{
   checkBusy();
   outDc.setValue(NH_DC_DATA);
   enableCs();

   spi.writeData( (char *)pData, len );

   disableCs();

   return;
}
*/

void CGraphicDisplayDem::setWindow(int x1, int y1, int x2, int y2)
{
   writeRegister(0x02,0x0000); // Column address start2
   writeRegister(0x03,0x0000); // Column address start1
   writeRegister(0x04,0x0000); // Column address end2
   writeRegister(0x05,0x00ef); // Column address end1
   writeRegister(0x06,0x0000); // Row address start2
   writeRegister(0x07,0x0000); // Row address start1
   writeRegister(0x08,0x0001); // Row address end2
   writeRegister(0x09,0x003f); // Row address end1
}

void CGraphicDisplayDem::putString(const char *string)
{
   int i1=0;
   while(string[i1])
   {
      writeData(string[i1++]);
   }
}


void CGraphicDisplayDem::putChar(const char ch)
{
   writeData(ch);
}


void CGraphicDisplayDem::clear()
{
   //writeIns(0x1);

   return;
}


void CGraphicDisplayDem::disableDrawMode()
{
   m_drawMode=false;
   spi.setDataSize(8);
   disableCs();
}

void CGraphicDisplayDem::enableDrawMode()
{
   if(!m_drawMode)
   {
      writeCommand(0x22);
      enableCs();
      spi.writeData(DEM_ID | DEM_WRITE | DEM_DATA);
      spi.setDataSize(6);

      m_drawMode=true;
   }
}


void CGraphicDisplayDem::clear(TRgb color)
{
   gotoxy(0,0, false);
   enableDrawMode();

   for(int i=0;i<240;i++)
   {
      m_line[i]=color;
   }

   enableCs();
   spi.writeData(DEM_ID | DEM_WRITE | DEM_DATA);    // Write : RS = 1, RW = 0
   spi.setDataSize(6);
   for(int i1=0; i1<320; i1++)
   {
      spi.writeData((uint8_t *)m_line,sizeof(m_line[0])*240);
   }
   spi.setDataSize(8);
   disableCs();

   return;
}


void CGraphicDisplayDem::dumpId()
{
   //int id=readRegister(0x00);
   //lInfo << "ID:" << id;
   while(1)
   {
      enableCs();

      spi.writeData( random());    // Write : RS = 1, RW = 0
      spi.writeData( rand(), 2 );                            // Write D0..D15

      disableCs();
   }
}

void CGraphicDisplayDem::gotoxyHw() /*virtual*/
{
   disableDrawMode();
   writeRegister(0x03, (xPos >> 0));
   writeRegister(0x02, (xPos >> 8));
   writeRegister(0x07, (yPos >> 0));
   writeRegister(0x06, (yPos >> 8));
   writeCommand(0x22);

}

#define SPI_START   (0x70)              /* Start byte for SPI transfer        */
#define SPI_RD      (0x01)              /* WR bit 1 within start              */
#define SPI_WR      (0x00)              /* WR bit 0 within start              */
#define SPI_DATA    (0x02)              /* RS bit 1 within start byte         */
#define SPI_INDEX   (0x00)              /* RS bit 0 within start byte         */

void CGraphicDisplayDem::drawPixel( TRgb color )
{
   //writeData( (char *)&color, sizeof(color));
   //dumpId();
   //return;
   //char icolor[]={0xff,0xff,0xff};

   //writeCommand(0x22);
   enableDrawMode();
   spi.writeData((uint8_t *)&color, 3);
   //disableDrawMode();
}


void CGraphicDisplayDem::drawPixels( const TRgb *pixels, int count) /* override */
{
   //writeCommand(0x22);

   //enableCs();
   //spi.writeData(DEM_ID | DEM_WRITE | DEM_DATA);
   //spi.setDataSize(6);
   enableDrawMode();
   spi.writeData((uint8_t *)pixels, 3*count);
   //disableDrawMode();
   //spi.setDataSize(8);
   //disableCs();
}

bool CGraphicDisplayDem::setRotation(bool rot) /* virtual override */
{
   disableDrawMode();
   //writeRegister(0x16, rot ? 0x68 : 0x08);
   writeRegister(0x16, rot ? 0x68 : 0x08);

#if 1
   if(rot)
   {
      writeRegister(0x02,0x0000); // Column address start2
      writeRegister(0x03,0x0000); // Column address start1
      writeRegister(0x04,0x0001); // Column address end2
      writeRegister(0x05,0x003f); // Column address end1
      writeRegister(0x06,0x0000); // Row address start2
      writeRegister(0x07,0x0000); // Row address start1
      writeRegister(0x08,0x0000); // Row address end2
      writeRegister(0x09,0x00ef); // Row address end1
   }
   else
   {
      writeRegister(0x02,0x0000); // Column address start2
      writeRegister(0x03,0x0000); // Column address start1
      writeRegister(0x04,0x0000); // Column address end2
      writeRegister(0x05,0x00ef); // Column address end1
      writeRegister(0x06,0x0000); // Row address start2
      writeRegister(0x07,0x0000); // Row address start1
      writeRegister(0x08,0x0001); // Row address end2
      writeRegister(0x09,0x003f); // Row address end1
   }
#endif
   return(true);
}


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