// this script was modified from some code from http://adafruit.com somewhere.
// piAlaPong
// 2012 James Kasper http://jk.cbwp.net!
#include "TFTLCD.h"
#include "TouchScreen.h"
#include <SD.h>

#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin

#define TS_MINX 150
#define TS_MINY 120
#define TS_MAXX 920
#define TS_MAXY 940

// For better pressure precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// For the one we're using, its 300 ohms across the X plate
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

// The control pins can connect to any pins but we'll use the 
// analog lines since that means we can double up the pins
// with the touch screen (see the TFT paint example)
#define LCD_CS A3    // Chip Select goes to Analog 3
#define LCD_CD A2    // Command/Data goes to Analog 2
#define LCD_WR A1    // LCD Write goes to Analog 1
#define LCD_RD A0    // LCD Read goes to Analog 0
/* For the 8 data pins:
Duemilanove/Diecimila/UNO/Alamode/etc ('168 and '328 chips) microcontoller:
D0 connects to digital 8
D1 connects to digital 9
D2 connects to digital 2
D3 connects to digital 3
D4 connects to digital 4
D5 connects to digital 5
D6 connects to digital 6
D7 connects to digital 7
*/

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

// In the SD card, place 24 bit color BMP files (be sure they are 24-bit!)
// There are examples in the sketch folder

// optional
#define LCD_RESET A4

// Color definitions
unsigned int colors[] = {
0x8000, // maroon
0xFA20, // red-orange
0xFD00, // orange
0x0400, // green
0x0010, // dk blue
0xF81F, // violet
0x8010, // purple
0x8222, // brown
0x8410, // grey
0x0000, // black
0xF800, // red
0xFD20, // lt orange
0xFFE0, // yellow
0x07E0, // lime
0x001F, // blue
0xFE19, // pink
0xDD1B, // lavender
0xDDD0, // lt brown
0xA514, // grey
0xFFFF  // white
};

// Adafruit TFT wiring from:
// https://www.adafruit.com/products/376
// or
// https://www.adafruit.com/products/335
TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// the file itself
File bmpFile;

// information we extract about the bitmap file
int bmpWidth, bmpHeight;
uint8_t bmpDepth, bmpImageoffset;

#define MINPRESSURE 10
#define MAXPRESSURE 1000
unsigned int bX = 120;
unsigned int bY = 160;
unsigned int bColor = colors[19];
unsigned int bDiameter = 5;

float bDirection = random(0, 360);
unsigned int bSpeed = 10;

unsigned int p1Length = 50;
unsigned int p1X = bX - p1Length/2;
unsigned int p1Y = 310;
unsigned int p1Color = colors[3];

unsigned int p2Length = 50;
unsigned int p2X = bX - p2Length/2;
unsigned int p2Y = 0;
unsigned int p2Color = colors[4];

boolean paused = false;

