/*
  Snowman

  by Chris Ritchie - 05/06/2017

  Based off of https://github.com/rmadhuram/ArduinoChristmasTunes
  Sleep code from https://bigdanzblog.wordpress.com/2014/08/10/attiny85-wake-from-sleep-on-pin-state-change-code-example/

  14/06/2017
    - Added sleep code
*/
#include "pitches.h"
#include <avr/sleep.h>
#include <avr/interrupt.h>
//#include <avr/pgmspace.h>

typedef struct {
  unsigned int *melody;
  byte *durations;
  byte numCount;
  byte tempo;
  byte low, high;
} tune;

tune tunes[3]; // Amount of songs

unsigned int frostySnowman[] = { N_G4, N_E4, N_F4, N_G4, N_C5, N_B4, N_C5, N_D5, N_C5, N_B4, N_A4,
								 N_G4, N_B4, N_C5, N_D5, N_C5, N_B4, N_A4, N_A4, N_G4, N_C5, N_E4,
								 N_G4, N_A4, N_G4, N_F4, N_E4, N_F4, N_G4};
byte frostySnowmanDurations[] = { 8, 6, 3, 5, 8, 3, 3, 5, 5, 5, 5,
								  11, 3, 3, 3, 4, 4, 5, 5, 6, 5, 5,
								  3, 3, 5, 5, 5, 5, 14, 14};

unsigned int jingleBells[] = { N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_G5, N_C5, N_D5, N_E5,
                               N_F5, N_F5, N_F5, N_F5, N_F5, N_E5, N_E5, N_E5, N_E5, N_D5, N_D5, N_E5, N_D5, N_G5,
                               N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_G5, N_C5, N_D5, N_E5,
                               N_F5, N_F5, N_F5, N_F5, N_F5, N_E5, N_E5, N_E5, N_G5, N_G5, N_F5, N_D5, N_C5
                             };
byte jingleBellsDurations[] = { 4, 4, 8, 4, 4, 8, 4, 4, 4, 4, 16,
                                4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8,
                                4, 4, 8, 4, 4, 8, 4, 4, 4, 4, 16,
                                4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16
                              };

unsigned int rudolph[] = { N_G4, N_A4, N_G4, N_E4, N_C5, N_A4, N_G4, N_G4, N_A4, N_G4,
						   N_A4, N_G4, N_C5, N_B4, N_F4, N_G4, N_F4, N_D4, N_B4, N_A4,
						   N_G4, N_G4, N_A4, N_G4, N_A4, N_G4, N_A4, N_E4};
byte rudolphDurations[]  = { 2, 2, 4, 4, 4, 4, 8, 2, 2, 2,
							 2, 3, 3, 11, 2, 2, 3, 3, 3, 3,
							 8, 2, 2, 2, 2, 3, 3, 11, 11};



const byte nosePin = 0; // Pin for nose LED
const byte buzzerPin = 1; // Pin for piezo buzzer

// Button pins and states
const int frostyBttn = 2;
boolean frostyState = 0;
const int jingleBttn = 4;
boolean jingleState = 0;
const int rudolphBttn = 3;
boolean rudolphState = 0;

void play(int tuneNum) {
  int numNotes = tunes[tuneNum].numCount;
  int tempo = tunes[tuneNum].tempo;
  for (int thisNote = 0; thisNote < numNotes; thisNote++) {
    int freq = tunes[tuneNum].melody[thisNote] * 2;
    int noteDuration = tempo * tunes[tuneNum].durations[thisNote];

    if (freq > 0) {
      digitalWrite(nosePin, HIGH); // Turn on nose when tone plays
      tone(buzzerPin, freq, noteDuration);

    } else {
      // REST
      delay(noteDuration);
    }

    delay(noteDuration);
    digitalWrite(nosePin, LOW); // Turn off nose when tone stops

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 0.30;
    delay(pauseBetweenNotes);

    noTone(buzzerPin);
  }
}


void setup() {

  int numTunes = 3;

  analogReference(INTERNAL);

  tunes[0].melody = jingleBells;
  tunes[0].durations = jingleBellsDurations;
  tunes[0].numCount = sizeof(jingleBells) / sizeof(int);
  tunes[0].tempo = 60;

  tunes[1].melody = rudolph;
  tunes[1].durations = rudolphDurations;
  tunes[1].numCount = sizeof(rudolph) / sizeof(int);
  tunes[1].tempo = 60;

  tunes[2].melody = frostySnowman;
  tunes[2].durations = frostySnowmanDurations;
  tunes[2].numCount = sizeof(frostySnowman) / sizeof(int);
  tunes[2].tempo = 60;

  for (int i = 0; i < numTunes; i++) {
    int low = N_DS8;
    int high = 0;
    for (int j = 0; j < tunes[i].numCount; j++) {
      int freq = tunes[i].melody[j];
      if (freq != 0 && freq < low) low = freq;
      if (freq > high) high = freq;
    }
    tunes[i].high = high;
    tunes[i].low = low;
  }

  // Initialize the button pins as an input with internal pullup resistors enabled
  pinMode(jingleBttn, INPUT_PULLUP);
  pinMode(frostyBttn, INPUT_PULLUP);
  pinMode(rudolphBttn, INPUT_PULLUP);
  pinMode(nosePin, OUTPUT);
}

void sleep() {

  GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
  PCMSK |= _BV(PCINT3);                   // Use PB3 as interrupt pin
  PCMSK |= _BV(PCINT4);                   // Use PB4 as interrupt pin
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep

  cli();                                  // Disable interrupts
  PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
  PCMSK &= ~_BV(PCINT3);                  // Turn off PB3 as interrupt pin
  PCMSK &= ~_BV(PCINT4);                  // Turn off PB4 as interrupt pin
  sleep_disable();                        // Clear SE bit
  ADCSRA |= _BV(ADEN);                    // ADC on

  sei();                                  // Enable interrupts
} // sleep

ISR(PCINT0_vect) {
  // This is called when the interrupt occurs, but I don't need to do anything in it
}

void loop() {
  sleep();
  // Read the state of the buttons
  jingleState = digitalRead(jingleBttn);
  frostyState = digitalRead(frostyBttn);
  rudolphState = digitalRead(rudolphBttn);

  // Check  if button has been pressed, and play corresponding song
  if (jingleState == LOW)
    play(0);

  else if (rudolphState == LOW)
    play(1);

  else if (frostyState == LOW)
    play(2);
}