#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "icone.h"
#include "puro.h"
#include "puroEarsAnim.h"
#include "puroTailAnim.h"
#include "puroEyesAnim.h"

void createMenu();
void displayHunger();
void displayPuroBase();
void displayMenu(); 
void displayPuroRigthEarAnim();
void displayPuroLeftEarAnim();
void resetMillis();
void displayBlank(uint8_t Xi, uint8_t Xf, uint8_t Yi, uint8_t Yf);
void select();
void selectHunger();
void selectLight();
void selectBrush();
void selectGame();
void selectPet();
void selectStatus();

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//variabili per fame
uint8_t oldHunger = 0;
uint8_t newHunger = 40;
static const uint8_t maxHunger = 120;


//variabile per 'morte'
bool puroDeath = false;

//struct per menu
typedef struct cell{
  unsigned char * data;
  cell * next;
  cell * prev;
};
cell * menu = nullptr;


uint8_t Brpin = 19;
bool Br;
uint8_t Bcpin = 18;
bool Bc;
uint8_t Blpin = 4;
bool Bl;

bool refresh = false;

unsigned long previousTime;
unsigned long currentTime;
unsigned long deltaTime;
unsigned long hungerRemove = 5 * 60 * 1000;


                                  //array di puntatori alle bitmap
static unsigned char * icons[] = {HUNGER_ICO, LIGHT_ICO, BRUSH_ICO, PET_ICO, GAME_ICO, STATUS_ICO};

