#include "compressor.h"

static constexpr uint8_t RLE_MARKER = 0xFF;

bool compressBuffer(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_max, size_t &out_len) {
  out_len = 0;
  if (!in || !out) {
    return false;
  }

  size_t i = 0;
  while (i < in_len) {
    uint8_t value = in[i];
    size_t run = 1;
    while (i + run < in_len && in[i + run] == value && run < 255) {
      run++;
    }

    if (value == RLE_MARKER || run >= 4) {
      if (out_len + 3 > out_max) {
        return false;
      }
      out[out_len++] = RLE_MARKER;
      out[out_len++] = static_cast<uint8_t>(run);
      out[out_len++] = value;
    } else {
      if (out_len + run > out_max) {
        return false;
      }
      for (size_t j = 0; j < run; ++j) {
        out[out_len++] = value;
      }
    }

    i += run;
  }

  return true;
}

bool decompressBuffer(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_max, size_t &out_len) {
  out_len = 0;
  if (!in || !out) {
    return false;
  }

  size_t i = 0;
  while (i < in_len) {
    uint8_t value = in[i++];
    if (value == RLE_MARKER) {
      if (i + 1 >= in_len) {
        return false;
      }
      uint8_t run = in[i++];
      uint8_t data = in[i++];
      if (out_len + run > out_max) {
        return false;
      }
      for (uint8_t j = 0; j < run; ++j) {
        out[out_len++] = data;
      }
    } else {
      if (out_len + 1 > out_max) {
        return false;
      }
      out[out_len++] = value;
    }
  }

  return true;
}
