/*
//CS MOSI SCLK MISO   CS RES DC SDA SCL GND VCC    off -> +5V / on -> GND
// |   |    |    |     |  |  |   |   |   |   |              |
// |   |    |    |     |  |  |   |   |   |   |              |
// V   V    V    V     V  V  V   V   V   V   V              V
// 4  11   13   12    10  8  9  11  13  GND +5V             2
//	        (Arduino)                              (Arduino)

#include <ST7735.h>
#include <SD.h>
#include <SPI.h>

// If we are using the hardware SPI interface, these are the pins (for future ref)
#define sclk 13 // SCL
#define mosi 11 // MOSI

// You can also just connect the reset pin to +5V (we do a software reset)
#define rst 8 // RES

// these pins are required
#define cs 10
#define dc 9

// Color definitions
#define	BLACK           0x0000
#define	BLUE            0x001F
#define	RED             0xF800
#define	GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0  
#define WHITE           0xFFFF

// to draw images from the SD card, we will share the hardware SPI interface
ST7735 tft = ST7735(cs, dc, rst);

// For Arduino Uno/Duemilanove, etc
//  connect the SD card with MOSI going to pin 11, MISO going to pin 12 and SCK going to pin 13 (standard)
//  Then pin 4 goes to CS (or whatever you have set up)
#define SD_CS 4    // Set the chip select line to whatever you use (4 doesnt conflict with the library)

// the file itself
File bmpFile;

const int buttonPin = 2;
int buttonState = 0; // Status von Knoepfchen
// information we extract about the bitmap file
int bmpWidth, bmpHeight;
uint8_t bmpDepth, bmpImageoffset;

void setup(void) {
  Serial.begin(9600);
  
   
  pinMode(cs, OUTPUT);
  pinMode(buttonPin, INPUT);
  
  digitalWrite(cs, HIGH);
     
  // initialize a ST7735R TFT
  tft.initR();      // change this to initB() for ST7735B TFT's  
  
  Serial.print("Initializing SD card...");

  if (!SD.begin(SD_CS)) {
    Serial.println("failed!");
    return;
  }
  Serial.println("SD OK!");
  
  bmpFile = SD.open("bendx.bmp");

  if (! bmpFile) {
    Serial.println("didnt find image");
    while (1);
  }
  
  if (! bmpReadHeader(bmpFile)) { 
     Serial.println("bad bmp");
     return;
  }
  
  Serial.print("image size "); 
  Serial.print(bmpWidth, DEC);
  Serial.print(", ");
  Serial.println(bmpHeight, DEC);
  
  tft.fillScreen(BLACK);
  bmpdraw(bmpFile, 0, 0);
  
  pinMode(buttonPin, INPUT);
}

void loop() {
  buttonState = digitalRead(buttonPin);

  if(buttonState == LOW){
    Serial.println("Knoepfche gedrueckt !");
    zeigeText();
    bmpdraw(bmpFile, 0, 0);
  }
}

void zeigeText(){
  tft.fillScreen(BLACK);
  tft.drawString(0, 0,    "Hallo lieber Luca,", YELLOW);
  delay(2000);
  for (int i = 0; i < 5; i++){
    tft.drawString(0, 20, "*                   *", BLACK);
    delay(200);
    tft.drawString(0, 20, "*  Glueckwunsch !!  *", RED);
    delay(200);
  }
  tft.drawString(0, 50, "nachtraeglich", WHITE);
  tft.drawString(0, 60, "       zum          ", WHITE);
  tft.drawString(0, 70, "    Geburtstag!    ", BLUE);
  delay(3000);
  
  tft.drawString(0, 120, "Viele Gruesse,      ", RED);
  tft.drawString(0, 130, "Henning", GREEN);
  delay(10000);
}
/*********************************************/
// This procedure reads a bitmap and draws it to the screen
// its sped up by reading many pixels worth of data at a time
// instead of just one pixel at a time. increading the buffer takes
// more RAM but makes the drawing a little faster. 20 pixels' worth
// is probably a good place

