// File: crn_prefix_coding.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once

namespace crnlib {
namespace prefix_coding {
const uint cMaxExpectedCodeSize = 16;
const uint cMaxSupportedSyms = 8192;
const uint cMaxTableBits = 11;

bool limit_max_code_size(uint num_syms, uint8* pCodesizes, uint max_code_size);

bool generate_codes(uint num_syms, const uint8* pCodesizes, uint16* pCodes);

class decoder_tables {
 public:
  inline decoder_tables()
      : m_table_shift(0), m_table_max_code(0), m_decode_start_code_size(0), m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL) {
  }

  inline decoder_tables(const decoder_tables& other)
      : m_table_shift(0), m_table_max_code(0), m_decode_start_code_size(0), m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL) {
    *this = other;
  }

  decoder_tables& operator=(const decoder_tables& other) {
    if (this == &other)
      return *this;

    clear();

    memcpy(this, &other, sizeof(*this));

    if (other.m_lookup) {
      m_lookup = crnlib_new_array<uint32>(m_cur_lookup_size);
      memcpy(m_lookup, other.m_lookup, sizeof(m_lookup[0]) * m_cur_lookup_size);
    }

    if (other.m_sorted_symbol_order) {
      m_sorted_symbol_order = crnlib_new_array<uint16>(m_cur_sorted_symbol_order_size);
      memcpy(m_sorted_symbol_order, other.m_sorted_symbol_order, sizeof(m_sorted_symbol_order[0]) * m_cur_sorted_symbol_order_size);
    }

    return *this;
  }

  inline void clear() {
    if (m_lookup) {
      crnlib_delete_array(m_lookup);
      m_lookup = 0;
      m_cur_lookup_size = 0;
    }

    if (m_sorted_symbol_order) {
      crnlib_delete_array(m_sorted_symbol_order);
      m_sorted_symbol_order = NULL;
      m_cur_sorted_symbol_order_size = 0;
    }
  }

  inline ~decoder_tables() {
    if (m_lookup)
      crnlib_delete_array(m_lookup);

    if (m_sorted_symbol_order)
      crnlib_delete_array(m_sorted_symbol_order);
  }

  // DO NOT use any complex classes here - it is bitwise copied.

  uint m_num_syms;
  uint m_total_used_syms;
  uint m_table_bits;
  uint m_table_shift;
  uint m_table_max_code;
  uint m_decode_start_code_size;

  uint8 m_min_code_size;
  uint8 m_max_code_size;

  uint m_max_codes[cMaxExpectedCodeSize + 1];
  int m_val_ptrs[cMaxExpectedCodeSize + 1];

  uint m_cur_lookup_size;
  uint32* m_lookup;

  uint m_cur_sorted_symbol_order_size;
  uint16* m_sorted_symbol_order;

  inline uint get_unshifted_max_code(uint len) const {
    CRNLIB_ASSERT((len >= 1) && (len <= cMaxExpectedCodeSize));
    uint k = m_max_codes[len - 1];
    if (!k)
      return UINT_MAX;
    return (k - 1) >> (16 - len);
  }
};

bool generate_decoder_tables(uint num_syms, const uint8* pCodesizes, decoder_tables* pTables, uint table_bits);

}  // namespace prefix_coding

}  // namespace crnlib
