// Segment clock made of WS2812 led strip

// fire width 19 height 7
// testing for segment clock
// Stolen from fire_201 byte by ldir
// Perlin fire
// https://pastebin.com/jSSVSRi6

//Perlin noise fire procedure 
//16x16 rgb led matrix demo
//Yaroslaw Turbin, 22.06.2020 
//https://vk.com/ldirko
//https://www.reddit.com/user/ldirko/
//https://www.reddit.com/r/FastLED/comments/hgu16i/my_fire_effect_implementation_based_on_perlin/
 
//idea in make perlin noise with time offset X and Z coord 
//this automatic scroll fire pattern    
//and distort fire noise.
//then substract Y based coodrd value to shift 
//fire color (not brightness) in palette. 
//this fadeout color from bottom matrix to up.
//this need some palette tweak for good looking fire color
  
#include "FastLED.h"
#include <Wire.h>
#include <Time.h>
#include "DS3231.h"

#define seconds()		((unsigned long)millis()/1000)
#define minutes()		((unsigned int)seconds()/60)

// LEDs pin
#define DATA_PIN 3
 
// LED brightness
#define BRIGHTNESS 255
#define OFF_LED CRGB(192,192,192)

// Matrix size
#define NUM_ROWS 7
#define NUM_COLS 19
#define NUM_LEDS NUM_ROWS * NUM_COLS

#define DISP_PIN    8
// Display size
#define	SEG_LEDS 		68		// fire: 3*24 = 72, seg disp: 4 * 14 + 3 * 4 = 68
#define SEG_DOTS		33		// 33 and 34

// SEGMENT clock
#define SEG_SEGMENTS	4   // 0,1,2,3
#define SEG_SEG_SIZE	18	// leds from one seg char to another

boolean SEG_FONT[][14] = {
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},	// 0
	{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},	// 1
	{1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1},	// 2
	{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1},	// 3
	{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1},	// 4
	{1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1},	// 5
	{1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},	// 6
	{1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},	// 7
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},	// 8
	{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1},	// 9
	{1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},	// C
	{1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1},	// % left half
	{1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1}	// % right half
};

// Define the arrays of leds
CRGB leds[NUM_LEDS];
CRGB disp[SEG_LEDS];
boolean seg[SEG_LEDS];
// These are physical leds positions on disp
//       1   2       4       6
//  19          22      24
//  38          41  42  43
//      58  59              63
//  76          79  80  81
//  95          98     100
//     115 116     118     120 121 122 123  
uint8_t disp_positions[] = {1,2,22,41,79,98,116,115,95,76,38,19,58,59,118,80,42,4,6,7,27,46,84,103,121,120,100,81,43,24,63,64,123,85,47,9,11,12,32,51,89,108,126,125,105,86,48,29,68,69,128,90,52,14,16,17,37,56,94,113,131,130,110,91,53,34,73,74};

DEFINE_GRADIENT_PALETTE( firepal ) {    // define fire palette
    0,   90,  60,  60,  //black
   64,  255,   0,   0,  // red
  140,  255, 255,   0,  // yellow
  180,  255, 255, 255,  // white
  190,   90,   0,   0,  // brown
  255,    60,   60,   60   // black
};
 
CRGBPalette16 myPal = firepal;