#define BUFFPIXEL 20

void bmpdraw(File f, int x, int y) {
  bmpFile.seek(bmpImageoffset);
  
  uint32_t time = millis();
  uint16_t p; 
  uint8_t g, b;
  int i, j;
  
  uint8_t sdbuffer[2 * BUFFPIXEL];  // 3 * pixels to buffer
  uint8_t buffidx = 2*BUFFPIXEL;
  
  Serial.print("rotation = "); Serial.println(tft.getRotation(), DEC);
  
  //set up the 'display window'
  tft.setAddrWindow(x, y, x+bmpWidth-1, y+bmpHeight-1);
  
  uint8_t rotback = tft.getRotation();
  //tft.setRotation(180);
  
  for (i=0; i< bmpHeight; i++) {
  //for (i=bmpHeight; i> 0; i--) {
    // bitmaps are stored with the BOTTOM line first so we have to move 'up'
  
    for (j=0; j<bmpWidth; j++) {
      // read more pixels
      //if (buffidx >= 2*BUFFPIXEL) {
        //bmpFile.read(sdbuffer, 3*BUFFPIXEL);
        //buffidx = 0;
        sdbuffer[0] = bmpFile.read();
        sdbuffer[1] = bmpFile.read();
        sdbuffer[2] = bmpFile.read();
        //Serial.print(sdbuffer[0], HEX);
        //Serial.print(sdbuffer[1], HEX);
        //Serial.print(sdbuffer[2], HEX);
      //}
      //p = (sdbuffer[1] << 8) | sdbuffer[0];
      //buffidx++;
      //buffidx++;
      // convert pixel from 565 to 888
      //b = sdbuffer[0] & 0x1f;
      //g = (sdbuffer[0] >> 5) | (sdbuffer[1] << 5);
      //r = sdbuffer[1] >> 3;
      
      // convert pixel from 888 to 565
      b = sdbuffer[0];     // blue
      g = sdbuffer[1];     // green
      p = sdbuffer[2];     // red
      
      p >>= 3;
      p <<= 6;
      
      g >>= 2;
      p |= g;
      p <<= 5;
      
      b >>= 3;
      p |= b;
     //Serial.print(p, HEX);
      // write out the 16 bits of color
      //tft.drawPixel(i, j, p);
      tft.pushColor(p);
    }
  }
  Serial.print(millis() - time, DEC);
  Serial.println(" ms");
}

boolean bmpReadHeader(File f) {
   // read header
  uint32_t tmp;
  
  if (read16(f) != 0x4D42) {
    // magic bytes missing
    return false;
  }
 
  // read file size
  tmp = read32(f);  
  Serial.print("size 0x"); Serial.println(tmp, HEX);
  
  // read and ignore creator bytes
  read32(f);
  
  bmpImageoffset = read32(f);  
  Serial.print("offset "); Serial.println(bmpImageoffset, DEC);
  
  // read DIB header
  tmp = read32(f);
  Serial.print("header size "); Serial.println(tmp, DEC);
  bmpWidth = read32(f);
  bmpHeight = read32(f);

  
  if (read16(f) != 1)
    return false;
    
  bmpDepth = read16(f);
  Serial.print("bitdepth "); Serial.println(bmpDepth, DEC);

  if (read32(f) != 0) {
    // compression not supported!
    return false;
  }
  
  Serial.print("compression "); Serial.println(tmp, DEC);

  return true;
}

/*********************************************/

// These read data from the SD card file and convert them to big endian 
// (the data is stored in little endian format!)

// LITTLE ENDIAN!
uint16_t read16(File f) {
  uint16_t d;
  uint8_t b;
  b = f.read();
  d = f.read();
  d <<= 8;
  d |= b;
  return d;
}


// LITTLE ENDIAN!
uint32_t read32(File f) {
  uint32_t d;
  uint16_t b;
 
  b = read16(f);
  d = read16(f);
  d <<= 16;
  d |= b;
  return d;
}