/*
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>. 
*/

#include <Wire.h>

#if defined(__AVR_ATmega2560__) // MOTHERBOARD
#define PIN_IDENT 36
#define PIN_ALARM 37
#elif defined(__AVR_ATmega32U4__) // SHIELD
#define PIN_IDENT 5
#define PIN_ALARM 6
#endif

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

byte ser_in;
const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

bool SerialReadHexByte(byte *out) {
  do {
    while(!Serial.available());
    ser_in = Serial.read();
  } while(ser_in == ' ');
  if(ser_in >= '0' && ser_in <= '9') *out = ser_in - '0';
  else if(ser_in >= 'a' && ser_in <= 'f') *out = ser_in - 'a' + 10;
  else if(ser_in >= 'A' && ser_in <= 'F') *out = ser_in - 'A' + 10;
  else return false;
  *out <<= 4;
  while(!Serial.available());
  ser_in = Serial.read();
  if(ser_in >= '0' && ser_in <= '9') *out |= ser_in - '0';
  else if(ser_in >= 'a' && ser_in <= 'f') *out |= ser_in - 'a' + 10;
  else if(ser_in >= 'A' && ser_in <= 'F') *out |= ser_in - 'A' + 10;
  else return false;
  return true;
}

void SerialWriteHexByte(byte data) {
  Serial.write(hex[data >> 4]);
  Serial.write(hex[data & 0xF]);
}

void loop() {
  byte address, data;
  int ok;
  while(Serial.available()) {
    ser_in = Serial.read();
    if(ser_in != ' ' && ser_in != '\n') {
      if(ser_in == 'W') {
        ok = 0;
        if(SerialReadHexByte(&address)) {
          if(address & 0x80) {
            Serial.write("Address must be in range 00-7F HEX\n");
          } else {
            Wire.beginTransmission(address);
            SerialWriteHexByte(address);
            Serial.write(" <-");
            while(SerialReadHexByte(&data)) {
              Serial.write(' ');
              SerialWriteHexByte(data);
              Wire.write(data);
              ok++;
            }
            Serial.write('\n');
            if(ser_in != '\n') Serial.write("Encountered bad character\n");
            Wire.endTransmission();
            Serial.write("Sent ");
            Serial.print(ok);
            Serial.write(" bytes\n");
            ok++;
          }
        }
        if(!ok) Serial.write("W [Address HEX] ([Byte-0 HEX]( ... ([Byte-n HEX])))\n");
      } else if(ser_in == 'R') {
        ok = 0;
        if(SerialReadHexByte(&address)) {
          ok = 1;
          if(address & 0x80) {
            Serial.write("Address must be in range 00-7F\n");
          } else {
            if(SerialReadHexByte(&data)) {
              if((data - 1) & 0xE0) {
                Serial.write("Byte count must be in range 01-20 HEX\n");
              } else {
                Wire.requestFrom(address, data);
                ok = 0;
                SerialWriteHexByte(address);
                Serial.write(" ->");
                while(Wire.available()) {
                  data = Wire.read();
                  Serial.write(' ');
                  SerialWriteHexByte(data);
                  ok++;
                }
                Serial.write("\nReceived ");
                Serial.print(ok);
                Serial.write(" bytes\n");
                ok++;
              }
            } else {
              ok = 0;
            }
          }
        }
        if(!ok) Serial.write("R [Address HEX] [Byte count HEX]\n");
      } else if(ser_in == 'O') {
        if(SerialReadHexByte(&data)) {
          if(data & 0x01) {
            digitalWrite(PIN_IDENT, HIGH);
            Serial.write("Ident enabled\n");
          } else {
            digitalWrite(PIN_IDENT, LOW);
            Serial.write("Ident disabled\n");
          }
        } else {
          Serial.write("O [Flags]\n");
        }
      } else if(ser_in == 'I') {
        data = 0x00;
        if(digitalRead(PIN_ALARM)) data |= 0x01;
        Serial.write("Flags: ");
        SerialWriteHexByte(data);
        Serial.write('\n');
      } else {
        Serial.write("Bad command\n");
      }
      if(ser_in != '\n') while(Serial.read() != '\n');
    }
  }
}
