#include <Arduino.h>

#include <Adafruit_NeoPixel.h>

#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Hack.h>

Adafruit_NeoPixel pixel(1, 12, NEO_GRB + NEO_KHZ800);

static MFRC522DriverPinSimple cs_pin(D0);
static MFRC522DriverSPI driver(cs_pin);
static MFRC522 mfrc522(driver);

constexpr uint8_t DEFAULT_KEY[4] = {0, 0, 0, 0};
static uint8_t key_bytes[4];

constexpr uint8_t IRQ = D7;

static bool keyIsDefault() {
  return !memcmp(key_bytes, DEFAULT_KEY, sizeof(DEFAULT_KEY));
}

static void printSelectedUid();

constexpr float LED_SCALE = 0.3;

static uint8_t R, G, B;

struct Color {
  uint8_t r, g, b;
};

constexpr Color
  READ_NODATA = Color { 0, 255, 255 },
  READ = Color { 0, 0, 255 },
  ERROR = Color { 255, 0, 0 };

constexpr uint8_t rescale_color(uint8_t channel) {
  return (uint8_t)((float)channel * LED_SCALE);
}

static void setColor(uint8_t r, uint8_t g, uint8_t b, bool retain = true) {
    pixel.clear();
    pixel.setPixelColor(0, pixel.Color(rescale_color(r), rescale_color(g), rescale_color(b)));
    pixel.show();

    if (retain) {
      R = r;
      G = g;
      B = b;
    }
}

static void setColor(const Color& color, bool retain = true) {
  setColor(color.r, color.g, color.b, retain);
}

static void restoreColor() {
  setColor(R, G, B);
}

static void updateColor() {
  if (keyIsDefault()) setColor(READ_NODATA);
  else setColor(READ);  
}

void setup() {
    Serial.begin(115200);

    pixel.begin();

    pinMode(11, OUTPUT);
    digitalWrite(11, HIGH);

    pinMode(IRQ, INPUT_PULLDOWN);

    attachInterrupt(digitalPinToInterrupt(IRQ), []() {
      Serial.println("IRQ");
    }, CHANGE);
    
    setColor(READ_NODATA);

    mfrc522.PCD_Init();

    memset(key_bytes, 0x0, sizeof(key_bytes));

    Serial.println("\n\n\n");
    Serial.println("card cloner start ok");
}

void loop() {
    unsigned long delayTime = 15;
    bool status;

    auto cardPresent = mfrc522.PICC_IsNewCardPresent();
    if (!cardPresent) {
      status = true;
      goto end;
    }

    pixel.clear();
    pixel.show();

    if (!mfrc522.PICC_ReadCardSerial()) goto end;

    Serial.printf("read card (sak: 0x%02x), uid: ", mfrc522.uid.sak);
    printSelectedUid();
    Serial.println();

    memcpy(key_bytes, mfrc522.uid.uidByte, MIN(mfrc522.uid.size, sizeof(key_bytes)));
    updateColor();
    setColor(0, 0, 0, false);

    delayTime = 300;
    status = true;

end:
    if (!status) setColor(ERROR, false);

    delay(delayTime / 2);
    restoreColor();
    delay(delayTime / 2);
}

static void printSelectedUid() {
    for (int i = 0; i < mfrc522.uid.size; i++) Serial.printf("%02x", mfrc522.uid.uidByte[i]);
}
