#include <Arduino.h>
#include <SoftwareSerial.h>
#include <JQ6500_Serial.h>
#include <avr/pgmspace.h> //  PROGMEM
#include <SPI.h>
#include <MFRC522.h>
#include <avr/sleep.h>
#include <avr/power.h>
#define adc_disable() (ADCSRA &= ~(1<<ADEN)) 
#define adc_enable() (ADCSRA |= (1<<ADEN)) 
#define RST_PIN 9
#define SS_PIN 10
#define mp3Pin 4
#define mp3Busy A0
#define readerPin 6
#define tonePin 5
#define ledPin 3
#define offDelay 70000 
#define winkStep 1500 
#define on 150 
#define off 80 
#define tOut1 0 
#define tOut11 500
#define tOut2 14000
#define tOut21 15000
#define tOut3 29000
#define tOut31 30000
#define tOut4 44000
#define tOut41 45000
#define tOut5 60000
#define tOut51 65000
#define tShake 2000 
#define nShakeQ 10 
#define introQ 5 
#define minVol 18 
#define midVol  22 
#define maxVol 25 
unsigned long dimDelay, winkStepDelay, onDelay, ledOffDelay, tShakeDelay;
boolean ledOn, ledOff, eyes, pwm;
int wink;
int pwmVal;
boolean playON = false;
boolean pwmUp = false;
byte pwmStep = 1;
unsigned int playFile;
MFRC522 mfrc522(SS_PIN, RST_PIN);       
unsigned long uidDec, uidDecTemp; 
byte bCounter, readBit, nShake, rnd;
byte vol = midVol; 
unsigned long ticketNumber;
unsigned long offTimeOut = 0; 
boolean mp3ON = false; 
boolean isInt = false; 
byte ticketQ = 32; 
byte fileQ = 0; 
PROGMEM const uint32_t ticketSet[]  = {2515217196, 2540548337, 2490970856, 2486466332, 2485920633, 35870611, 37836807, 37836806, 2377004330, 2522873668, 2514304566, 23472725, 2485702426, 2374853555, 2374391583, 2492957469, 2486467162, 2489280075, 2488031661, 2491726641, 2491720188, 2490968782, 2490968783, 2488900952, 2489969016, 2506562651, 2375447052, 2375449579, 2489276180, 2483389692, 2486466331, 2484789326};
JQ6500_Serial mp3(8,7); 
void enterSleep()
{
 mp3.playFileNumberInFolderNumber(01, 005); 
 mp3.playFileNumberInFolderNumber(01, 005); 
 delay(2500);
 tone(tonePin, 800, 500);
 delay(500); 
 digitalWrite(readerPin, LOW); 
 digitalWrite(mp3Pin, LOW);
 digitalWrite(ledPin, LOW);
 pinMode(ledPin, INPUT);
 pinMode(readerPin, INPUT);
 pinMode(mp3Pin, INPUT);
 
  adc_disable();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  
  sleep_enable();
  
  sleep_mode();
  
 sleep_disable();
 power_all_enable();
 adc_enable();
 tone(tonePin, 450, 500);
 pinMode(ledPin, OUTPUT);
 pinMode(readerPin, OUTPUT);
 pinMode(mp3Pin, OUTPUT);
 digitalWrite(ledPin, LOW);
 digitalWrite(readerPin, HIGH); 
 digitalWrite(mp3Pin, HIGH);
 SPI.begin();            
 mfrc522.PCD_Init();     
 mp3Init();
 offTimeOut = millis();
 ledOffDelay = millis();
 mp3.playFileNumberInFolderNumber(01, 001); 
 mp3.playFileNumberInFolderNumber(01, 001); 
 delay(500);
 playON = false; 
 mp3ON = true;
}
void wakeUp() {
   detachInterrupt(0);
   ledOffDelay = millis();
   if (isInt == false) { 
    isInt = true;
   }
   attachInterrupt(0, wakeUp, LOW); 
}
void setup() {
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(0, wakeUp, LOW);
  pinMode(readerPin, OUTPUT);  
  digitalWrite(readerPin, HIGH);
  SPI.begin();            
  mfrc522.PCD_Init();     
  pinMode(mp3Pin, OUTPUT);
  mp3Init();
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  eyes = false;
  ledOn = false;
  ledOff = false;
  dimDelay = millis();
  winkStepDelay = millis();
  wink = 0;
  pwmUp = true; 
  pwmVal = 0;
  ledOffDelay = millis();
  offTimeOut = millis();  
  mp3.playFileNumberInFolderNumber(01, 001); 
  mp3.playFileNumberInFolderNumber(01, 001); 
  delay(500);
  playON = false; 
  mp3ON = true;
}
void loop() {
rnd = random(1, fileQ-1);
if ((millis() - offTimeOut) > offDelay) { 
     enterSleep();
 } else { 
 ledWink(); 
 if (isInt == true) {
  
  offTimeOut = millis(); 
  if (nShake == 0) {
    tShakeDelay = millis();
  }
  
  if ((millis() - tShakeDelay) < tShake){
    nShake = nShake + 1;
  } else {
    tShakeDelay = millis();
    nShake = 0;
  }
   isInt = false;
 }
 if (nShake > nShakeQ) {
  playRandom();
  nShake = 0;
 }
playPreset(); 
  if (mp3ON == true) {
   
   if (playON == true) { 
    offTimeOut = millis(); 
   }
   nShake = 0;
   eyesPWM(); 
   
    
    if(analogRead(mp3Busy) < 250) { 
     mp3ON = false; 
     playON = false;
     digitalWrite(ledPin, LOW);
    }
  }
  
 scanPlay(); 
 
 }
}
void setBitsForGood(byte daBeat) {
  
        if (daBeat == 1) {
                bitSet(ticketNumber, bCounter);
                bCounter=bCounter+1;
                }
        else {
                bitClear(ticketNumber, bCounter);
                bCounter=bCounter+1;
        }
}
void mp3Init() {
  digitalWrite(mp3Pin, HIGH);
  delay(100);
  mp3.begin(9600);
  mp3.reset();
  mp3.setVolume(vol);
  mp3.setLoopMode(MP3_LOOP_NONE);
  fileQ = mp3.countFiles(MP3_SRC_SDCARD); 
  fileQ = fileQ - introQ; 
}
void playRandom() {
   tone(tonePin, 450, 500);
   delay(500);  
   playFile = rnd;
   mp3ON = true;
   playON = true;
   mp3.playFileByIndexNumber(playFile);
   mp3.playFileByIndexNumber(playFile);
   mp3.playFileByIndexNumber(playFile);  
   delay(500);
}
void ledWink() {
 
if ((millis() - winkStepDelay) > winkStep) { 
  if (eyes == true) { 
    if (ledOn == false) {
      onDelay = millis(); 
        ledOn = true; 
    }
 
    if ((millis() - onDelay) > on) { 
      digitalWrite(ledPin, LOW); 
      eyes = false; 
    }
  }
  if (eyes == false) { 
    
    if (ledOff == false) {
      ledOffDelay = millis(); 
        ledOff = true; 
    }
    if ((millis() - ledOffDelay) > off) { 
      digitalWrite(ledPin, HIGH); 
      eyes = true; 
    }
  }
  if (ledOn == true && ledOff == true) { 
    wink = wink+1;
    ledOn = false;
    ledOff = false;    
  }
  if (wink == 4) { 
     winkStepDelay = millis();
     wink = 0;
 }
 }
}
void playPreset() {
 if (mp3ON == false) {
  
  if ((millis() - offTimeOut) > tOut2 && (millis() - offTimeOut) < tOut21) {
    mp3.playFileNumberInFolderNumber(01, 002); 
    mp3ON = true; 
    delay(500);
  }
  if ((millis() - offTimeOut) > tOut3 && (millis() - offTimeOut) < tOut31) {
    mp3.playFileNumberInFolderNumber(01, 003); 
    mp3ON = true;
    delay(500);
   }  
  if ((millis() - offTimeOut) > tOut4 && (millis() - offTimeOut) < tOut41) {
    mp3.playFileNumberInFolderNumber(01, 004); 
    mp3ON = true;  
    delay(500); 
  }    
    
 }
  
}
void eyesPWM(){
 if ((millis() - winkStepDelay) > (pwmStep)/4) {
   
    if (pwmUp == true) {
      if (pwmVal < 128) { 
        analogWrite(ledPin, pwmVal);
        pwmVal = pwmVal + 1;
        pwmStep = pwmStep - 1;
        winkStepDelay = millis();
      } else {
            pwmUp = false;
            pwmStep = 1;
            pwmVal = 128;
      } 
    }
    
    if (pwmUp == false) {
      if (pwmVal > pwmStep) {
        analogWrite(ledPin, pwmVal);
        pwmVal = pwmVal - 1;
        pwmStep = pwmStep +1 ;
        winkStepDelay = millis();
      } else {
            pwmUp = true;
            pwmStep = 128;
            pwmVal = 1;
      } 
    }
    }
  
}
void scanPlay() {
 if (fileQ > 0) {
          
        if ( ! mfrc522.PICC_IsNewCardPresent()) {
                return;
        }
        
        if ( ! mfrc522.PICC_ReadCardSerial()) {
                return;
        }
        uidDec = 0;
        byte status;
        byte byteCount;
        byte buffer[18]; 
        byte pages[2]={4, 8}; 
        byte pageByte; 
        
        byteCount = sizeof(buffer);
        byte bCount=0;
                
        mfrc522.MIFARE_Read(4, buffer, &byteCount);
        
          
                                bCounter = 0; 
                                
                                
                                for (bCount=0; bCount<4; bCount++) {
                                        readBit = bitRead(buffer[6], (bCount+4));
                                        setBitsForGood(readBit);
                                }
                                
                                for (pageByte=5; pageByte > 2; pageByte--) {
                                        for (bCount=0; bCount<8; bCount++) {
                                                readBit = bitRead(buffer[pageByte], bCount);
                                                setBitsForGood(readBit);
                                        }
                                }
                                
                                for (bCount=0; bCount<4; bCount++) {
                                        readBit = bitRead(buffer[2], bCount);
                                        setBitsForGood(readBit);
                                }
                               for (byte ticketNum = 0; ticketNum < ticketQ; ticketNum++) {
                                unsigned long ticketTemp = pgm_read_dword_near(ticketSet + ticketNum);
                                if (ticketTemp == ticketNumber) {
                                  tone(tonePin, 450, 500);
                                  delay(500); 
                                  if (ticketNum < (ticketQ - 2)) {
                                    if ((ticketNum+1) < fileQ) {
                                      digitalWrite(ledPin, HIGH);
                                       playFile = ticketNum+1;
                                      mp3ON = true;
                                      playON = true;
                                      mp3.playFileByIndexNumber(playFile);
                                      mp3.playFileByIndexNumber(playFile);
                                      mp3.playFileByIndexNumber(playFile);
                                      delay(500);
                                      }
                                      return;
                                  } else {
                                    if (ticketNum == ticketQ-1) {
                                      enterSleep(); 
                                    }
                                    if (ticketNum == ticketQ-2) {
                                      setVol(); 
                                    }
                                  }
                                  
                                }
                               }
                        
                
        
    mfrc522.PICC_HaltA();    
  }
  
}
void setVol() {
  switch (vol) {
    case maxVol:
      vol = minVol;
      break;
    case midVol:
      vol = maxVol;
      break;
    case minVol:
      vol = midVol;
      break;
  } 
  mp3.setVolume(vol);
  
}