#include <Arduino.h>


#define ESPNOW
//#define UDP

#ifdef UDP
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#endif

#ifdef ESPNOW
#include <ESP8266WiFi.h>
#include <espnow.h>
#endif

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = RECEIVERMAC;

#ifdef UDP
const char* ssid = "network";
const char* password = "password";


WiFiUDP Udp;
unsigned int localUdpPort = 4210;  // local port to listen on
char incomingPacket[255];  // buffer for incoming packets
//char  replyPacket[] = "Hi there! Got the message :-)";  // a reply string to send back

unsigned int remoteUDPPort = 5005;
IPAddress remoteIP = IPAddress(192,168,1,221);
#endif


#ifdef ESPNOW
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus);
#endif

bool flag_init_send;


enum Action {
  HELLO=0,
  PRESS=1,
  RELEASE=2,
  DOWN=3,
  UP=4
}; 

#define INPUT_SIZE 6
enum Input {
  KEY_LEFT=0,
  KEY_RIGHT=1,
  KEY_UP=2,
  KEY_DOWN=3,
  KEY_A=4,
  KEY_B=5
}; 


typedef struct struct_message {
  uint8_t id;
  Action action;
  Input input;
} struct_message;

struct_message myData;


uint8_t input_pins[] = {PIN_LEFT,PIN_RIGHT,PIN_UP,PIN_DOWN,PIN_A,PIN_B};
bool sent_states[INPUT_SIZE];
bool input_states[INPUT_SIZE];
unsigned long last_send_state[INPUT_SIZE];
#define DEBOUNCE_TIME 50


bool flag_send=false;



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

  for (uint8_t i=0;i<INPUT_SIZE;i++)
  {
    pinMode(input_pins[i],INPUT_PULLUP);
  }

  myData.id=ID;
  myData.action=HELLO;
  myData.input=KEY_LEFT; //does not matter

#ifdef ESPNOW

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  Serial.print("WiFi-Channel Default: ");
  Serial.println(WiFi.channel());
  
  wifi_promiscuous_enable(true);
  wifi_set_channel(CHANNEL);
  wifi_promiscuous_enable(false); 
  Serial.print("WiFi Channel Update : ");
  Serial.println(WiFi.channel());

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);

#endif

#ifdef UDP
  
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" connected");
  

  Udp.begin(localUdpPort);
  Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
#endif
  flag_init_send=true;
}


void loop()
{
  unsigned long loopmillis=millis();

#ifdef UDP
  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
    // receive incoming UDP packets
    Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
    int len = Udp.read(incomingPacket, 255);
    if (len > 0)
    {
      incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet contents: %s\n", incomingPacket);

  }
#endif

  for (uint8_t i=0;i<INPUT_SIZE;i++)
  {
    input_states[i]=!digitalRead(input_pins[i]); 
    //input_states High=Pressed
  }

  
  for (uint8_t i=0;i<INPUT_SIZE;i++)
  {
    if (input_states[i]!=sent_states[i] && loopmillis-last_send_state[i]>DEBOUNCE_TIME) //input changed
    {
      last_send_state[i]=loopmillis;
      if (input_states[i]){
        myData.action=PRESS;
      }else{
        myData.action=RELEASE;
      }
      
      myData.input=Input(i);
      flag_send=true;
    }
      
    
  }

  #define TIME_IDLE_SEND 60000 //ms. 0 disables
  static unsigned long last_idlesend=0;
  if (((millis()-last_idlesend > TIME_IDLE_SEND && TIME_IDLE_SEND!=0) || (flag_init_send)) && !flag_send){
    last_idlesend=millis();
    flag_init_send=false;
    myData.action=HELLO;
    
    flag_send=true;
  }
  

  if (flag_send) {
    flag_send=false;
    
    
#ifdef UDP
    Udp.beginPacket(remoteIP, remoteUDPPort);
    char packet[3];
    
    packet[0]=myData.id;
    packet[1]=myData.action;
    packet[2]=myData.input;

    if (myData.action==PRESS){
      sent_states[myData.input]=true;
    }else if (myData.action==RELEASE){
      sent_states[myData.input]=false;
    }

    Serial.printf("sending %i %i %i\n",packet[0],packet[1],packet[2]);
    
    Udp.write(packet,sizeof(packet));
    
    Udp.endPacket();
#endif
#ifdef ESPNOW

    if (myData.action==PRESS){
      sent_states[myData.input]=true;
    }else if (myData.action==RELEASE){
      sent_states[myData.input]=false;
    }
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    Serial.printf("sending");
#endif

  }
}


#ifdef ESPNOW
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  //Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0){
    //Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
    flag_send=true;
  }
}
#endif
