#pragma once

#include <stdbool.h>
#include <stdint.h>


#define blky_clamp(x, a, b) ((x) < (a)? (a): (x) > (b)? (b): (x))
#define blky_abs(x) ((x) < 0? -(x): (x))


/* ---- Sensor ----
 * calculates correl from samples sequencially */

typedef struct blky_sensor_t {
  double sum, avg, var;
  uint64_t time;

  double cov;
  double correl;
  double prev_correl;

# define BLKY_SENSOR_MAX_DUR 64
  float values[BLKY_SENSOR_MAX_DUR];
} blky_sensor_t;

void
blky_sensor_feed(
    blky_sensor_t*, const float* v, uint64_t n);

void
blky_sensor_drop(
    blky_sensor_t*, uint64_t until);


/* ---- Block ----
 * calculates probability representing how likely the block is a feature,
 * by dealing with multiple sensors */

double  /* 0~1 probability */
blky_block_estimate(
    const blky_sensor_t* sensors, uint64_t n,
    double max_var, double min_avg);


/* ---- Extractor ----
 * extracts all features from a sequence of multiple frames */

typedef struct blky_extractor_t {
  // must be filled before init()
  uint32_t block_num_x;
  uint32_t block_num_y;
  uint32_t sensor_num_block_x;
  uint32_t sensor_num_block_y;
  uint32_t samples_per_pix;
  uint32_t pix_stride;
  uint32_t utime;

  double correl_max_var;
  double correl_min_avg;

  // immutable internal params
  uint32_t block_num;
  uint32_t sensor_num_block;
  uint32_t sensor_num_whole;

  double block_w;
  double block_h;
  double sensor_interval_x;
  double sensor_interval_y;

  // mutable internal state
  uint64_t time;

  // heap objects
  uint8_t*       mem;
  blky_sensor_t* sensors;
  double*        probs;
} blky_extractor_t;

typedef struct blky_extractor_feat_t {
  uint32_t block;
  uint64_t begin;
  double   prob;
} blky_extractor_feat_t;

void
blky_extractor_init(
    blky_extractor_t* ex);

void
blky_extractor_deinit(
    blky_extractor_t* ex);

bool
blky_extractor_feed(
    blky_extractor_t* ex,
    const uint8_t* img, uint32_t w, uint32_t h, const double verts[8]);


/* ---- Pathfinder ---- */
typedef struct blky_pathfinder_step_t {
  struct blky_pathfinder_step_t* prev;
  uint32_t indices[1];
} blky_pathfinder_step_t;

typedef struct blky_pathfinder_t {
  // must be filled before init()
  uint32_t block_num;
  uint32_t feat_bits;
  uint64_t seed;

  // internal state
  blky_pathfinder_step_t* step_last;

  uint64_t steps;
  uint64_t step_bytes;

  double* probs;
  double* probs_prev;
} blky_pathfinder_t;

void
blky_pathfinder_init(
    blky_pathfinder_t* pf);

void
blky_pathfinder_deinit(
    blky_pathfinder_t* pf);

void
blky_pathfinder_feed(
    blky_pathfinder_t* pf,
    const double*      probs);


/* ---- Encoder ----
 * converts byte to feature */

typedef struct blky_encoder_t {
  uint32_t block_num;
  uint32_t block_index;
  uint8_t  feat_bits;
  uint64_t seed;

  uint64_t count;
  uint32_t scrap;
  uint8_t  scrap_bits;
} blky_encoder_t;

void
blky_encoder_feed(
    blky_encoder_t* enc,
    uint8_t         data);

bool
blky_encoder_pop(
    blky_encoder_t* enc,
    uint32_t*       feat,
    bool            force);


/* ---- Decoder ----
 * converts block indices to byte */

typedef struct blky_decoder_t {
  uint32_t block_num;
  uint8_t  feat_bits;
  uint64_t seed;

  uint64_t count;
  uint32_t block_index;
  uint32_t scrap;
  uint8_t  scrap_bits;
} blky_decoder_t;

bool
blky_decoder_feed(
    blky_decoder_t* de,
    uint32_t        block_index);

bool
blky_decoder_pop(
    blky_decoder_t* de,
    uint8_t*        b,
    bool            force);


/* ---- Image utility ---- */
void blky_image_convert_to_normalized_coord(
    const double verts[8], double* x, double* y);

uint64_t blky_image_offset(
    uint32_t w, uint32_t h, const double verts[8], double x, double y);


/* ---- numeric utility ---- */
static inline uint64_t blky_numeric_xorshift64(uint64_t x) {
  x ^= x << 13;
  x ^= x >> 7;
  x ^= x << 17;
  return x;
}
static inline uint32_t blky_numeric_hop(uint32_t prev, uint32_t offset, uint64_t seed) {
  seed = (seed^blky_numeric_xorshift64(prev+seed)) + (offset << 4);
  return (uint32_t) ((seed & 0xFFFFFFFF) ^ (seed >> 32));
}