#ifndef __PLINK2_BITMAP_H__
#define __PLINK2_BITMAP_H__

// This library is part of PLINK 2.00, copyright (C) 2005-2021 Shaun Purcell,
// Christopher Chang.
//
// This library is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library.  If not, see <http://www.gnu.org/licenses/>.

// Compressed-bitmap-file support.  Similar to, but much simpler than, .pgen.
//
// Header format:
// - Two initial magic bytes, 0x6c 0x1c.
// - Number of rows, then number of columns, both as little-endian uint32s.
// - Number of bytes (in 1..4) used for each row-record-byte-length.
// - .pgen-style row block offsets, all but the last block have 2^16 rows.
// - .pgen-style alternation of 2^16 row record types (2 bits each) and row
//   record byte-lengths.
// The row record types are as follows:
//   0 = Direct storage.
//   1 = Differences-from-earlier-row encoding.  The last row without this type
//       of encoding is the base.  (There is of course no need for an analogue
//       of the "raregeno" field here, or in the last two record types.)
//   2 = Simple difflist, base value is 0.
//   3 = Simple difflist, base value is 1.

#include "plink2_bits.h"

#ifdef __cplusplus
namespace plink2 {
#endif

typedef struct BitmapReaderStruct {
  // ----- Header information, constant after initialization -----
  uint32_t row_ct;
  uint32_t col_ct;

  // size (row_ct + 1) so that the number of bytes of (zero-based) row n is
  // row_fpos[n+1] - row_fpos[n].
  // --pmerge can *almost* use a sequential reader, which would let us get away
  // with a much smaller memory allocation here.  Unfortunately, same-position
  // variants may need to be reordered by ID, and that makes it simpler to
  // stick to a random-access API with this entire array preloaded.
  uint64_t* row_fpos;

  unsigned char* rrtypes;

  // ----- Mutable state -----
  // If we don't fseek, what's the next row we'd read?
  uint32_t fp_ridx;

  FILE* ff;
  unsigned char* fread_buf;

  uint32_t ldbase_ridx;

  uint32_t ldbase_difflist_len;

  uintptr_t* ldbase_raw_bitvec;
  uintptr_t* ldbase_bitvec;
  uint32_t* ldbase_difflist_col_ids;

  // might want workspace and/or ldbase_raw_bitvec popcount
} BitmapReader;

void PreinitBitmapReader(BitmapReader* brp);

typedef struct BitmapWriterStruct {
  uint32_t row_ct;
  uint32_t col_ct;

  uint64_t* rblock_fpos;
  unsigned char* rrec_len_buf;
  uintptr_t* rrtype_buf;

  uint32_t ldbase_set_bit_ct;

  // should match ftello() return value
  uint64_t rblock_fpos_offset;

  // this must hold col_ct entries
  uintptr_t* ldbase_bitvec;

  // this must hold 2 * (col_ct / kPglMaxDifflistLenDivisor) entries
  uint32_t* ldbase_difflist_col_ids;  // 1 extra entry, == col_ct

  unsigned char* fwrite_buf;
  unsigned char* fwrite_bufp;

  uint32_t ldbase_difflist_len;

  uintptr_t rrec_len_byte_ct;

  uint32_t ridx;

  FILE* ff;
} BitmapWriter;

void PreinitBitmapWriter(BitmapWriter* bwp);

PglErr BitmapReaderInitPhase1(const char* fname, BitmapReader* brp, uintptr_t* br_alloc_cacheline_ct_ptr, char* errstr_buf);

BoolErr CleanupBitmapReader(BitmapReader* brp, PglErr* reterrp);

BoolErr CleanupBitmapWriter(BitmapWriter* bwp, PglErr* reterrp);

#ifdef __cplusplus
}  // namespace plink2
#endif

#endif  // __PLINK2_BITMAP_H__
