#include <cstring>  // Per strlen()

#define FLASHLEDPIN 4 
#define SAMPLE_PER_SYMBOL 4
#define DATA_LEN 40

//unsigned int inputSignal = 0xABCD1234;

enum receiver_state {
  IDLE, //waiting for sync
  SYNC, //synced, waiting for STX
  START, //STX received
  DATA //receiving DATA
};

enum receiver_state frame_state = IDLE;


volatile unsigned int contatore = 0;    // Indica quale bit del segnale leggere
volatile unsigned int subcontatore = 0; // Contatore per la durata del simbolo
char* segnale = "10100110101010100101"; // Stringa binaria del segnale

unsigned char getNextBit() {
    // Restituisce il bit corrente (1 o 0) come unsigned char
    //Serial.print("sub,cont:");
    //Serial.print(subcontatore);
    //Serial.print("-");
    //Serial.println(contatore);
    // Incrementa il contatore secondario (subcontatore)
    if (subcontatore == SAMPLE_PER_SYMBOL) {
      subcontatore=0;
      contatore++;
      if (contatore == strlen(segnale)) {
            contatore = 0; // Riprendi dall'inizio se il segnale è terminato
      }
    }
    subcontatore++; // Continua a restituire lo stesso bit
    if (segnale[contatore] == '1') {
        return 0x1; // Bit alto
    } else {
        return 0x0; // Bit basso
    }
}


#define IDLE_TIME 20
#define START_SYMBOL 0x02
#define STOP_SYMBOL 0x01
#define START_STOP_MASK ((STOP_SYMBOL << 20) | (START_SYMBOL << 18) | STOP_SYMBOL) //STOP/START/16bits/STOP
#define SYNC_SYMBOL_MANCHESTER (0x6665)
inline int is_a_word(volatile long *manchester_word, int time_from_last_sync, volatile unsigned int *detected_word) {
  if (time_from_last_sync >= IDLE_TIME || frame_state == IDLE) { // we received enough bits to test the sync
    if (((*manchester_word) & START_STOP_MASK) == (START_STOP_MASK)){ // testing first position
      (*detected_word) = ((*manchester_word) >> 2) & 0xFFFF;
      if (frame_state == IDLE) {
        if ((*detected_word) == SYNC_SYMBOL_MANCHESTER)
          return 2;
      }
      return 1;
      // byte with correct framing
    } else if (frame_state != IDLE && time_from_last_sync == IDLE_TIME) {
      (*detected_word) = ((*manchester_word) >> 2) & 0xFFFF;
      return 1;
    }
  }
  return 0;
}

inline int insert_edge(volatile long *manchester_word, signed char edge, int edge_period, volatile int *time_from_last_sync, volatile unsigned int *detected_word) {
  int new_word = 0;
  int is_a_word_value = 0;
  int sync_word_detect = 0;
  if (((*manchester_word) & 0x01) != edge) { //mak sure we don't have same edge ...
    if (edge_period > (SAMPLE_PER_SYMBOL + 1)) { // se ci troviamo di fronte a scenari 00 o 11
      Serial.print("*|");
      unsigned char last_bit = (*manchester_word) & 0x01; //Aggiunge l'ultimo bit
      //Siccome la successione stopstart comporta andare incontro a situazioni 11 allora quandi stiamo in questa situazione puo' essere che e' inizata una nuova parola (un char + start + stop)
      (*manchester_word) = ((*manchester_word) << 1) | last_bit; // signal was steady for longer than a single symbol,
      (*time_from_last_sync) += 1;
      is_a_word_value = is_a_word(manchester_word, (*time_from_last_sync), detected_word);
      if (is_a_word_value > 0) { //found start stop framing
        new_word = 1;
        (*time_from_last_sync) = 0;
        if (is_a_word_value > 1) sync_word_detect = 1; //we detected framing and sync word in manchester format
      }
    }
    //storing edge value in word
    if (edge < 0) {
      Serial.print("Z|");
      (*manchester_word) = ((*manchester_word) << 1) | 0x00; // signal goes down
    } else {
      Serial.print("U|");
      (*manchester_word) = ((*manchester_word) << 1) | 0x01; // signal goes up
    }
    (*time_from_last_sync) += 1;
    is_a_word_value = is_a_word(manchester_word, (*time_from_last_sync), detected_word);
    if (sync_word_detect == 0 && is_a_word_value > 0)  { //if sync word was detected at previous position, don't take word detection into account
      new_word = 1;
      (*time_from_last_sync) = 0;
    }
  }  else  {
    new_word = -1;
  }
  return new_word;
}



volatile long shift_reg = 0;
volatile int dist_last_sync = 0;
volatile unsigned int detected_word = 0;
volatile int new_word = 0;

//inline int insert_edge(volatile long *manchester_word, signed int edge, int edge_period, volatile int *time_from_last_sync, volatile unsigned int *detected_word){
// return(-1);
//}

