/**  Header Files **************************************************/
#include <p18f4520.h>
#include <stdio.h>
#include <adc.h>
#include "LCD Module.h"
#include <portb.h>
#include <timers.h>
#include <delays.h>

/** Configuration Bits *********************************************/
#pragma config OSC = INTIO67
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config BOREN = OFF
#pragma config XINST = OFF

/** Define Constants Here ******************************************/
#define PRESSED 0

#define SPEAKER_NOBODY 0
#define SPEAKER_LOGAN 1
#define SPEAKER_AVERY 2

#define TIMER_START 49911   // 2^16 - 1000000/4/16

/** Local Function Prototypes **************************************/
void low_isr(void);
void high_isr(void);
void updateLcd(void);

/** Declare Interrupt Vector Sections ****************************/
#pragma code high_vector=0x08

void interrupt_at_high_vector(void) {
    _asm goto high_isr _endasm
}

#pragma code low_vector=0x18

void interrupt_at_low_vector(void) {
    _asm goto low_isr _endasm
}

/** Global Variables ***********************************************/
char currentSpeaker = SPEAKER_NOBODY;
char timeRemaining = 0;
char line2[17];

/*******************************************************************
 * Function:        void main(void)
 ********************************************************************/
#pragma code

void main(void) {
    // Set the clock to 4 MHz
    OSCCONbits.IRCF2 = 1;
    OSCCONbits.IRCF1 = 0;
    OSCCONbits.IRCF0 = 0;

    // Pin IO Setup
    OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_12_TAD,
            ADC_CH0 & ADC_INT_OFF & ADC_REF_VDD_VSS,
            0x0B); // Four analog pins
    TRISA = 0xFF; // All of PORTA input
    TRISB = 0xFF; // All of PORTB input
    TRISC = 0x00; // All of PORTC output
    TRISD = 0x00; // All of PORTD output
    PORTC = 0x00; // Turn off all 8 Port C LEDs

    // Interrupt setup
    RCONbits.IPEN = 1; // Put the interrupts into Priority Mode

    OpenRB1INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_OFF);
    INTCON3bits.INT1IP = 1; // Setting the prior as high

    // Turn on the RB2 interrupt INT2, falling edge
    OpenRB2INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_OFF);
    INTCON3bits.INT2IP = 0; // Setting the prior as low
    
    OpenTimer0(TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_16);
    INTCON2bits.TMR0IP = 1;
    
    INTCONbits.GIEH = 1; // Turn on high priority interrupts
    INTCONbits.GIEL = 1; // Turn on low priority interrupts

    XLCDInit();
    XLCDClear();

    while (1) {
        updateLcd();
    }
}

/*****************************************************************
 * Function:        void high_isr(void)
 ******************************************************************/
#pragma interrupt high_isr

void high_isr(void) {
    if (INTCON3bits.INT1IF) { // Interrupt 1, RB1 with debounce
        INTCON3bits.INT1IF = 0;
        Delay1KTCYx(7); // Adjusted for a 4 MHz clock
        if (PORTBbits.RB1 == PRESSED) {
		  currentSpeaker = SPEAKER_LOGAN;
          timeRemaining = 4;
          WriteTimer0(TIMER_START);
        }
    }
    if (INTCONbits.TMR0IF) { // Timer 0 interrupt
        INTCONbits.TMR0IF = 0;
        WriteTimer0(TIMER_START);
        if (timeRemaining > 0) {
            timeRemaining = timeRemaining - 1;        
        }
        if (timeRemaining == 0) {
            currentSpeaker = SPEAKER_NOBODY;
        }
    }
}

/******************************************************************
 * Function:        void low_isr(void)
 ********************************************************************/
#pragma interruptlow low_isr

void low_isr(void) {
    if (INTCON3bits.INT2IF) { // Interrupt 2, RB2 with debounce
        INTCON3bits.INT2IF = 0;
        Delay1KTCYx(7); // Adjusted for a 1 MHz clock
        if (PORTBbits.RB2 == PRESSED) {
		  currentSpeaker = SPEAKER_AVERY;
          timeRemaining = 4;
          WriteTimer0(TIMER_START);
        }
    }
}

#pragma code
/*****************************************************************
 * Additional Helper Functions
 ******************************************************************/

void updateLcd() {
    XLCDL1home();
    if (currentSpeaker == SPEAKER_LOGAN) {
        XLCDPutRomString("Logan is talking");
    } else if (currentSpeaker == SPEAKER_AVERY) {
        XLCDPutRomString("Avery is talking");
    } else {
        XLCDPutRomString("Nobody talking");
    }

    XLCDL2home();
    if (timeRemaining == 0) {
        XLCDPutRomString("                ");
    } else {
        sprintf(line2, "Time left: %d sec", timeRemaining);
        XLCDPutRamString(line2);        
    }
}