void setup() {
  Serial.begin(9600);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {  //se  non trova il display entra in un loop infinito
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  unsigned long startUp = millis();

  pinMode(19, INPUT);
  pinMode(18, INPUT);
  pinMode(4, INPUT);

  createMenu();                   //creo il menu con lista circolare doppiamente linkata senza sentinella
  display.clearDisplay();         //pulisce display
}

void loop() {  
  displayHunger(); Serial.println(newHunger);
  displayPuroBase();                 //display puro idle animation
  displayMenu();                  //display menu

  if(!newHunger) puroDeath = true;//se la fame scende a zero sarà visualizzata la sprite di 'morte' finche non si fa qualcosa che deve essere ancora scelto
  if(puroDeath) for(;;){
    /*************************************/
    /*codice da eseguire in caso di morte*/
    /*************************************/
    displayPuroBase();                 //display puro idle animation
    displayMenu();                  //display menu
  }
  display.display();
  //newHunger--;//test
  
  /*
  quando dovrai fare l'animazione, per evitare di dover aspettare la fine dell'animazione fai una cosa come
  nel casco e ci metti un 'whilie per il blink', così nel frattempo che si aspetta per il cambio frame 
  comunque si controlla per la pressione del pulsante
  */
  while(!refresh){
    Br = digitalRead(Brpin);
    Bl = digitalRead(Blpin);
    Bc = digitalRead(Bcpin);
    if(Br){
      while(digitalRead(Brpin)) delay(1); 
      displayPuroRigthEarAnim();
      menu = menu->next;
      refresh = true;
    }
    if(Bl){
      while(digitalRead(Blpin)) delay(1);
      displayPuroLeftEarAnim();
      menu = menu->prev;
      refresh = true;
    }
    if(Bc){
      while(digitalRead(Bcpin)) delay(1);
      select();
      refresh = true;
    }
    delay(1);
    currentTime = millis();
    if(currentTime < previousTime) resetMillis();
    else if(currentTime - previousTime >= hungerRemove){
      newHunger--;
      previousTime = currentTime;
    }
    if(newHunger != oldHunger) refresh = true;
  }
  refresh = false;
  display.clearDisplay();         //pulisce display
}

void resetMillis() {
  // Disabilita le interruzioni
  cli();
  // Resetta il conteggio di millis()
  *((volatile unsigned long *)0x44) = 0;
  *((volatile unsigned long *)0x48) = 0;
  // Abilita le interruzioni
  sei();
}

void displayPuroBase() {
  display.drawBitmap(20, 2, Puro, 72, 52, 1);   //display Puro bitmap
}

void displayPuroRigthEarAnim(){
  displayBlank(20, 35, 10, 30);
  
  display.drawBitmap(20, 2, PuroRigthEarIdle1, 72, 52, 1);   //display idlePuro ear1
  display.display();

  displayBlank(20, 35, 10, 30);
  
  display.drawBitmap(20, 2, PuroRigthEarIdle2, 72, 52, 1);   //display idlePuro ear2
  display.display();
  
  displayBlank(20, 35, 10, 30);

  display.drawBitmap(20, 2, PuroRigthEarIdle1, 72, 52, 1);   //display idlePuro ear1
  display.display();
}

void displayPuroLeftEarAnim(){
  displayBlank(23, 35, 34, 44);
  
  display.drawBitmap(20, 2, PuroLeftEarIdle1, 72, 52, 1);   //display idlePuro ear1
  display.display();
  displayBlank(23, 35, 34, 44);
  
  display.drawBitmap(20, 2, PuroLeftEarIdle2, 72, 52, 1);   //display idlePuro ear2
  display.display();
  
  displayBlank(23, 35, 34, 44);

  display.drawBitmap(20, 2, PuroLeftEarIdle1, 72, 52, 1);   //display idlePuro ear1
  display.display();
}

void displayPuroTailAnim(){
  displayBlank(73, 89, 2, 17);
  display.drawBitmap(20, 2, PuroTailIdle1, 72, 52, 1);   //display idlePuro ear1
  display.display();

  displayBlank(73, 89, 2, 17);
  
  display.drawBitmap(20, 2, PuroTailIdle2, 72, 52, 1);   //display idlePuro ear2
  display.display();
  
  displayBlank(73, 89, 2, 17);

  display.drawBitmap(20, 2, PuroTailIdle1, 72, 52, 1);   //display idlePuro ear1
  display.display();
}

void displayBlank(uint8_t Xi, uint8_t Xf, uint8_t Yi, uint8_t Yf){
  for(uint8_t  i = Xi ; i < Xf ; ++i){//x
    for(uint8_t  j = Yi ; j < Yf ; ++j){//y
      display.drawPixel(i, j, SSD1306_BLACK);
    }
  }
}

void displayHunger(){
  uint8_t upperHunger = newHunger/2, lowerHunger = newHunger-upperHunger; //calcolo lower e upper hunger
  if(newHunger == 0) display.drawPixel(11, 60, SSD1306_BLACK);
  else{
    if(newHunger == 1) display.drawPixel(11, 60, SSD1306_WHITE);   //se la fame è a uno fa vedere un pixel
    else{
      if(upperHunger > 0) {
        display.drawLine(10, 60, 10, 61-upperHunger, SSD1306_WHITE);  //display upper hunger
        display.drawLine(11, 60, 11, 61-lowerHunger, SSD1306_WHITE);  //display lower hunger
      }
    }//SSD1306_BLACK
  }
  //display.display();            //aggiorna buffer
  oldHunger = newHunger;        //aggiorna hunger
}

void createEmptyList() {        //crea lista vuota con la sentinella
  menu= new cell;
  menu->next = menu;
  menu->prev = menu;
  menu->data = sentinel;        //si inizializza con una sprite apposita
}

void headInsert(unsigned char * icona){     //inserimento in testa delle icone del menu
  cell * newElement = new cell;             //creazione nuova cella
  newElement->data = icona;                 //inserimento icona
  newElement->next = menu->next;            //il prossimo elemento sarà quello precedentemente inserito
  newElement->prev = menu;                  //il precedente sarà il menu
  newElement->prev->next = newElement;      //il prossimo elemento dopo la sentinella sarà la cella inserita
  newElement->next->prev = newElement;      //il precedente dell'elemento dopo è l'elemento inserito
}

void createMenu(){
  createEmptyList();            //crea lista vuota
  for(uint8_t i = 0 ; i < 6 ; ++i) headInsert(icons[5-i]);    //inserimento di tutti gli elementi al contrario, così la prima sarà la fame

  cell * cur = menu -> next;      //eliminazione dela sentinella
  menu->prev->next = menu->next;
  menu->next->prev = menu->prev;
  delete menu;
  menu = cur;
}

void displayMenu(){
  display.drawBitmap(109, 43, menu->prev->data, 16, 16, 1);   //display menu cella precedente
  display.drawBitmap(109, 23, menu->data, 16, 16, 1);   //display menu cella puntata
  display.drawBitmap(108, 22, frame, 24, 18, 1);   //display frame
  display.drawBitmap(109, 3, menu->next->data, 16, 16, 1);   //display menu cella successiva
  //display.display();            //aggiorna buffer
} 

void select(){
  if(menu->data == HUNGER_ICO) selectHunger();
  if(menu->data == LIGHT_ICO) selectLight();
  if(menu->data == BRUSH_ICO) selectBrush();
  if(menu->data == GAME_ICO) selectGame();
  if(menu->data == PET_ICO) selectPet();
  if(menu->data == STATUS_ICO) selectStatus();
}

void selectHunger()
{
  Serial.println("hunger");
      displayPuroTailAnim();
}

void selectLight()
{
  Serial.println("light");
}

void selectBrush()
{
  Serial.println("brush");
}

void selectGame()
{
  Serial.println("game");
}

void selectPet()
{
  Serial.println("pet");
}

void selectStatus()
{
  Serial.println("status");
}