volatile unsigned int newbit =0;
volatile unsigned int oldbit =0;
volatile  unsigned int symbol_memory =0;
volatile unsigned int symbol_memory_count1 =0;
volatile int edge_val =0;
hw_timer_t *timerRX = NULL;

void ICACHE_RAM_ATTR timer3RX_ISR(void) {
  unsigned int now = millis();
  newbit = getNextBit();
  Serial.print("Letto segnale:");
  Serial.println(newbit);
  digitalWrite(FLASHLEDPIN, newbit);
  if (true) {
        if (newbit > oldbit) {
            symbol_memory = 1;
            symbol_memory_count1 = 0;
            edge_val = 1;
            oldbit = newbit;
        } else if (oldbit > newbit) {
            symbol_memory = -1;
            symbol_memory_count1 = 0;
            edge_val = -1;
            oldbit = newbit;
        } else {
            edge_val = 0;
            symbol_memory_count1 = symbol_memory_count1 + 1;
        }
  }
  Serial.print("Edge(");
  Serial.print(edge_val);
  Serial.print(") BitRepeated(");
  Serial.print(symbol_memory);
  Serial.print(") Repetition(");
  Serial.print(symbol_memory_count1);
  Serial.println(")");
}

volatile int oldValue = 0;
volatile int steady_count = 0;
volatile int old_edge_val = 0;
volatile int symbol_memory_count = 3;
volatile unsigned int sensorValue =0;
void ICACHE_RAM_ATTR timerRX_ISR(void) {
    unsigned int now = millis();
    newbit = getNextBit();
    sensorValue=newbit;
    digitalWrite(FLASHLEDPIN, newbit);
    if (symbol_memory_count > 4 && symbol_memory_count < 7) {
        Serial.println("sonoqui");
        edge_val = 0;
        symbol_memory_count++;
    } else {
        if (sensorValue > oldValue) {
            symbol_memory = 1;
            symbol_memory_count = 0;
            edge_val = 1;
            oldValue = sensorValue;
        } else if (oldValue > sensorValue) {
            symbol_memory = 0;
            symbol_memory_count = 0;
            edge_val = -1;
            oldValue = sensorValue;
        } else {
            edge_val = 0;
            symbol_memory_count =0;
        }
    }
    Serial.print(sensorValue);
    Serial.print("|");
    Serial.print(edge_val);
    Serial.print("|");
    if (edge_val == 0 || edge_val == old_edge_val || (edge_val != old_edge_val && steady_count < 2)) {
        if (steady_count < (2 * SAMPLE_PER_SYMBOL)) {
            steady_count++;
        }
        Serial.print(steady_count);
        Serial.print("|");
    } else {
        Serial.print("INSERT");
        Serial.print("|");
        new_word = insert_edge(&shift_reg, edge_val, steady_count, &dist_last_sync, &detected_word);
        if (dist_last_sync > (4 * SAMPLE_PER_SYMBOL)) {
            dist_last_sync = DATA_LEN;
        }
        if (new_word >= 0) {
            steady_count = 0;
        }
    }
    Serial.print(new_word);
    Serial.print("|");
    Serial.print(dist_last_sync);
    Serial.print("|");
    Serial.print(shift_reg,BIN);
    Serial.print("|");
    Serial.print("|");
    Serial.println(dist_last_sync);
    old_edge_val = edge_val;
}



void setup() {
  Serial.begin(115200);
  pinMode(FLASHLEDPIN, OUTPUT); 
  Serial.println("Started with signal:");
  //Serial.println(inputSignal,BIN);
  //adc1_config_width(ADC_WIDTH_12Bit);  //ko per esp32cam
  for (int i=0; i< strlen(segnale); i++){
    Serial.print(segnale[i]);
  }
  Serial.println("\nSamples:");
  contatore=0;
  subcontatore=0;
  for (int i=0; i<= (strlen(segnale)-1)*SAMPLE_PER_SYMBOL; i++){
    Serial.print(getNextBit());
  }
  contatore=0;
  subcontatore=0;
  Serial.println("---");
  delay(1000);
  timerRX = timerBegin(1000000);
  timerAttachInterrupt(timerRX, &timerRX_ISR);
  timerAlarm(timerRX, 5000000, true,0);
   

  //timer2RX = timerBegin(0, 80, true);  // use tiemr 0 and set prescale to 80 so 1 tick is 1 uSec
  //timerAttachInterrupt(timer2RX, &timer2RX_ISR, true); // point to the ISR
  //timerAlarmWrite(timer2RX, 1000000, true);  // set alarm every 1 sec
  //timerAlarmEnable(timer2RX);  // enable the alarm

  Serial.println("S|E|SC|NW|DLS");


}
void loop() {
  //Serial.print(getNextBit());
  // put your main code here, to run repeatedly:
  delay(10); // this speeds up the simulation
}