/*
  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/>. 
*/

#ifndef LIB_SANDS_H_
#define LIB_SANDS_H_
#include <Arduino.h>
#include <stdint.h>
#include <inttypes.h>
#include "Stream.h"
#include <Wire.h>
#include <EEPROM.h>
#include <HardwareSerial.h>

// For motherboard
#if defined(__AVR_ATmega2560__) // MOTHERBOARD
// UI connector GPIO
#define SerialUI Serial2
#define UI_GPIO0  6 // PH3
#define UI_GPIO1  7 // PH4
#define UI_GPIO2  8 // PH5
#define UI_GPIO3  9 // PH6
#elif defined(__AVR_ATmega32U4__) // SHIELD
#include <SoftwareSerial.h>
extern SoftwareSerial SerialUI;
// UI connector GPIO
#define UI_GPIO0  8 // PB4
#define UI_GPIO1  9 // PB5
#define UI_GPIO2  4 // PD4
#define UI_GPIO3 15 // PB1 (SCLK on ICSP)
// SoftwareSerial pins (for shield only)
#define UI_RX    14 // PB3 (MISO on ICSP)
#define UI_TX    16 // PB2 (MOSI on ICSP)
#endif

typedef enum {
	DEVTYPE_RELAY = 'R',
	DEVTYPE_MOSFET = 'M',
	DEVTYPE_CONSUMPTION = 'C',
	DEVTYPE_INPUT = 'I',
	DEVTYPE_SHIELD = 'S',
} devtype_e;

class SandS_Class : public Stream {
public:
  SandS_Class();

	void begin(char type, char major, char minor, char revision);
	void onCommand(void (*)(byte, int));

	bool loadData(void *data, uint8_t data_size);
	void saveData(void *data, uint8_t data_size);
	void setAlarm(boolean alarm);
	boolean getAlarm(void);
	
  virtual size_t write(uint8_t);
  virtual size_t write(const uint8_t *, size_t);
  void onRequest(void (*function)(byte));
	
  inline int available(void) { return Wire.available(); }
  inline int read(void) { return Wire.read(); }
  inline int peek(void) { return Wire.peek(); }
	inline void flush(void) { return Wire.flush(); }
  
  inline size_t write(unsigned long n) { return write((uint8_t)n); }
  inline size_t write(long n) { return write((uint8_t)n); }
  inline size_t write(unsigned int n) { return write((uint8_t)n); }
  inline size_t write(int n) { return write((uint8_t)n); }
  using Print::write;	
};

extern SandS_Class SandS;

class Module {
  public:
    virtual bool initialize();
  protected:
    uint8_t address;
    char type;
    word maxversion;
    word version;
    bool send(uint8_t *data, uint8_t size);
    bool recv(uint8_t *data, uint8_t size);
    bool initialized;
};

class Relay : public Module {
  private:
    uint8_t  powerupState;
    uint8_t  pwmPeriod;
    uint16_t commTimeout;
	uint8_t  responseMode;
    bool pwm;

  public:
    Relay(uint8_t address);

    static const uint8_t ENGAGED    = 0x01;
    static const uint8_t DISENGAGED = 0x00;
    static const uint8_t PWM        = 0x80;

    bool initialize();
    void digitalWrite(uint8_t value);
    void analogWrite(uint8_t value);
	bool setConfiguration(uint8_t powerupState, uint8_t pwmPeriod, word commTimeout, byte responseMode);
};

class Mosfet : public Module {
  private:
    uint8_t  powerupState;
    uint8_t  pwmFrequency;
    uint16_t commTimeout;
	uint8_t  alarmMode;
	uint16_t alarmLevel;
	uint16_t alarmDelay;
	uint8_t  responseMode;
    bool pwm;

  public:
    Mosfet(uint8_t address);

    static const uint8_t ENGAGED    = 0x01;
    static const uint8_t DISENGAGED = 0x00;
    static const uint8_t PWM        = 0x80;

    bool initialize();
    void digitalWrite(uint8_t value);
    void analogWrite(uint8_t value);
    bool setConfiguration(uint8_t powerupState, uint8_t pwmFrequency, word commTimeout, byte alarmMode, word alarmLevel, word alarmDelay, byte responseMode);
};

class Consumption : public Module {
  private:
    uint8_t  ampRange; // Amperes at 1/3V input
	uint16_t commTimeout;
	uint8_t  alarmMode;
	uint16_t alarmLevel;
	uint16_t alarmDelay;
    
  public:
    Consumption(uint8_t address);

    bool initialize();
    word analogRead();
    bool setConfiguration(uint8_t range, word commTimeout, byte alarmMode, word alarmLevel, word alarmDelay);
};

class Input : public Module {
  private:
    double   bias;
    word     gain;
    uint8_t  mode;
	uint16_t commTimeout;
	uint8_t  alarmMode;
	uint16_t alarmLevel;
	uint16_t alarmDelay;

  public:
    Input(uint8_t address);

    static const uint8_t ANALOG3V3  = 0x00;
    static const uint8_t ANALOG12V  = 0x01;
    static const uint8_t ANALOG24V  = 0x02;
    static const uint8_t RESISTANCE = 0x03;
    static const uint8_t DIGITAL3V3 = 0x10;
    static const uint8_t DIGITAL12V = 0x11;
    static const uint8_t DIGITAL24V = 0x12;
    static const uint8_t AC3V3      = 0x20;
    static const uint8_t AC12V      = 0x21;
    static const uint8_t AC24V      = 0x22;

    bool initialize();
    double analogRead();
    bool digitalRead();

    bool setBias(double bias);
    bool setGain(word gain);
    bool setConfiguration(uint8_t mode, double bias, word gain, word commTimeout, byte alarmMode, word alarmLevel, word alarmDelay);
};

enum {
  SERIAL_RS232,
  SERIAL_RS485_FD,
  SERIAL_RS485_HD,
};

#if defined(__AVR_ATmega2560__) // MOTHERBOARD
#define MaxOnMoBo Serial1
class Max3160 : public Stream {
  private:
  public:
    Max3160() {}
    bool begin(unsigned long, uint8_t = SERIAL_8N1, byte = SERIAL_RS232, boolean = false);
    void end() { MaxOnMoBo.end(); }
    virtual int available(void) { return MaxOnMoBo.available(); }
    virtual int peek(void) { return MaxOnMoBo.peek(); }
    virtual int read(void) { return MaxOnMoBo.read(); }
    virtual void flush(void) { MaxOnMoBo.flush(); }
    virtual size_t write(uint8_t n) { return MaxOnMoBo.write(n); }
    inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }
	virtual void setRTS(boolean);
    using Print::write; // pull in write(str) and write(buf, size) from Print
    operator bool();
};
#elif defined(__AVR_ATmega32U4__) // SHIELD
class Max3160 : public Stream {
  private:
	int rxcount;
  public:
    Max3160() {}
    bool begin(unsigned long, uint8_t = SERIAL_8N1, byte = SERIAL_RS232, boolean = false);
    void end() { }
    virtual int available(void);
    virtual int peek(void);
    virtual int read(void);
    virtual void flush(void) {}
    virtual size_t write(uint8_t n);
    inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }
	virtual void setRTS(boolean);
    using Print::write; // pull in write(str) and write(buf, size) from Print
    operator bool();
};
#endif
extern Max3160 SerialPort;


#endif

void WireSEI();
void WireCLI();