void setup(){
  Serial.begin(115200);
  Serial.println("piAlaPong\nBy:\nhttp://jk.cbwp.net!\n");
  Serial.println(freeRam());
  
  tft.reset();
  
  // find the TFT display
  uint16_t identifier = tft.readRegister(0x0);
/*  
  if (identifier == 0x9325) {
    Serial.println("Found ILI9325");
  } else if (identifier == 0x9328) {
    Serial.println("Found ILI9328");
  } else {
    Serial.print("Unknown driver chip ");
    Serial.println(identifier, HEX);
    while (1);
  }  
*/ 
  tft.initDisplay();

 // Serial.print("Initializing SD card...");
  pinMode(10, OUTPUT);

  if (!SD.begin(10)) {
   // Serial.println("failed!");
  } else {
/*    
    Serial.println("SD OK!");

    // background screen
    bmpFile = SD.open("bg.bmp");

    if (!bmpFile) {
      Serial.println("didnt find image");
    } else if (! bmpReadHeader(bmpFile)) { 
      Serial.println("bad bmp");
    } else {
      Serial.print("image size "); 
      Serial.print(bmpWidth, DEC);
      Serial.print(", ");
      Serial.println(bmpHeight, DEC);
 
      tft.fillScreen(BLACK);
      //tft.setRotation(2);
      bmpdraw(bmpFile, 0, 0); //120, 40);
      //tft.setRotation(0);
      bmpFile.close();
      delay(2000);
    }
*/
  }

  // clear screen
  tft.fillScreen(colors[9]);
  
  
  // for debug
  pinMode(13, OUTPUT);
  // get penRadius from analog input
  pinMode(19, INPUT); 
  
//  bmpFile = SD.open("/");
//  printDirectory(bmpFile, 0);
//  bmpFile.close();
}
float deg2rad(float d){
  float r = d * 3.141592653589793 / 180.0;
  return r;
}
float rad2deg(float r){
  float d = r * 180.0 / 3.141592653589793;
  return d;
}
void loop(){//}void ignore(){ // common setup debugger
  digitalWrite(13, HIGH);
  Point p = ts.getPoint();
  digitalWrite(13, LOW);

  // if you're sharing pins, you'll need to fix the directions of the touchscreen pins!
  //pinMode(XP, OUTPUT);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  //pinMode(YM, OUTPUT);

  // we have some minimum pressure we consider 'valid'
  // pressure of 0 means no pressing!

  if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
    /*
    // press the bottom of the screen to erase 
    if (p.y < (TS_MINY-5)) { 
      tft.fillRect(boxHeight,  boxWidth*2, tft.width()-boxHeight, tft.height()-boxWidth*2, colors[9]);
    }
    */
    
    // turn from 0->1023 to tft.width
    p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
    p.y = map(p.y, TS_MINY, TS_MAXY, tft.height(), 0);

    // do something with p.x and p.y

    paused = !paused;
    delay(200);
  }
  
  //move ball
  if(!paused){
    clr();
    move();
    draw();
  }
  
}
void clr(){
  // erase ball on screen
  tft.fillCircle(bX, bY, bDiameter, colors[9]);
  
  // erase paddle 1
  tft.fillRoundRect(p1X, p1Y, p1Length, 10, bDiameter, colors[9]);

  // erase paddle 2
  tft.fillRoundRect(p2X, p2Y, p2Length, 10, bDiameter, colors[9]);
}
void draw(){
  // place ball on screen
  tft.fillCircle(bX, bY, bDiameter, bColor);
  
  // place paddle 1
  tft.fillRoundRect(p1X, p1Y, p1Length, 10, bDiameter, p1Color);

  // place paddle 2
  tft.fillRoundRect(p2X, p2Y, p2Length, 10, bDiameter, p2Color);
}
void move(){
  Serial.print("IN  bDirection=");
  Serial.print(bDirection);
  Serial.print("; bX=");
  Serial.print(bX);
  Serial.print("; bY=");
  Serial.print(bY);
  Serial.println(";");
  while(bDirection == 90 || bDirection == 270){
    bDirection = random(0, 360);
  }

  float r = deg2rad(bDirection);
  bX = bX - bSpeed * sin(r);
  bY = bY - bSpeed * cos(r);
  if(bX < bDiameter || bX >= 240 - bDiameter){
    if(bX < 0){
      bX = bDiameter - bX;
    } else if(bX < bDiameter){
      bX = bDiameter + (bDiameter - bX);
    } else {
      bX = 240 - (bX - 240);
    }
    bDirection = 360 - bDirection;
  }
  if(bY < bDiameter || bY >= 320 - bDiameter){
    if(bY < 0){
      bY = bDiameter - bY;
    } else if(bY < bDiameter){
      bY = bDiameter + (bDiameter - bY);
    } else {
      bY = 320 - (bY - 320);
    }
    if(bDirection <= 180){
      bDirection = 180 - bDirection;
    } else {
      bDirection = 540 - bDirection;
    }
  }
  p1X = bX - p1Length/2;
  p2X = bX - p2Length/2;
  if(p1X < bDiameter){ p1X = bDiameter; }
  if(p2X < bDiameter){ p2X = bDiameter; }
  if(p1X >= 240 - p1Length){ p1X = 240 - p1Length - 1; }
  if(p2X >= 240 - p2Length){ p2X = 240 - p2Length - 1; }
  Serial.print("OUT bDirection=");
  Serial.print(bDirection);
  Serial.print("; bX=");
  Serial.print(bX);
  Serial.print("; bY=");
  Serial.print(bY);
  Serial.println(";");

  Serial.print("p1X=");
  Serial.print(p1X);
  Serial.print("; p1Y=");
  Serial.print(p1Y);
  Serial.println(";");

  Serial.print("p2X=");
  Serial.print(p2X);
  Serial.print("; p2Y=");
  Serial.print(p2Y);
  Serial.println(";");
}

/*********************************************/
// 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[3 * BUFFPIXEL];  // 3 * pixels to buffer
  uint8_t buffidx = 3*BUFFPIXEL;
  
  //Serial.print("rotation = "); Serial.println(tft.getRotation(), DEC);
  
  for (i=0; i< bmpHeight; i++) {
    // bitmaps are stored with the BOTTOM line first so we have to move 'up'

    if (tft.getRotation() == 3) {
      tft.goTo(x, y+bmpHeight-i); 
    } else if  (tft.getRotation() == 2) {
      tft.goTo(x+i, y);
    } else if  (tft.getRotation() == 1) {
        tft.goTo(x+bmpWidth-i, y+bmpHeight); 
    } else if  (tft.getRotation() == 0) {
      tft.goTo(x+bmpWidth, y+i); 
    }
    
    for (j=0; j<bmpWidth; j++) {
      // read more pixels
      if (buffidx >= 3*BUFFPIXEL) {
        bmpFile.read(sdbuffer, 3*BUFFPIXEL);
        buffidx = 0;
      }
      
      // convert pixel from 888 to 565
      b = sdbuffer[buffidx++];     // blue
      g = sdbuffer[buffidx++];     // green
      p = sdbuffer[buffidx++];     // red
      
      p >>= 3;
      p <<= 6;
      
      g >>= 2;
      p |= g;
      p <<= 5;
      
      b >>= 3;
      p |= b;
     
       // write out the 16 bits of color
      tft.writeData(p);
    }
  }
}

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;
}
// this handy function will return the number of bytes currently free in RAM, great for debugging!   
int freeRam(void)
{
  extern int  __bss_end; 
  extern int  *__brkval; 
  int free_memory; 
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  }
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  }
  return free_memory; 
} 

