#include "nfc.h"
#include <cstdio>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"

namespace esphome {
namespace nfc {

static const char *const TAG = "nfc";

char *format_uid_to(char *buffer, std::span<const uint8_t> uid) {
  return format_hex_pretty_to(buffer, FORMAT_UID_BUFFER_SIZE, uid.data(), uid.size(), '-');
}

char *format_bytes_to(char *buffer, std::span<const uint8_t> bytes) {
  return format_hex_pretty_to(buffer, FORMAT_BYTES_BUFFER_SIZE, bytes.data(), bytes.size(), ' ');
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// Deprecated wrappers intentionally use heap-allocating version for backward compatibility
std::string format_uid(std::span<const uint8_t> uid) {
  return format_hex_pretty(uid.data(), uid.size(), '-', false);  // NOLINT
}
std::string format_bytes(std::span<const uint8_t> bytes) {
  return format_hex_pretty(bytes.data(), bytes.size(), ' ', false);  // NOLINT
}
#pragma GCC diagnostic pop

uint8_t guess_tag_type(uint8_t uid_length) {
  if (uid_length == 4) {
    return TAG_TYPE_MIFARE_CLASSIC;
  } else {
    return TAG_TYPE_2;
  }
}

int8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data) {
  for (uint8_t i = 0; i < MIFARE_CLASSIC_BLOCK_SIZE; i++) {
    if (data[i] == 0x00) {
      // Do nothing, skip
    } else if (data[i] == 0x03) {
      return i;
    } else {
      return -2;
    }
  }
  return -1;
}

bool decode_mifare_classic_tlv(std::vector<uint8_t> &data, uint32_t &message_length, uint8_t &message_start_index) {
  if (data.size() < MIFARE_CLASSIC_BLOCK_SIZE) {
    ESP_LOGE(TAG, "Error, data too short for NDEF detection.");
    return false;
  }
  auto i = get_mifare_classic_ndef_start_index(data);
  if (i < 0 || data[i] != 0x03) {
    ESP_LOGE(TAG, "Error, Can't decode message length.");
    return false;
  }
  uint8_t idx = static_cast<uint8_t>(i);
  if (idx + 4 <= data.size() && data[idx + 1] == 0xFF) {
    message_length = ((0xFF & data[idx + 2]) << 8) | (0xFF & data[idx + 3]);
    message_start_index = idx + MIFARE_CLASSIC_LONG_TLV_SIZE;
  } else if (idx + 2 <= data.size()) {
    message_length = data[idx + 1];
    message_start_index = idx + MIFARE_CLASSIC_SHORT_TLV_SIZE;
  } else {
    ESP_LOGE(TAG, "Error, TLV data too short.");
    return false;
  }
  return true;
}

uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length) {
  uint32_t buffer_size = message_length + 2 + 1;
  if (buffer_size % MIFARE_ULTRALIGHT_READ_SIZE != 0)
    buffer_size = ((buffer_size / MIFARE_ULTRALIGHT_READ_SIZE) + 1) * MIFARE_ULTRALIGHT_READ_SIZE;
  return buffer_size;
}

uint32_t get_mifare_classic_buffer_size(uint32_t message_length) {
  uint32_t buffer_size = message_length;
  if (message_length < 255) {
    buffer_size += MIFARE_CLASSIC_SHORT_TLV_SIZE + 1;
  } else {
    buffer_size += MIFARE_CLASSIC_LONG_TLV_SIZE + 1;
  }
  if (buffer_size % MIFARE_CLASSIC_BLOCK_SIZE != 0) {
    buffer_size = ((buffer_size / MIFARE_CLASSIC_BLOCK_SIZE) + 1) * MIFARE_CLASSIC_BLOCK_SIZE;
  }
  return buffer_size;
}

bool mifare_classic_is_first_block(uint8_t block_num) {
  if (block_num < MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * MIFARE_CLASSIC_16BLOCK_SECT_START) {
    return (block_num % MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW == 0);
  } else {
    return (block_num % MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH == 0);
  }
}

bool mifare_classic_is_trailer_block(uint8_t block_num) {
  if (block_num < MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * MIFARE_CLASSIC_16BLOCK_SECT_START) {
    return ((block_num + 1) % MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW == 0);
  } else {
    return ((block_num + 1) % MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH == 0);
  }
}

}  // namespace nfc
}  // namespace esphome
