/**
   * Called if a SYN packet was received
   *
   * @throws IOException
   */
  protected void onEBusSyncReceived() throws IOException {

    if (inputBuffer.position() == 1 && inputBuffer.get(0) == EBusTelegram.SYN) {
      if (lockCounter > 0) lockCounter--;
      logger.trace("Auto-SYN byte received");

      // send a telegram from queue if available
      send(false);

    } else if (inputBuffer.position() == 2 && inputBuffer.get(0) == EBusTelegram.SYN) {
      logger.warn("Collision on eBus detected (SYN DATA SYNC Sequence) ...");
      blockNextSend = true;

      // send a telegram from queue if available
      send(false);

    } else if (inputBuffer.position() < 5) {
      if (lockCounter > 0) lockCounter--;
      logger.trace("Telegram to small, skip!");

      // send a telegram from queue if available
      send(false);

    } else {
      if (lockCounter > 0) lockCounter--;
      byte[] receivedTelegram = Arrays.copyOf(inputBuffer.array(), inputBuffer.position());

      // send a telegram from queue if available, time critical!
      send(false);

      // After senden we can process the last received telegram
      final EBusTelegram telegram = EBusUtils.processEBusData(receivedTelegram);
      if (telegram != null) {

        // execute event
        onEBusTelegramReceived(telegram);

      } else {
        logger.debug("Received telegram was invalid, skip!");
      }
    }

    // reset receive buffer
    inputBuffer.clear();
  }
  /**
   * Add a byte array to send queue.
   *
   * @param data
   * @return
   */
  public boolean send(byte[] data) {

    if (data == null || data.length == 0) {
      logger.debug("Send data is empty, skip");
      return false;
    }

    byte crc = 0;
    for (int i = 0; i < data.length - 1; i++) {
      byte b = data[i];
      crc = EBusUtils.crc8_tab(b, crc);
    }

    // replace crc with calculated value
    data[data.length - 1] = crc;

    return outputQueue.add(data);
  }
  /**
   * Internal send function. Send and read to detect byte collisions.
   *
   * @param secondTry
   * @throws IOException
   */
  protected void send(boolean secondTry) throws IOException {

    // blocked for this send slot because a collision
    if (blockNextSend) {
      logger.trace("Sender was blocked for this SYN ...");
      blockNextSend = false;
      return;
    }

    // currently no data to send
    if (outputQueue.isEmpty()) {
      logger.trace("Send buffer is empty, nothing to send...");
      return;
    }

    // counter not zero, it's not allowed to send yet
    if (lockCounter > 0) {
      logger.trace("No access to ebus because the lock counter ...");
      return;
    }

    byte[] dataOutputBuffer = outputQueue.peek();
    logger.debug(
        "EBusSerialPortEvent.send() data: {}", EBusUtils.toHexDumpString(dataOutputBuffer));

    // clear first
    inputBuffer.clear();

    boolean isMasterAddr = EBusUtils.isMasterAddress(dataOutputBuffer[1]);

    // send command
    for (int i = 0; i < dataOutputBuffer.length; i++) {
      byte b = dataOutputBuffer[i];
      outputStream.write(b);

      // directly read the current wrote byte from bus
      int read = inputStream.read();
      if (read != -1) {

        byte r = (byte) (read & 0xFF);
        inputBuffer.put(r);

        // do arbitation on on first byte only
        if (i == 0 && b != r) {

          // written and read byte not identical, that's
          // a collision
          logger.warn("eBus collision detected!");

          // last send try was a collision
          if (lastSendCollisionDetected) {
            logger.warn("A second collision occured!");
            resetSend();
            return;
          }
          // priority class identical
          else if ((byte) (r & 0x0F) == (byte) (b & 0x0F)) {
            logger.trace("Priority class match, restart after next SYN ...");
            lastSendCollisionDetected = true;

          } else {
            logger.trace("Priority class doesn't match, blocked for next SYN ...");
            blockNextSend = true;
          }

          // stop after a collision
          return;
        }
      }
    }

    // sending master data finish

    // reset global variables
    lastSendCollisionDetected = false;
    blockNextSend = false;

    // if this telegram a broadcast?
    if (dataOutputBuffer[1] == (byte) 0xFE) {
      logger.warn("Broadcast send ..............");

      // sende master sync
      outputStream.write(EBusTelegram.SYN);
      inputBuffer.put(EBusTelegram.SYN);

    } else {

      int read = inputStream.read();
      if (read != -1) {
        byte ack = (byte) (read & 0xFF);
        inputBuffer.put(ack);

        if (ack == EBusTelegram.ACK_OK) {

          // if the telegram is a slave telegram we will
          // get data from slave
          if (!isMasterAddr) {

            // len of answer
            byte nn2 = (byte) (inputStream.read() & 0xFF);
            inputBuffer.put(nn2);

            byte crc = EBusUtils.crc8_tab(nn2, (byte) 0);

            if (nn2 > 16) {
              logger.warn("slave data to lang, invalid!");

              // resend telegram (max. once)
              if (!resend(secondTry)) return;
            }

            // read slave data, be aware of 0x0A bytes
            while (nn2 > 0) {
              byte d = (byte) (inputStream.read() & 0xFF);
              inputBuffer.put(d);
              crc = EBusUtils.crc8_tab(d, crc);

              if (d != (byte) 0xA) {
                nn2--;
              }
            }

            // read slave crc
            byte crc2 = (byte) (inputStream.read() & 0xFF);
            inputBuffer.put(crc2);

            // check slave crc
            if (crc2 != crc) {
              logger.warn("Slave CRC wrong, resend!");

              // Resend telegram (max. once)
              if (!resend(secondTry)) return;
            }

            // sende master sync
            outputStream.write(EBusTelegram.ACK_OK);
            inputBuffer.put(EBusTelegram.ACK_OK);
          } // isMasterAddr check

          // send SYN byte
          outputStream.write(EBusTelegram.SYN);
          inputBuffer.put(EBusTelegram.SYN);

        } else if (ack == EBusTelegram.ACK_FAIL) {

          // clear uncompleted telegram
          inputBuffer.clear();

          // resend telegram (max. once)
          if (!resend(secondTry)) return;

        } else if (ack == EBusTelegram.SYN) {
          logger.warn("No answer from slave, skip ...");

          // clear uncompleted telegram or it will result
          // in uncomplete but valid telegram!
          inputBuffer.clear();

          resetSend();
          return;

        } else {
          // Wow, wrong answer, and now?
          logger.warn("Received wrong telegram: {}", EBusUtils.toHexDumpString(inputBuffer));

          // clear uncompleted telegram
          inputBuffer.clear();

          // resend telegram (max. once)
          if (!resend(secondTry)) return;
        }
      }
    }

    // after send process the received telegram
    byte[] buffer = Arrays.copyOf(inputBuffer.array(), inputBuffer.position());
    final EBusTelegram telegram = EBusUtils.processEBusData(buffer);
    if (telegram != null) {
      onEBusTelegramReceived(telegram);

    } else {
      logger.debug("Received telegram was invalid, skip!");
    }

    // reset send module
    resetSend();
  }