/*
ssd1306 lcd controller


*/
#include <hal_types.h>
#include <stdio.h>
#include <string.h>
// Include Name definitions of individual bits and bit-fields in the CC254x device registers.
#include <ioCC254x_bitdef.h>
// Include device specific file
#include "ioCC2541.h"
#include "hal_i2c.h"
#include "stdlib.h"
#include "ssd1306.h"

#define WIDTH 72
#define HEIGHT 40
#define PAGES HEIGHT/8

#define OLED_RST  8 
//I2C
#define IIC_CMD        0X00
#define IIC_RAM        0X40
//#define command(Reg)  I2C_Write_Byte(Reg, IIC_CMD)
//#define data(Data)    I2C_Write_Byte(Data, IIC_RAM)


#define command(Reg)  I2C_Write_Byte(Reg, IIC_CMD)
#define data(Data)    I2C_Write_Byte(Data, IIC_RAM)



#define uint8_t         uint8
#define bool uint8
#define false 0
#define true 1



uint8_t ADDRESS = 0x3c;// address of the OLED
uint8_t oled_buf[WIDTH * HEIGHT / 8];


//////
//////
//////void i2c_write_byte(uint8_t value, uint8_t cmd)
//////{
//////  uint8_t addr = 0x3c;
//////  wire.begintransmission(addr);
//////  wire.write(cmd);
//////  wire.write(value);
//////  wire.endtransmission();
//////  
//////  
//////}

static void I2C_Write_Byte(uint8_t value, uint8_t cmd){
    uint8_t buf[2];
      buf[0]=cmd;
      buf[1]=value;
     HalI2CWrite(2, buf);

}

//
// Write a block of pixel data to the OLED
// Length can be anything from 1 to 1024 (whole display)
//
static void oledWriteDataBlock(uint8  *ucBuf, uint8 iLen)
{
uint8 ucTemp[129];

  ucTemp[0] = 0x40; // data command
  memcpy(&ucTemp[1], ucBuf, iLen);
  HalI2CWrite(iLen+1,ucTemp);
  
  // Keep a copy in local buffer
#ifdef USE_BACKBUFFER
  memcpy(&ucScreen[iScreenOffset], ucBuf, iLen);
  iScreenOffset += iLen;
#endif
}



//--------------------------------------------

void oled_begin(void)
{
    //pinMode(OLED_RST, OUTPUT);      
    //digitalWrite(OLED_RST, HIGH);
    //delay(10);
    //digitalWrite(OLED_RST, LOW);
    //delay(10);
    //digitalWrite(OLED_RST, HIGH);
    //HalI2CInit(uint8 address, i2cClock_t clockRate)
    HalI2CInit(ADDRESS, i2cClock_533KHZ);

    command(0xae);//--turn off oled panel
	
    command(0xd5);//--set display clock divide ratio/oscillator frequency
    command(0x80);//--set divide ratio

    command(0xa8);//--set multiplex ratio
    command(0x27);//--1/40 duty

    command(0xd3);//-set display offset
    command(0x00);//-not offset

    command(0xad);//--Internal IREF Setting	
    command(0x30);//--

    command(0x8d);//--set Charge Pump enable/disable
    command(0x14);//--set(0x10) disable

    command(0x40);//--set start line address

    command(0xa6);//--set normal display

    command(0xa4);//Disable Entire Display On

    command(0xa1);//--set segment re-map 128 to 0

    command(0xC8);//--Set COM Output Scan Direction 64 to 0

    command(0xda);//--set com pins hardware configuration
    command(0x12);

    command(0x81);//--set contrast control register
    command(0xaf);

    command(0xd9);//--set pre-charge period
    command(0x22);

    command(0xdb);//--set vcomh
    command(0x20);

    command(0xaf);//--turn on oled panel
    
}

//-------------------------------------------
void oled_pixel(uint8 x, uint8 y, uint8 color)
{
    if(x > WIDTH || y > HEIGHT)return ;
    if(color)
        oled_buf[x+(y/8)*WIDTH] |= 1<<(y%8);
    else
        oled_buf[x+(y/8)*WIDTH] &= ~(1<<(y%8));
}

//-------------------------------------------

