//Z21 DCC RailCom Decoder
//Dekodierung globaler Railcomdaten am Serial3 (MEGA only!)
//
//Copyright (c) by Philipp Gahtow, year 2021

//--------------------------------------------------------------
#if defined(DCCGLOBALDETECTOR) && defined(DCC)

uint16_t RailcomCVAdr = 0;  //init global RailCom detector for POM

//--------------------------------------------------------------
#if defined(MEGA_MCU)
/*
void notifyCVPOMRead(uint16_t Adr, uint8_t value) {
  #if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
    z21.setCVPOMBYTE(Adr, value);
  #endif
}
*/

#define RCSerial Serial3  //Serial Port that we will listen to for RailCom Data

//--------------------------------------------------------------
byte RailComGetCV = false;  //try to receive a CV value
byte RailComReadFirst = true;
byte RailComReadData = 0; //Save the first 2 bit data
byte RailComReadLastCV = 0xFF; //Save the last read CV value
byte RailComCVRead = false; //receive a CV value
byte RailComCVTime = 0; //cycle time
uint16_t RailComReadAdr = 0;  //Adress read over RailCom

//--------------------------------------------------------------
//init the Serial interface for receiving RailCom
void RailComSetup(void) {
  RCSerial.begin(250000);    //Read Railcom Message at 250KBit
}

//--------------------------------------------------------------
//Decoding RailCom Received Data
byte RailComDecodeInData (void) {
  switch (RCSerial.read()) {
    case B10101100: return 0x00;
    case B10110010: return 0x10; 
    case B01010110: return 0x20;
    case B11000110: return 0x30;
    case B10101010: return 0x01;
    case B10110100: return 0x11;
    case B01001110: return 0x21;
    case B11001100: return 0x31;
    case B10101001: return 0x02;
    case B10111000: return 0x12;
    case B01001101: return 0x22;
    case B01111000: return 0x32;
    case B10100101: return 0x03;
    case B01110100: return 0x13;
    case B01001011: return 0x23;
    case B00010111: return 0x33;
    case B10100011: return 0x04;
    case B01110010: return 0x14;
    case B01000111: return 0x24;
    case B00011011: return 0x34;
    case B10100110: return 0x05;
    case B01101100: return 0x15;
    case B01110001: return 0x25;
    case B00011101: return 0x35;
    case B10011100: return 0x06;
    case B01101010: return 0x16;
    case B11101000: return 0x26;
    case B00011110: return 0x36;
    case B10011010: return 0x07;
    case B01101001: return 0x17;
    case B11100100: return 0x27;
    case B00101110: return 0x37;
    case B10011001: return 0x08;
    case B01100101: return 0x18;
    case B11100010: return 0x28;
    case B00110110: return 0x38;
    case B10010101: return 0x09;
    case B01100011: return 0x19;
    case B11010001: return 0x29;
    case B00111010: return 0x39;
    case B10010011: return 0x0A;
    case B01100110: return 0x1A;
    case B11001001: return 0x2A;
    case B00100111: return 0x3A;
    case B10010110: return 0x0B;
    case B01011100: return 0x1B;
    case B11000101: return 0x2B;
    case B00101011: return 0x3B;
    case B10001110: return 0x0C;
    case B01011010: return 0x1C;
    case B11011000: return 0x2C;
    case B00101101: return 0x3C;
    case B10001101: return 0x0D;
    case B01011001: return 0x1D;
    case B11010100: return 0x2D;
    case B00110101: return 0x3D;
    case B10001011: return 0x0E;
    case B01010101: return 0x1E;
    case B11010010: return 0x2E;
    case B00111001: return 0x3E;
    case B10110001: return 0x0F;
    case B01010011: return 0x1F;
    case B11001010: return 0x2F;
    case B00110011: return 0x3F;
    case 0x0F: return 0xF0;   //ACK
  }
  return 0xFF;
}

//--------------------------------------------------------------
//Check if we receive data?
void RailComRead(void) {
  if (RailComGetCV) { //need to read data?
    RailComCVTime++;
    while (RCSerial.available()) {  //is there serial data in buffer?
      byte data = RailComDecodeInData();   //read Railcom Data and decode Railcom Data
      if (data < 0x40) {    //gültige Railcom Message empfangen
            if (RailComReadFirst) {
              RailComReadData = data;   //Save first Byte of Data
              if ((data >> 2) < 3)  //app:pom, app:adr_low, app:adr_high -> read only 12 Bit!
                RailComReadFirst = false;
            }
            else {
              RailComReadFirst = true;
              byte RailComID = RailComReadData >> 2; //Save ID
              RailComReadData = (RailComReadData << 6) | data;    //first 2 Bit and add next 6 Bit
              if (RailComID == 0) { //app:pom
                  RailComCVTime = 25; //Reset Timer!
                  RailComReadLastCV = RailComReadData;
                  RailComCVRead = true;
              }
              else if (RailComID == 1) { //app:adr_high
                RailComReadAdr = (RailComReadData << 8) | (RailComReadAdr & 0xFF);
              }
              else if (RailComID == 2) {  //app:adr_low
                RailComReadAdr = (RailComReadAdr & 0xFF00) | RailComReadData;
              }
            }
      }
      else RailComReadFirst = true;
    }
    if (RailComCVTime == 0) {  //TIMEOUT?
      RailComGetCV = false; //Stop reading! ERROR!
      if (RailComCVRead == true) {
        RailComCVRead = false;
        #if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
        z21.setCVPOMBYTE(RailcomCVAdr, RailComReadLastCV);
        #endif
        #if defined(RCDEB)
        Debug.print(F(" result: A"));
        Debug.print(RailComReadAdr);
        Debug.print(" CV");
        Debug.print(RailcomCVAdr+1);
        Debug.print(" = ");
        Debug.println(RailComReadLastCV);
        #endif
      }
      else {
        #if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
        z21.setCVNack();  //Antwort: LAN_X_CV_NACK
        #endif
        #if defined(RCDEB)
        Debug.println(" RC ERROR");
        #endif
      }
    }
  }
}

//--------------------------------------------------------------
//Start Reading Data!
void RailComStart(void) {
  while(RCSerial.available()){RCSerial.read();}  //remove all rubish data
  RailComGetCV = true;  //start reading RailCom Data
  RailComReadLastCV = 0xFF;  //Reset the last Value
  RailComCVTime = 0;  //Reset the cycle time
  RailComReadAdr = 0;
  RailComRead();  //Try to read data...
}

#endif  //End MEGA Serial

//--------------------------------------------------------------
#endif
