//-----------------------------------------------------------------------------
///
/// \brief  Newhaven Display
///
///         SPI, Chip-Select and Data-Command wires needed.
///
///         #1    GND
///         #2    +3V3
///         #4    Data/Instr -> GPIO4
///         #7    SCLK
///         #8    MOSI
///         #16   /Reset; +3V3
///         #17   /CS -> GPIO3
///
/// \date   20151226
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


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


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

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

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

#include <biwak/stm32/gdisplay_nh.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>


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


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


const TCommandData initInstructions[]=
{
    { 0xFD, 2                           // Command lock setting
         ,0x12},                             // unlock
    { 0xFD, 2      // Command lock setting
         ,0xB1},         // unlock
    { 0xAE, 1},
    { 0xB3, 2     // Command lock setting
         ,0xF1},         // clock=Diviser+1 frequency=fh
    { 0xCA, 2        // Duty
         ,0x7F},         // OLED_END+1
    { 0xA2, 2        // Display offset
         ,0x00},

    { 0xA1, 2        //Set display start line
         ,0x00},         //0x00 start line
    { 0xA0, 2        //Set Re-map, color depth
         ,0xA0},         //8-bit 262K
    { 0xB5, 2        //set GPIO
         ,0x00},         //disabled
    { 0xAB, 2        //Function Set
         ,0x01},         //8-bit interface, internal VDD regulator
    { 0xB4, 4       //set VSL
         ,0xA0          //external VSL
         ,0xB5
         ,0x55},
    { 0xC1, 4        //Set contrast current for A,B,C

         ,0x8a           //Color A
         ,0x51           //Color B
         ,0x8a},         //Color C

      /*
      ,0xff             //Color A R
      ,0x8a             //Color B G
      ,0xff},         //Color C B
      */
    { 0xC7, 2        //Set master contrast
         ,0x0f},
    { 0xB9, 1},      //use linear grayscale LUT
    { 0xB1, 2        //Set pre & dis-charge
         ,0x32},         //pre=1h, dis=1h
    { 0xBB, 2        //Set precharge voltage of color A,B,C
         ,0x07},
    { 0xB2, 4        //display enhancement
         ,0xa4
         ,0x00
         ,0x00},
    { 0xB6, 2        //precharge period
         ,0x01},
    { 0xBE, 2        //Set VcomH
         ,0x07},
    { 0xA6, 1},      //Normal display
    { 0x15, 3        //set column start and end addresses
         ,0x00         //
         ,0x7F},
    { 0x75, 3      //set row start and end addresses
      ,0x00         //
      ,0x7F},         //
    { 0x5C, 1},      //write to RAM command

    { 0xAF, 1},      //Display on
    { 0, 0, 0, 0, 0, 0}
};


#define csEnableDelay   00
#define csDisableDelay  00
#define busyDelay       00


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


/// \brief  Le contructeur
///
CGraphicDisplayNh::CGraphicDisplayNh(CSpi &_spi, EGpioPeripheral eGpioPeripheralCs, EGpioPin eGpioPinCs,
                                 EGpioPeripheral eGpioPeripheralDc, EGpioPin eGpioPinDc )
   :spi(_spi)
   ,outCs(eGpioPeripheralCs, eGpioPinCs)
   ,outDc(eGpioPeripheralDc, eGpioPinDc)
{
   busyCounter=0;
   fg=blue;
   bg=black;

   m_xSize=128;
   m_ySize=128;

   mirrored=true; // 0/0 is in the upper right corner on newhaven display

   resetDisplay();

   return;
}


CGraphicDisplayNh::~CGraphicDisplayNh()
{

}


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

   writeInsArray(initInstructions);
   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

   // We have to clean the display, display contains only random data after reset
   clear( black );
   return;
}


void CGraphicDisplayNh::enableCs()
{
   outCs.setValue(NH_CS_ACTIVE);
}


void CGraphicDisplayNh::disableCs()
{
   outCs.setValue(NH_CS_INACTIVE);
}


/// \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 CGraphicDisplayNh::checkBusy()
{
   busyCounter=0;
   disableCs();
}


void CGraphicDisplayNh::writeIns (const uint8_t data, int delayValue /*=0*/)
{
   checkBusy();
   enableCs();

   outDc.setValue(NH_DC_INSTRUCTION);
   spi.writeData( &data, 1  );

   disableCs();

   return;
}


void CGraphicDisplayNh::writeInsArray (const TCommandData *pData)
{
   int i1=0;
   outCs.setValue(NH_CS_ACTIVE);
   for(; pData[i1].len; i1++)
   {
      writeIns(pData[i1].command);
      //lHint << "Command: " << (int)pData[i1].command;
      for(int i2=0; i2<pData[i1].len-1; i2++)
      {
         writeData( (uint32_t)pData[i1].data[i2] );
         //lHint << "   Data: " << (int)pData[i1].data[i2];
      }
   }
   outCs.setValue(NH_CS_INACTIVE);

   return;
}


void CGraphicDisplayNh::writeData (const char data)
{
   checkBusy();
   outDc.setValue(NH_DC_DATA);
   enableCs();

   spi.writeData( data, 1 );

   disableCs();

   return;
}


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

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

   disableCs();

   return;
}


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


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


void CGraphicDisplayNh::clear()
{
   writeIns(0x1);

   return;
}


void CGraphicDisplayNh::clear(TRgb color)
{
   writeIns(0x15);
   writeData(0x00);
   writeData(0x7F);

   writeIns(0x75);
   writeData(0x00);
   writeData(0x7F);

   writeIns(0x5C);

   for(int i=0;i<128;i++)
      m_line[i]=color;

   for(int j=0;j<128;j++)
      writeData( (char *)&m_line[0], sizeof(m_line));

   return;
}


void CGraphicDisplayNh::gotoxyHw() /*virtual*/
{
   writeIns(0x15);
   writeData(0x00 + xPos);
   writeData(0x7F);

   writeIns(0x75);
   writeData(0x00 + yPos);
   writeData(0x7F);

   writeIns(0x5C);
}


void CGraphicDisplayNh::drawPixel( const TRgb color )
{
   writeData( (char *)&color, sizeof(color));
}


void CGraphicDisplayNh::drawPixels( const TRgb *pixels, int count)
{
   writeData((const char *)pixels, count);
}


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