/*********
 * Blinkinlove / Materix
 * Techno-romantic jewelery based on
 * Adafruit Feather HUZZAH boards and
 * 4*8 NeoPixel Matrix FeatherWings
 *********/

#include "config.h"

#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
// Default for the featherwing is 16, but for Huzzah it can't work
// 15 is recommended (requires cutting 16 jumper and soldering 15)
#define NEOPIXEL_PIN    15

// How many NeoPixels are attached to the Arduino?
#define NEOPIXEL_COUNT (4*8)

Adafruit_NeoPixel strip(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

#include <PGMWrap.h>

int8_p heart0[8 * 4 * 3] PROGMEM = {
  0, 0, 0,    16, 0, 0,    64, 0, 0,    0, 0, 0,    116, 0, 0,    16, 0, 0,    0, 0, 0,    0, 0, 0,
  0, 0, 0,    64, 0, 0,    32, 0, 0,    60, 0, 0,    29, 0, 0,    145, 0, 0,    0, 0, 0,    0, 0, 0,
  0, 0, 0,    0, 0, 0,    125, 1, 1,    48, 0, 0,    126, 0, 0,    0, 0, 0,    0, 0, 0,    0, 0, 0,
  0, 0, 0,    0, 0, 0,    0, 0, 0,    134, 0, 0,    0, 0, 0,    0, 0, 0,    0, 0, 0,    0, 0, 0
};

int8_p heart1[8 * 4 * 3] PROGMEM = {
  0, 0, 0,    32, 0, 0,    160, 0, 0,    0, 0, 0,    185, 0, 0,    32, 0, 0,    0, 0, 0,   0, 0, 0,
  0, 0, 0,    160, 0, 0,    64, 0, 0,    120, 0, 0,    58, 0, 0,    200, 0, 0,    0, 0, 0,    0, 0, 0,
  0, 0, 0,    0, 0, 0,    190, 1, 1,    96, 0, 0,    190, 0, 0,    0, 0, 0,    0, 0, 0,    0, 0, 0,
  0, 0, 0,    0, 0, 0,    0, 0, 0,    194, 0, 0,    0, 0, 0,    0, 0, 0,    0, 0, 0,    0, 0, 0
};

void drawImage(int8_p *img) {
  for (int i = 0; i < 8 * 4; i++) {
    strip.setPixelColor(i, strip.Color(img[i * 3], img[i * 3 + 1], img[i * 3 + 2]));
  }
  strip.show();
}

#define NUM_FRAMES 9
int8_p *frames[NUM_FRAMES] = {
  heart1, heart1, heart0,
  heart1, heart1, heart0,
  heart0, heart0, heart0
};

#define DURATION_FAST 30
#define DURATION_SLOW 300

int current_frame = 0;
unsigned long last_flip = 0;

#define NUM_SCAN_PIXELS 4
int scan_pixels[NUM_SCAN_PIXELS] = { 31, 23, 15, 7 };
#define SCAN_FRAME_DURATION 200


// Zzzen vanity logo (scrolls after reset)
int8_p zzzen[24 * 4] PROGMEM = {
  255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 0, 0, 255,
  0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 255, 128, 128, 255, 0, 255, 255, 0, 255,
  0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 255, 255,
  255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 255, 0, 0, 255
};


void scrollZzzen(int offs) {
  for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 8; x++) {
      int xoffs = x+offs, level = 0;
      if (xoffs>=0 && xoffs<24) {
        level = zzzen[24*y+xoffs];
      }
      strip.setPixelColor(8 * y + x, strip.Color(0, 0, level));
    }
  }
  strip.show();
}

#include "ESP8266WiFi.h"



#define SCAN_INTERVAL 6000
unsigned long next_scan = 0;

String my_mac;
String mate_mac;

#define RSSI_NEAR -20
#define RSSI_FAR -100
int rssi = 0;
bool scanning = false;

// set rssi to mate's rssi (0 if not found)
void checkScanResult(int numResults) {
  rssi = 0;
  if (numResults == 0) {
    if (DEBUG) {
      Serial.println("no networks found");
    }
  } else {
    if (DEBUG) {
      Serial.print(numResults);
      Serial.println(" networks found");
    }
    for (int i = 0; i < numResults; ++i) {
      if (mate_mac.equals(WiFi.BSSIDstr(i))) {
        rssi = WiFi.RSSI(i);
        break;
      }
    }
  }
  if (DEBUG) {
    Serial.print(mate_mac);
    if (rssi) {
      Serial.print(" RSSI: ");
      Serial.println(rssi);
    } else {
      Serial.println(" not found");
    }
  }
  scanning = false;
}


// Show heart animation frame according to rssi)
// If scanning: indicate that (animation if no rssi, single pixel otherwise)
void animate(int rssi, bool scanning, unsigned long nowmillis) {
  if (rssi) {
    int duration = constrain(
                     map(rssi, RSSI_NEAR, RSSI_FAR, DURATION_FAST, DURATION_SLOW),
                     DURATION_FAST, DURATION_SLOW);
    if ((nowmillis - last_flip) > duration || nowmillis < last_flip) {
      drawImage(frames[current_frame]);
      current_frame = (current_frame + 1) % NUM_FRAMES;
      last_flip = nowmillis;
    }
  } else {
    drawImage(heart0);
    current_frame = 0;
  }
  if (scanning) {
    if (rssi) {
      strip.setPixelColor(scan_pixels[0], strip.Color(0, 0, 16));
    } else {
      int bright = (nowmillis / SCAN_FRAME_DURATION) % NUM_SCAN_PIXELS;
      for (int i = 0; i < NUM_SCAN_PIXELS; i++) {
        int blueness = i == bright ? 64 : i < bright ? 16 : 0;
        strip.setPixelColor(scan_pixels[i], strip.Color(0, 0, blueness));
      }
    }
    strip.show();
  }
}


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

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.setBrightness(32); // (max = 255)
  strip.show();            // Turn OFF all pixels ASAP

  for (int offs = -23 ; offs < 25; offs++) {
    scrollZzzen(offs);
    delay(100);
  }
  
  my_mac = WiFi.softAPmacAddress();
  if (DEBUG) {
    Serial.print("My MAC: ");
    Serial.println(my_mac);
  }
  if (my_mac.equals(HERS)) {
    mate_mac = String(HIS);
  } else {
    mate_mac = String(HERS);
  }
  WiFi.softAP(String("blinkinlove:") + my_mac.substring(12), "whereartthou");

}

void loop()
{
  unsigned long nowmillis = millis();
  if (nowmillis > next_scan) {
    if (DEBUG) {
      Serial.print("Scanning for ");
      Serial.println(mate_mac);
    }
    scanning = true;
    WiFi.scanNetworksAsync(checkScanResult);
    next_scan = nowmillis + SCAN_INTERVAL;
  }
  animate(rssi, scanning, nowmillis);
  delay(20);
}
