// Fire animation demo with 8 custom characters on 2x16 LCD
// Author: Yaroslaw Turbin, 26-10-2024
// Code improvement and organization: Anderson, 06-11-2024

#include <Wire.h>
#include <FastLED.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

// LCD setup
const int LCD_COLS = 16;
const int LCD_ROWS = 2;
hd44780_I2Cexp lcd; // I2C LCD object

// Fire animation parameters
#define NUM_ROWS  8
#define NUM_COLS  40
byte firebuff[NUM_ROWS * NUM_COLS]; // Buffer for fire effect
byte customChars[8][8]; // Storage for custom characters

// Initialize the LCD display
void setup() {
  Serial.begin(115200); // Serial for debugging FPS if needed

  // Initialize the LCD with defined columns and rows
  lcd.begin(LCD_COLS, LCD_ROWS);
  lcd.backlight();
  lcd.noCursor();
  lcd.clear();

  // Display initial message
  lcd.setCursor(1, 0);
  lcd.print("WORLD IN FIRE");
  lcd.setCursor(0, 1);
  lcd.print(" \1\2\3\4\5\6\7 \1\2\3\4\5\6\7");
}

// Generate fire effect and update fire buffer
void generateFireEffect() {
  int timeOffset = millis() / 3;
  int index = 0;

  for (byte row = 0; row < NUM_ROWS; row++) {
    for (byte col = 0; col < NUM_COLS; col++) {
      byte color = qsub8(inoise8(col * 50, row * 50 + millis(), timeOffset), 
        abs8(row - (NUM_ROWS - 1)) * 255 / (NUM_ROWS + 10));
      firebuff[index] = (color > 96) ? 255 : 0;
      index++;
    }
  }
}

// Convert fire buffer section to a custom character format
void convertBufferToCustomChar(int offset, byte *symbol) {
  for (byte row = 0; row < 8; row++) {
    byte bits = 0;
    for (byte col = 0; col < 5; col++) {
      byte color = firebuff[offset + row * NUM_COLS + col];
      bits <<= 1;
      if (color) bits |= 0x01;
    }
    symbol[row] = bits;
  }
}

// Calculate and display Frames Per Second (FPS) in serial
void displayFPS() {
  static int frameCount = 0;
  static unsigned long lastTime = millis();
  frameCount++;

  if (millis() - lastTime > 8000) {
    Serial.print(F("FPS: "));
    Serial.println(frameCount / 8);
    frameCount = 0;
    lastTime = millis();
  }
}

void loop() {
  // Generate the fire effect pattern
  generateFireEffect();

  // Update the LCD with custom characters
  for (byte i = 0; i < 8; i++) {
    convertBufferToCustomChar(i * 5, customChars[i]);
    lcd.createChar(i, customChars[i]);
  }

  // Display the fire animation on the LCD
  lcd.setCursor(0, 1);
  for (byte i = 0; i < 8; i++) {
    lcd.write(i);
  }

  // Optional: Uncomment to display FPS in the Serial Monitor
  // displayFPS();

  delay(100); // Control animation speed
}