// James Ferguson
// CE351
// Temperature Master

#include <SPI.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define ONE_WIRE_BUS 2
#define UP_BUTTON 3
#define SELECT_BUTTON 4
#define DOWN_BUTTON 5

bool upButtonState = false;
bool lastUpButtonState = false;
bool selectButtonState = false;
bool lastSelectButtonState = false;
bool downButtonState = false;
bool lastDownButtonState = false;

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature temperatureSensor(&oneWire);

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// state
enum States {LOGGINGWIFI_CONFIGACCESS_POINT_CONFIGPASSWORD_CONFIG};

// SPI commands
const byte SIMO_TEMPERATURE = B00000001;
const byte SIMO_CONNECT_WIFI = B00000010;
const byte SIMO_ACCESS_POINT = B00000011;
const byte SIMO_PASSWORD = B00000100;

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTHSCREEN_HEIGHT&Wire, -1);

float temperature = 0;



void setup() {
  Serial.begin(115200);
  
  digitalWrite(SS, HIGH); // disable Slave Select
  SPI.begin ();
  SPI.setClockDivider(SPI_CLOCK_DIV128);//divide the clock by 128

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    Serial.println("SSD1306 allocation failed");
    for(;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  display.display();
}

void loop() {
  upButtonState = digitalRead(UP_BUTTON);
  selectButtonState = digitalRead(SELECT_BUTTON);
  downButtonState = digitalRead(DOWN_BUTTON);

  static States state;
  
  switch(state) {
    case LOGGING :
      state = logging();
      break;
      
    case WIFI_CONFIG :
      state = wifiConfig();
      break;
  
    case PASSWORD_CONFIG : {
      static unsigned int position;
      static String password;
      if (editValue(&password, &position)) {
        state = WIFI_CONFIG;
        position = 0;

        // send temperature data to slave
        digitalWrite(SS, LOW); // enable Slave Select
        SPI.transfer(SIMO_PASSWORD); // transmit command
        for (unsigned int i = 0; i < password.length(); i++) { // dransmit data asociated with command
          SPI.transfer(password[i]);
        }
        SPI.transfer('\r');
        digitalWrite(SS, HIGH); // disable Slave Select
        
      } else {
        state = PASSWORD_CONFIG;
      }
    } break;

    case ACCESS_POINT_CONFIG : {
      static unsigned int position;
      static String accesPoint;
      if (editValue(&accesPoint, &position)) {
        state = WIFI_CONFIG;
        position = 0;

        // send temperature data to slave
        digitalWrite(SS, LOW); // enable Slave Select
        SPI.transfer(SIMO_ACCESS_POINT); // transmit command
        for (unsigned int i = 0; i < accesPoint.length(); i++) { // dransmit data asociated with command
          SPI.transfer(accesPoint[i]);
        }
        SPI.transfer('\r');
        digitalWrite(SS, HIGH); // disable Slave Select
        
      } else {
        state = ACCESS_POINT_CONFIG;
      }
    } break;
   
    default :
      state = LOGGING;
      break;
  }
  lastUpButtonState = upButtonState;
  lastSelectButtonState = selectButtonState;
  lastDownButtonState = downButtonState;
}

States logging () {

  if ((selectButtonState == true&& (lastSelectButtonState == false)) { // select button pressed switch to config menu
    return WIFI_CONFIG;
  }
  
  temperatureSensor.requestTemperatures();
  temperature = temperatureSensor.getTempCByIndex(0);

  // send temperature data to slave
  digitalWrite(SS, LOW); // enable Slave Select
  SPI.transfer(SIMO_TEMPERATURE);
  String sendData = String(temperature);
  for (unsigned int i = 0; i < sendData.length(); i++) {
    SPI.transfer(sendData[i]);
  }
  SPI.transfer('\r');
  digitalWrite(SS, HIGH); // disable Slave Select
      
  display.clearDisplay();
  display.drawRect(00, SCREEN_WIDTH, 11, SSD1306_WHITE);
  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(22);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
  display.print("Temperature: ");
  display.print(temperature);
  display.print(char(248)); // degree charicter
  display.print("C");
  display.display();

  return LOGGING;
}

States wifiConfig () {
  enum MenuItem {BACKACCESS_POINTPASSWORDAPPLYEND};
  const String menuItemText[] = {"Back""AP""Paswrd""Ok"};
  static MenuItem menuPosition;

  if ((upButtonState == true&& (lastUpButtonState == false)) { // menu up
    if (menuPosition == 0) {
      menuPosition = END - 1;
    } else {
      menuPosition = menuPosition - 1;
    }
    
  } else if ((downButtonState == true&& (lastDownButtonState == false)) { // menu down
    if (menuPosition == END - 1) {
      menuPosition = 0;
    } else {
      menuPosition = menuPosition + 1;
    }
    
  } else if ((selectButtonState == true&& (lastSelectButtonState == false)) { // select button pressed
    switch(menuPosition) {
      case BACK :
        return LOGGING;
        break;
        
      case ACCESS_POINT : {
        return ACCESS_POINT_CONFIG;
        break; }
  
      case PASSWORD : {
        return PASSWORD_CONFIG;
        break; }

      case APPLY : {
        Serial.println("Apply");
        // send temperature data to slave
        digitalWrite(SS, LOW); // enable Slave Select
        SPI.transfer(SIMO_CONNECT_WIFI);
        digitalWrite(SS, HIGH); // disable Slave Select
        return LOGGING;
        break; }
    }
  }
  
  display.clearDisplay();
  display.setTextSize(1);      // Normal 1:1 pixel scale

  for (MenuItem i = 0; i < END; i = i + 1) {
    display.setCursor(22 + i * 10);     // Start at top-left corner
    if (menuPosition == i) {
      display.drawRect(0, i*10, SCREEN_WIDTH, 11, SSD1306_WHITE);
    }
    display.setTextColor(SSD1306_WHITE);
    display.print(menuItemText[i]);
  }
  display.display();

  return WIFI_CONFIG;
}

bool editValue (String* value, unsigned int* position) { // returns true if value finished entering false if otherwise
  const int maxLength = 5; // set to one less than the maximum amount of charicters
  const int symbolsLength = 1+26;
  const char symbols[symbolsLength] = " abcdefghijklmnopqrstuvwxyz"; // list of posable chars
  unsigned int charIndex; // current index into above string 

  // increase the length of the string to edit char at value
  while (value->length() < *position + 1) { 
    *value = *value + " ";
  }

  // get charIndex at position
  for (unsigned int i = 0; i < symbolsLength; i++) {
    if ((*value)[value->length() - 1== symbols[i]) {
      charIndex = i;
      break;
    }
  }

  // button input
  if ((upButtonState == true&& (lastUpButtonState == false)) { // menu up cycle down a char at position
    charIndex--;
    if (charIndex == -1) {
      charIndex = symbolsLength - 1;
    }
    (*value)[*position] = symbols[charIndex];
  } else if ((downButtonState == true&& (lastDownButtonState == false)) { // menu down cycle up a char at position
    charIndex++;
    if (charIndex == symbolsLength) {
      charIndex = 0;
    }
    (*value)[*position] = symbols[charIndex];

  } else if ((selectButtonState == true&& (lastSelectButtonState == false)) { // select button pressed increase position or exit
    if (*position == maxLength) { // ran out of chars exit editor
      while ((*value)[value->length() - 1== ' ') { // remove empty spaces at the end
        value->remove(value->length() - 1);
      }
      return true;
    }
    *position = *position + 1; // go to next position
  }

  // update display
  display.clearDisplay();
  display.drawRect(04, maxLength * 7 + 411, SSD1306_WHITE);
  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(26);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
  display.print(*value);
  display.drawTriangle(*position * 6 + 40*position * 6 + 22*position * 6 + 62, SSD1306_WHITE); // draw charicter highlight
  display.drawTriangle(*position * 6 + 418*position * 6 + 216*position * 6 + 616, SSD1306_WHITE); // draw charicter highlight
  display.display();
  
  return false;
}