time_t t;
int i;

 
void setup() {
  byte t_sec, t_min, t_hr, t_wd, t_dy, t_mon, t_yr;
  // delay(2000); // POWER UP

  // Serial.begin(9600);
  Wire.begin(); // Serial
  // DS3231
  readDS3231time(&t_sec, &t_min, &t_hr, &t_wd, &t_dy, &t_mon, &t_yr);
  setTime(t_hr, t_min, t_sec, t_dy, t_mon, 2000 + t_yr);

		// set the initial time here:
//		if (yr < 1) {
//			// DS3231  seconds, minutes, hours, wday, date, month, year
//			setDS3231time( 0,      27,    23,    7,   12,     6,   16);
//		}

  t = now();
  // DEBUG
  // Serial.print("Date/Time: ");
  // Serial.print(day(t), DEC);
  // Serial.print(".");
  // Serial.print(month(t), DEC);
  // Serial.print(".");
  // Serial.print(year(t) - 2000, DEC);
  // Serial.print(" ");
  // Serial.print(hour(t), DEC);
  // Serial.print(":");
  // Serial.print(minute(t), DEC);
  // Serial.println(".");

  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, DISP_PIN>(disp, SEG_LEDS);
  FastLED.setBrightness(BRIGHTNESS);

  // JUST TO SEE POSITIONS:
  // for (int i=0; i<min(NUM_LEDS,SEG_LEDS) ; i++ ) {
  //   leds[i] = CRGB::Red;
  //   disp[i] = CRGB::Red;
  //   FastLED.delay(100);
  // }
}
 
 
void loop() {
  int  a = millis();
  static int last_seconds;	boolean secondely	= false;
  static int last_minutes;	boolean minutely	= false;

  secondely = (last_seconds < seconds() ? true : false);
	minutely = (last_minutes < minutes() ? true : false);

  // Update once per second
  if (secondely) {
    setSegs();
    seg[SEG_DOTS] = seg[SEG_DOTS+1] = (last_seconds % 2 == 0 ? true : false);
  }
 
  // DEBUG
  // if (minutely) {
  //   Serial.print("Time: ");
  //   Serial.print(hour(t), DEC);
  //   Serial.print(":");
  //   Serial.print(minute(t), DEC);
  //   Serial.print(":");
  //   Serial.print(second(t), DEC);
  //   Serial.println(".");
  // }

  for (int i = 0; i < NUM_COLS; i++) {
    for (int j = 0; j < NUM_ROWS; j++) {
      leds[XY(i,j)] = ColorFromPalette (myPal,            // palette
        qsub8 ( inoise8 (i*60 , j*60+a , a/3), abs8(j - (NUM_ROWS-1)) * 255 / (NUM_ROWS+4)), // color index
        BRIGHTNESS);                                      // brightness
    }                                                              
  }

  // copy leds values into disp by their positions
  for (int i = 0 ; i < SEG_LEDS ; i++) {
    if (seg[i]) {
      disp[i] = leds[ disp_positions[i] ];
    } else {
      disp[i] = OFF_LED;
    }
  }
 
  if ( secondely ) {
		last_seconds = seconds();
		last_minutes = minutes();
		// last_hours = hours();
	}
	// last_millis = millis();

  FastLED.delay(10);
}
 
uint8_t XY (uint8_t x, uint8_t y) { return (y * NUM_COLS + x);}     //simple function to find led number in led matrix, 
                                                                    //change this to your routine
                                                                    //or generate XY function for your matrix there:
                                                                    //https://macetech.github.io/FastLED-XY-Map-Generator/


// We need to calculate which leds are off and which are on.
// on leds may be affected with fire.
// It's like mask.
void setSegs() {
  t = now();  // maybe localtime() should be used
  int h, m;

  h = minute(t);
  m = second(t);

  // Swith off all segs:
  for (int i=0; i<=SEG_LEDS; i++ ) {
    seg[i] = false;
  }

  // leading zero at 08:25
  if (h>9) {
    segMarkSegments( 0, (int) h / 10 );
  }
  segMarkSegments( 1, (int) h % 10 );
  segMarkSegments( 2, (int) m / 10 );
  segMarkSegments( 3, (int) m % 10 );
}

// Write character at position
// use "seg" array to note, which segments can be fired
// position is display position: 0-3
void segMarkSegments(byte position, byte font_char) {
	byte j, disp_position;

	// strip pos = position * 18
	// for each of 7 segment display (14)
	for ( j = 0 ; j < 14 ; j++ ) 	{
		disp_position = position * SEG_SEG_SIZE + j;
		if ( disp_position < NUM_LEDS ) {
			if ( SEG_FONT[font_char][j] == 1 ) {
        seg[disp_position] = true;
			} else {
        seg[disp_position] = false;
			}
		}
	}
}


// // Write character at position
// // use "leds" array
// void seg_write(byte position, byte font_char, CRGB color) {
// 	byte j, strip_position;

// 	// strip pos = position * 18
// 	// for each of 7 segment display (14)
// 	for ( j = 0 ; j < 14 ; j++ ) 	{
// 		strip_position = position * SEG_SEG_SIZE + j;
// 		if ( strip_position < NUM_LEDS ) {
// 			if ( SEG_FONT[font_char][j] == 1 ) {
// 				if ( color.r == 0 && color.g == 0 && color.b == 0 ) {
// 					leds[strip_position] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
// 				} else {
// 					leds[strip_position] = color;
// 				}
// 			} else {
// 				leds[strip_position] = BACKGROUND_COLOR;
// 			}
// 		}
// 	}
// }