void oled_draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8 color){
    
//    uint8_t w = WIDTH;
//    uint8_t h = HEIGHT;
    if (y0 > y1) {
        // swap to approach calcs from lower point to higher point
        uint8_t tmp = y0;
        y0 = y1;
        y1 = tmp;
        tmp = x0;
        x0 = x1;
        x1 = tmp;
    }
    int delta_x = ((int)x1) - ((int)x0);
    int delta_y = ((int)y1) - ((int)y0);
    if (delta_x == 0) {
        if (delta_y != 0) {
            // it is a straight line
            oled_pixel(x0, y0, color);
            while (delta_y--) {
                //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
                oled_pixel(x0, y0, color);
                y0++;
            }
        } else {// single pixel
            //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
            oled_pixel(x0, y0, color);
        }
        return;
    } else if (delta_y == 0) {
        if (delta_x != 0) {
            // it is a straight line
            //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
              oled_pixel(x0, y0, color);
            while (delta_x--) {
                //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
                oled_pixel(x0, y0, color);
                x0++;
            }
        } else {// single pixel
            //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
            oled_pixel(x0, y0, color);
        }
        return;
    }
    // ok both delta_x and delta_y are not zero, and the line is not horizontal
    // or vertical
    // Bresenham's algorithm using http://www.phatcode.net/res/224/files/html/ch35/35-03.html
    // calculate the length of the line in each coordinate
    int dirxn = 0;
    bool is_octant_1 = false;
    if (delta_x > 0) {
        if (delta_x > delta_y) {
            dirxn = 1;
            is_octant_1 = false;
        } else {
            dirxn = 1;
            is_octant_1 = true;
        }
    } else {
        delta_x = abs(delta_x);
        if (delta_x > delta_y) {
            dirxn = -1;
            is_octant_1 = false;
        } else {
            dirxn = -1;
            is_octant_1 = true;
        }
    }
    if (is_octant_1) {
        int delta_x2 = delta_x * 2;
        int delta_x2_y2 = delta_x2 - (int)(delta_y * 2);
        int error_term = delta_x2 - (int)delta_y;
        //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
        oled_pixel(x0, y0, color);
        while (delta_y--) {
            if (error_term >= 0) {
                x0 += dirxn;
                error_term += delta_x2_y2;
            } else {
                error_term += delta_x2;
            }
            y0++;
            //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
            oled_pixel(x0, y0, color);
        }
    } else {
        int delta_y2 = delta_y * 2;
        int delta_y2_x2 = delta_y2 - (int)(delta_x * 2);
        int error_term = delta_y2 - (int)delta_x;
        //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
        oled_pixel(x0, y0, color);
        while (delta_x--) {
            if (error_term >= 0) {
                y0++;
                error_term += delta_y2_x2;
            } else {
                error_term += delta_y2;
            }
            x0 += dirxn;
            //ssd1306_framebuffer_put_pixel(fbp, x0, y0, color);
            oled_pixel(x0, y0, color);
        }
    }
 
}

//-------------------------------------------
// sends all the display buffer to the display
void oled_display(void)
{    uint8_t page;   
    
    for (page = 0; page < PAGES; page++) {         
        command(0xB0 + page);/* set page address */     
        /* Setting the first column address to write to:
          First column is at byte 28(0b11100) 
          This number has to be devided in two nibles and send as commands
          The low nible is 0x0c=0b1100. the high nible is0b1 but this has to be adjusted for the command code 0b11 
        */      
        
        command((DISP_START_COLUMN)&0xf);   /* set low column address */
        command(((DISP_START_COLUMN)>>4)|0x10);  /* set high column address */           
        //pOffset=page*WIDTH;
        oledWriteDataBlock(&oled_buf[page*WIDTH],WIDTH);

//        for(i = 0; i< WIDTH; i++ ) {
//          //data(oled_buf[i+page*WIDTH]);// write data one
//          data(oled_buf[i+pOffset]);// write data one
//        }        
    }
}
//-------------------------------------------
void test_pixels(void){
  oled_buf[0] = 0xff;

}


