// Include required header files
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "font.h"
#include "main.h"

const char       nameArray[] PROGMEM = "FABACADEMY";  // Only CAPS are defined as characters
volatile uint8_t namePtr = 0;       // a pointer to keep track of the current character

int main() {
  // Setting the hardware i/o pins
  DDRA  = 0b11111111;               // every pin on port A is output
  DDRB  = 0b00000110;               // some pins on port B is output
  PORTB = 0b00000001;               // PB0 is input, so enable the pull-up resistor

  // Setting the hardware timer interrupt
  TCNT1 = 0;                        // count from zero
  OCR1A = 16 * MATRIX_POV_TIME;     // count up to this value for PoV effect 16 = 1ms
  TCCR1B |= _BV(WGM12) |            // set counter to CTC mode
            _BV(CS11)  | _BV(CS10); // set clock divider to 64 = 15625 ticks per second
  TIMSK1 |= _BV(OCIE1A);            // enable interrupt on CTC mode for OCR1A  

  // Initialize variables
  if (pgm_read_byte(&nameArray[namePtr]) >= 65 && pgm_read_byte(&nameArray[namePtr]) <= 91) {
    fontPtr = pgm_read_byte(&nameArray[namePtr]) - 65;    // read the first character
  }
  
  // enable global interrupts -> enable timer
  sei();

  // Running the main program
  while (1) {                                             // loop forever
    if (ledMatrixShiftCtr >= MATRIX_SHIFT_CTR) {          // shift the matrix in time
      ledMatrixShiftCtr = 0;                              // reset the counter

      ledMatrixArray[4] = ledMatrixArray[3];
      ledMatrixArray[3] = ledMatrixArray[2];
      ledMatrixArray[2] = ledMatrixArray[1];
      ledMatrixArray[1] = ledMatrixArray[0];
      ledMatrixArray[0] = pgm_read_byte(&fontArray[(fontPtr * FONT_CHAR_WIDTH) + fontCharPtr]);

      fontCharPtr++;                                      // increment to get the next row in the character
      if (fontCharPtr >= FONT_CHAR_WIDTH) {               // check if we are done with this character
        fontCharPtr = 0;                                  // start again from the first row

        namePtr++;                                        // get the next character
        if (namePtr >= (sizeof(nameArray)/sizeof(char))) {// check we are at the last character
          namePtr = 0;                                    // start again from the first character
        }

        if (pgm_read_byte(&nameArray[namePtr]) >= 65 && pgm_read_byte(&nameArray[namePtr]) <= 91) { // validate input character
          fontPtr = pgm_read_byte(&nameArray[namePtr]) - 65;// process correct character
        } else {
          fontPtr = 26;                                   // replace incorrect character with space
        }
      }
    }
  }
  return 0; // We should never reach this...
}

// Timer1 interrupt on reaching compareA register
ISR(TIM1_COMPA_vect) {                    // a timer interrupt has occurred  
  ledMatrixPtr++;                         // select the next active column
  if (ledMatrixPtr >= MATRIX_NUM_COLS) {  // check if we are at the last column
    ledMatrixPtr = 0;                     // start again at the first column
  }
  ledMatrixShiftCtr++;                    // increment the matrix shift timer

  PORTA &= 0b00000000;                    // clear port A
  PORTB &= 0b11111001;                    // clear port B
  PORTA |= ledMatrixArray[ledMatrixPtr];       // load character line

  if (ledMatrixPtr == 0) {                // Set active column 0 (right)
    PORTA |= 0b00100000;
    PORTB |= 0b00000000;
  }
  if (ledMatrixPtr == 1) {                // Set active column 1
    PORTA |= 0b01000000;
    PORTB |= 0b00000000;
  }
  if (ledMatrixPtr == 2) {                // Set active column 2
    PORTA |= 0b10000000;
    PORTB |= 0b00000000;
  }
  if (ledMatrixPtr == 3) {                // Set active column 3
    PORTA |= 0b00000000;
    PORTB |= 0b00000100;
  }
  if (ledMatrixPtr == 4) {                // Set active column 4 (left)
    PORTA |= 0b00000000;
    PORTB |= 0b00000010;
  }
} 
