#include "lua_rnc.h"

#include "config.h"

#include <array>

#include "rnc.h"
#include "th_lua.h"

//! Provides lua function to decompress RNC data
/*!
    @param L Lua state where the function is called from.  In lua a call
        to this function has one parameter which is the RNC compressed data.
        The return value is the decompressed data.
*/

namespace {

int l_decompress(lua_State* L) {
  size_t inlen;
  const uint8_t* in =
      reinterpret_cast<const uint8_t*>(luaL_checklstring(L, 1, &inlen));

  // Verify that the data contains an RNC signature, and that the input
  // size matches the size specified in the data header.
  if (inlen < rnc_header_size || inlen != rnc_input_size(in)) {
    lua_pushnil(L);
    lua_pushliteral(L, "Input is not RNC compressed data");
    return 2;
  }
  size_t outlen = rnc_output_size(in);

  // Allocate scratch area as Lua userdata so that if something horrible
  // happens, it'll be cleaned up by Lua's GC. Remember that most Lua API
  // calls can throw errors, so they all have to be wrapped with code to
  // detect errors and free the buffer if said buffer was not managed by Lua.
  void* outbuf = lua_newuserdata(L, outlen);

  lua_pushnil(L);
  switch (rnc_unpack(in, (uint8_t*)outbuf)) {
    case rnc_status::ok:
      lua_pushlstring(L, (const char*)outbuf, outlen);
      return 1;

    case rnc_status::file_is_not_rnc:
      lua_pushliteral(L, "Input is not RNC compressed data");
      break;

    case rnc_status::huf_decode_error:
      lua_pushliteral(L, "Invalid Huffman coding");
      break;

    case rnc_status::file_size_mismatch:
      lua_pushliteral(L, "Size mismatch");
      break;

    case rnc_status::packed_crc_error:
      lua_pushliteral(L, "Incorrect packed CRC");
      break;

    case rnc_status::unpacked_crc_error:
      lua_pushliteral(L, "Incorrect unpacked CRC");
      break;

    default:
      lua_pushliteral(L, "Unknown error decompressing RNC data");
      break;
  }
  return 2;
}

constexpr std::array<luaL_Reg, 2> rnclib{
    {{"decompress", l_decompress}, {nullptr, nullptr}}};

}  // namespace

int luaopen_rnc(lua_State* L) {
  luaT_register(L, "rnc", rnclib);
  return 1;
}