//-------------------------------------------
void oled_blink_colon_new(uint8 min){
#define PAGE_1_ADDRESS  0xb1
#define PAGE_3_ADDRESS  0xb3
#define PAGE_ADDRESS_BYTE_NDX  1 // where to write the page address in the 
#define COLON_POS_LOW_BITS_BYTE_NDX  2
#define COLON_POS_HIGH_BITS_BYTE_NDX  4  
#define COLON_DATA_START_NDX   6
#define COLON_DATA_SIZE        4 // fore bytesfor colon data
  
  static uint8 colOn=0;
  static uint8 firstTime;
  uint8_t col_pos =COLON_X+DISP_START_COLUMN;
 
  //uint8_t colonData[2][4]={{0,0,0,0},{7,7,7,7}};
  
  uint8_t i2cData[]={
                          // colon On/off commands and data
                          0,0xb1, // set page address command                   // index 0,1       
                           0,   // command for colon position low bits           // index 2       
                           0,    // colon position low bits. Updated later  at this position: index 3
                           0,   // command for colon position hi bits. Updated later  at this position index 4
                           0x40, // data command at index 5
                            0,0,0,0 // actual data starts at index 6
  
          };
  


       if((min/10)%10==1){ // all this is to move the colon to the right if the right digit is '1'
        if(!(firstTime &&colOn)){ // if colOn that's mean we are going to delete the colon.If the firsTime that mean wthe colon is on the right position
          //NOP();
          col_pos+=5; // move 5 columns to the right
        }
        firstTime =0;
      }else{
        firstTime =1;
      
      }
       colOn=!colOn;
       // update the colon data according to on Off state (colonOn)
       memset(i2cData+COLON_DATA_START_NDX,colOn,COLON_DATA_SIZE);
      *(i2cData+COLON_POS_LOW_BITS_BYTE_NDX)=col_pos&0x0f;
      *(i2cData+COLON_POS_HIGH_BITS_BYTE_NDX)=(col_pos>>4)|0x10;
      HalI2CWrite(sizeof(i2cData),i2cData);
      // page 3 
       *(i2cData+PAGE_ADDRESS_BYTE_NDX)=PAGE_3_ADDRESS ;
       HalI2CWrite(sizeof(i2cData),i2cData);
//      
//        
//        command(0xB1 );/* set page address */     
//        command(col_pos&0x0f);      
//        command( (col_pos>>4)|0x10); /* set high  4 bits of  address of start column */      
// //       oledWriteDataBlock(colonData[colOn],4);
//       
//        command(0xB3 );/* set page address */     
//        command(col_pos&0x0f);      
//        command( (col_pos>>4)|0x10); /* set high  4 bits of  address of start column */      
// //       oledWriteDataBlock(colonData[colOn],4);
}      

//-------------------------------------------
void oled_blink_colon_org(uint8 min){
  static uint8 colOn=0;
  static uint8 firstTime;
  uint8_t col_pos =COLON_X+DISP_START_COLUMN;
 
  uint8_t colonData[2][4]={{0,0,0,0},{7,7,7,7}};
  


       if((min/10)%10==1){ // all this is to move the colon to the right if the right digit is '1'
        if(!(firstTime &&colOn)){ // if colOn that's mean we are going to delete the colon.If the firsTime that mean wthe colon is on the right position
          //NOP();
          col_pos+=5; // move 5 columns to the right
        }
        firstTime =0;
      }else{
        firstTime =1;
      
      }
       colOn=!colOn;
        command(0xB1 );/* set page address */     
        command(col_pos&0x0f);      
        command( (col_pos>>4)|0x10); /* set high  4 bits of  address of start column */      
        oledWriteDataBlock(colonData[colOn],4);
       
        command(0xB3 );/* set page address */     
        command(col_pos&0x0f);      
        command( (col_pos>>4)|0x10); /* set high  4 bits of  address of start column */      
        oledWriteDataBlock(colonData[colOn],4);
       
       
       
//        command(0xB1 );/* set page address */     
//        command((COLON_X+DISP_START_COLUMN)&0xf );      /* set low  4 bits of  address of start column */      
//        command( START_COL_HI_BITS); /* set high  4 bits of  address of start column */      
//        oledWriteDataBlock(colonData[colOn],4);
//       
//        command(0xB3 );/* set page address */     
//        command(START_COL_LO_BITS);      /* set low  4 bits of  address of start column */      
//        command(START_COL_HI_BITS); /* set high  4 bits of  address of start column */      
//        oledWriteDataBlock(colonData[colOn],4);
//       
       
        
        
    
  

}
//-------------------------------------------
// update the display RAM with one digit
// sends single digit display data to the controller
void oled_dig_disp_update(uint8 x,uint8 y){
  uint8_t page; 
    for (page = 0; page < PAGES; page++) {         
        command(0xB0 + page);/* set page address */     
        command((DISP_START_COLUMN+x)&0xf);   /* set low column address */
        command(((DISP_START_COLUMN+x)>>4)|0x10);  /* set high column address */           
        //pOffset=page*WIDTH;
        oledWriteDataBlock(&oled_buf[page*WIDTH+x],HOR_SEG_WIDTH+SEG_THICK+SEG_THICK);
  
  }

}
//-------------------------------------------

void oled_clear(void){
          memset(oled_buf,0,WIDTH * HEIGHT / 8);
  }
//-------------------------------------------
void oled_cmd(uint8 cmd){
  command(cmd);
}
//-------------------------------------------

