/*
  Able to use PROGMEM by using #include avr/pgmspace.h
  See https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga73084a8bbde259ffae72980354b3f027
  for details.

  CM 10/15/24: This sketch is working well.
  - Create a huminoid looking robot with the 8x8 display as its head. The body could 
    encorperate the ATtiny85, maybe a pulsing LED for a heart?
*/

#include <avr/pgmspace.h> // used with 'PROGMEM'
#include"data_bytes.h" // MAX7219 animation arrays

uint8_t position = 0;
static uint8_t MAX7219_state[8] = {0,0,0,0,0,0,0,0}; // used with MAX7219_set_pixel
unsigned int randNum_row;
unsigned int randNum_col;
uint8_t pixel = 0;


void setup() {
MAX7219_init();
/*
  if analog input pin (pin number) is unconnected, random analog noise will cause the call to randomSeed() to generate
  different seed numbers each time the sketch runs. randomSeed() will then shuffle the random function.
*/
  randomSeed(analogRead(A0)); // Pin PB5 = ADC0
}

void loop() {

  MAX7219_clear_display();

  // ---------------------------------------------


  // Display a static 8x8 image from a 1D array
  display_byte_array(smiley);
  delay(1000);

  // MAX7219_clear_display();
 

  // ---------------------------------------------


  // Displays a static 8x8 image from one row of a 2D array
  display_byte_2D_array(dogface, 0);
  delay(200);
  display_byte_2D_array(dogface, 1);
  delay(200);
  display_byte_2D_array(dogface, 0);
  delay(200);
  display_byte_2D_array(dogface, 1);
  delay(200);
  display_byte_2D_array(dogface, 0);
  delay(200);
  display_byte_2D_array(dogface, 1);
  delay(200);
  display_byte_2D_array(dogface, 0);
  delay(200);
  display_byte_2D_array(dogface, 1);
  delay(200);


  // ---------------------------------------------


  // Displays scrolling 'invader' array, with delay (array name)
  scrolling_2D_array_with_delay(invader1);
  delay(1000);


  // ---------------------------------------------


  // Displays scrolling 'invader' array, with delay (array name)
  scrolling_2D_array_with_delay(invader2);
  delay(1000);

 
  // ---------------------------------------------


  // Displays scrolling array, (array name, number of rows)
  scrolling_2D_array(LSU, 28);
  delay(1000); // delay until next function is run

  MAX7219_clear_display();

  scrolling_2D_array(running_man_LR, 21);
  delay(1000); // delay until next function is run

  MAX7219_clear_display();

  scrolling_2D_array(dancing_man_RL, 45);
  delay(1000); // delay until next function is run

  MAX7219_clear_display();

  scrolling_2D_array(heart, 57);
  delay(1000); // delay until next function is run


  // ---------------------------------------------


  MAX7219_clear_display();
  // Display one pixel, (row 0 to 7, column 0 to 7, ON = 1)
  MAX7219_set_pixel(7, 0, 1);
  delay(1000);

  MAX7219_clear_display();


  // ---------------------------------------------


  // displays random pixels. Time of while loop is set in function
  // with '100' equaling 1 second. delay(10) x 100 = 1000ms
  random_pixels(500);
  delay(1000); // delay until next function is run


  // ---------------------------------------------

}

// = = = = = = = = = = = FUNCTIONS = = = = = = = = = = = = = 

void MAX7219_init() {
  // PB0 -> DIN, PB1 -> CS, PB2 -> CLK
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2); // Set pins as outputs
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  MAX7219_write(0x09, 0); // Decode-Mode Register: No decode for digits 7–0
  MAX7219_write(0x0A, 8); // Intensity Register: Format 0 to 16
  MAX7219_write(0x0B, 7); // Scan-Limit Register: Display digits 0 1 2 3 4 5 6 7
  MAX7219_write(0x0C, 1); // Shutdown Register: Normal Operation, turn on MAX7219
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

void SPI_send(uint8_t data) { // variable 'data' is used in 'command_byte' and 'data_byte'
  for (uint8_t i = 0; i < 8; i++) { // try uint8_t i = 8; i >= 1; i-- (no difference) // Loop 8 times to shift out MSB first, of byte to MAX7219
    PORTB &= ~(1 << PB2);           // Set CLK to LOW
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    if (data & 0b10000000) {        // Mask the MSB 0x80 of the data
      PORTB |= (1 << PB0);          // Set DIN to HIGH if MSB = 1
    }
    else (PORTB &= ~(1 << PB0));    // Set DIN to LOW if MSB = 0
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PORTB |= (1 << PB2);            // Set CLK to HIGH
    data = data << 1;               // Shift data to left 1 bit
  }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Write to a row, 1 through 8
void MAX7219_write(uint8_t command_byte, uint8_t data_byte) {
  PORTB &= ~(1 << PB1); // CS set LOW to enable MAX7219
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  SPI_send(command_byte); // Send command byte
  SPI_send(data_byte); // Send data byte
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  PORTB |= (1 << PB1); // CS set HIGH to disable MAX7219
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

void MAX7219_clear_display() {
  uint8_t i;
  for (i = 0; i < 8; i++) {
    MAX7219_write(i + 1, 0);
  }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Display one pixel, (row 0 to 7, column 0 to 7, ON = 1)
void MAX7219_set_pixel(uint8_t row, uint8_t col, bool value) {
	uint8_t data;
	if (row > 7 || col > 7)
		return;

	data = 1 << col;
	if (value) {
		MAX7219_state[row] |= data;
	}
  else {
		MAX7219_state[row] &= ~data;
	}

	MAX7219_write(row + 1, MAX7219_state[row]);
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Display a static 8x8 image from a 1D array
void display_byte_array(uint8_t image[8]) {
  uint8_t i;
  for (i = 0; i < 8; i++) {
    MAX7219_write(i + 1, image[i]); // Write to rows 1 through 8
  }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Displays a static 8x8 image from one row of a 2D array
void display_byte_2D_array(uint8_t image[][8], uint8_t num) {
  uint8_t i;
  for (i = 0; i < 8; i++) {
    // MAX7219_write(i + 1, image[num][i]); // Use without PROGMEM
    MAX7219_write(i + 1, pgm_read_byte(&image[num][i])); // Use with PROGMEM
  }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Displays scrolling 'invader' array, with delay (array name)
void scrolling_2D_array_with_delay(uint8_t image[][8]) {
for (uint8_t j = 0; j < 21; j++) {
    if (j == 9 || j == 10 || j == 11 || j == 12) {
      delay(250);
    }
    else {
      delay(100);
    }
    for (uint8_t i = 0; i < 8; i++) {
      // MAX7219_write(i + 1, image[j][i]); // Use without PROGMEM
      MAX7219_write(i + 1, pgm_read_byte(&image[j][i])); // Use with PROGMEM
    }
  }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Displays scrolling array, (array name, number of rows)
void scrolling_2D_array(uint8_t image[][8], uint8_t num) {
  for (uint8_t j = 0; j < num; j++) {
    delay(180);
    for (uint8_t i = 0; i < 8; i++) {
      //MAX7219_write(i + 1, image[j][i]); // Use without PROGMEM
      MAX7219_write(i + 1, pgm_read_byte(&image[j][i])); // Use with PROGMEM
    }
  }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// displays random pixels. Time of while loop is set in function
// with '100' equaling 1 second. delay(10) x 100 = 1000ms
void random_pixels(uint16_t count) {
  uint16_t num = 0;
  while (num < count) {
		randNum_row = random(8);
    randNum_col = random(8);
		MAX7219_set_pixel(randNum_row, randNum_col, ++pixel & 0x01);
		delay(10);
    num = num + 1;
	}
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
ATTINY8520PU