/*
 * Copyright (c) 2019 Clementine Computing LLC.
 *
 * This file is part of PopuFare.
 *
 * PopuFare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PopuFare 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#ifndef _PASSDB_H
#define _PASSDB_H

#include <stdio.h>
#include <stdlib.h>

#include "../common/common_defs.h"
#include "ruleparam_db.h"
#include "byte_access.h"

typedef unsigned long long seq_t;
typedef unsigned long long logical_card_id_t;

//#define PASSDB_CONSISTENCY_CHECK
#define FN_SZ 1024

//#define RIDER_ONE_CRED_SIZE 27
//#define RIDER_TWO_CRED_SIZE ( sizeof(seq_t) + sizeof(logical_card_id_t) + 
// sizeof(unsigned char) + sizeof(unsigned long long) + 
// sizeof(unsigned char) + sizeof (unsigned long) + sizeof(unsigned long) +
// sizeof(unsigned short int) )

// This record is only used for transport
// of information.  On disk storage needs to be 
// done through the helper functions.
//
typedef struct rider_record_slim_two_cred_struct
{
    seq_t               seq;
    logical_card_id_t   id;


    unsigned long long  magstripe;

    unsigned long       rfid_site;
    unsigned long       rfid_val;

    unsigned short int  rule_param_bucket_id;
    unsigned char       rfid_code;
    unsigned char       magstripe_code;

} rider_record_slim_two_cred;

//#define RIDER_TWO_CRED_SIZE 36
//#define RIDER_ONE_CRED_SIZE ( sizeof(seq_t) + sizeof(logical_card_id_t) +
// sizeof(unsigned char) + sizeof(unsigned long long) + 
// sizeof (unsigned short int) )


// This record is only used for transport
// of information.  On disk storage needs to be 
// done through the helper functions.
//
typedef struct rider_record_slim_one_cred
{
    seq_t               seq;
    logical_card_id_t   id;

    unsigned long long  credential;

    unsigned short int  rule_param_bucket_id;
    unsigned char       code;

} rider_record_slim_one_cred;


//THIS STRUCTURE MUST BE A POWER OF 2 BYTES IN SIZE
//NOTE: this was the old entry format for the old database.
// rider_records are still used, but serve only as transport
// structures to hold information and for the 'spillover'
// records.  The main on-disk storage now is the rider one
// cred and rider two cred structures above, which are not
// powers of two in size.  
// This rider_record struct is also used to transport
// information about the card and provide a consistent
// interface to the rest of the ecosystem.
//
typedef struct rider_record_struct
{
    seq_t seq;				//sequence number
    logical_card_id_t  id;			//rider ID
      
    char magstripe_value[CREDENTIAL_LEN];
    char rfid_value[CREDENTIAL_LEN];
    
    char rule_name[RULENAME_LEN];
    char rule_param[PARAM_LEN];    

} rider_record;


typedef struct rule_pass_param_bucket_struct
{

    unsigned int rule_param_bucket_id;

    char rule_name[RULENAME_LEN];
    char rule_param[PARAM_LEN];    

} rule_pass_param_bucket;


// 0              <= idx < INDEX_MIDPOINT    -> single credential (either rfid or mag only)
// INDEX_MIDPOINT <= idx < 2*INDEX_MIDPOINT  -> double credential (both rfid + mag)
//
// idx >= 2*INDEX_MIDPOINT in spillover
//
// Each 'bank' is a block of X one credential, two credential or spillover packed
// structures.  X is read from the passdb.conf file (see passdb_slim_config.[ch]).
//
typedef struct rider_node_struct
{
  int idx;
  struct rider_node_struct *next;

} rider_node;

typedef struct passdb_slim_context_struct
{
  void **rider_one_cred_bank;
  void **rider_two_cred_bank;
  void **rider_spillover_bank;

  // Helper vairables to keep count of number of one and two credit
  // credentials in all of the banks.
  //

  // heap of active and free rider indexes 
  // (indexed into rider_one_cred and rider_two_cred)
  // simple linear linked lists
  //
  rider_node *activelist;

  rider_node *freelist_one_cred;
  rider_node *freelist_two_cred;
  rider_node *freelist_spillover;

  char **one_cred_db_bank_fn;
  char **two_cred_db_bank_fn;
  char **spillover_db_bank_fn;

  // current number of banks for each database type
  //
  int n_one_cred;
  int n_two_cred;
  int n_spillover;

  int n_one_cred_bank;
  int n_two_cred_bank;
  int n_spillover_bank;

  int n_one_cred_bank_size;
  int n_two_cred_bank_size;
  int n_spillover_bank_size;

  int n_one_cred_max_bank;
  int n_two_cred_max_bank;
  int n_spillover_max_bank;

  int hash_modulus;

  // hashes of logical_card_id, mag and rf
  // for quick lookup
  //
  rider_node **logical_card_id_hash;
  rider_node **rider_mag_hash;
  rider_node **rider_rf_hash;

  seq_t seq;

  // THESE ARE USED IF MMAP IS DETERMINED TO BE BROKEN...
  // it's broken...
  int mmap_broken;

  int num_free;
  int num_active;

  int *passes_one_cred_bank_fd;
  int *passes_two_cred_bank_fd;
  int *passes_spillover_bank_fd;

  size_t rider_one_file_size;
  size_t rider_two_file_size;
  size_t rider_spillover_file_size;

  size_t rider_one_file_bank_size;
  size_t rider_two_file_bank_size;
  size_t rider_spillover_file_bank_size;

  char diagnostic_str[128];

  ruleparam_db_ctx *ruleparam_db;
  char *ruleparam_db_fn;

  char *one_cred_db_base_fn;
  char *two_cred_db_base_fn;
  char *spillover_db_base_fn;

  char *db_fn_suffix;

  float high_watermark_threshold;
  float low_watermark_threshold;

} passdb_slim_context;


typedef struct passdb_slim_config_t {

  int hash_modulus;

  int n_one_cred_bank;
  int n_two_cred_bank;
  int n_spillover_bank;

  int n_one_cred_bank_size;
  int n_two_cred_bank_size;
  int n_spillover_bank_size;

  int n_one_cred_max_bank;
  int n_two_cred_max_bank;
  int n_spillover_max_bank;

  char *one_cred_db_base_fn;
  char *two_cred_db_base_fn;
  char *spillover_db_base_fn;

  char *db_fn_suffix;

  char *ruleparam_db_fn;

  size_t rider_one_file_bank_size;
  size_t rider_two_file_bank_size;
  size_t rider_spillover_file_bank_size;

  float high_watermark_threshold;
  float low_watermark_threshold;

} passdb_slim_config;

//--------------------------

int format_new_passdb( char *, int, long );
int format_new_passdbs();


int attach_to_passdb(passdb_slim_context *ctx);
int detach_from_passdb(passdb_slim_context *ctx);

int delete_rider(passdb_slim_context *ctx, rider_record *rec, int sync);
int update_rider(passdb_slim_context *ctx, rider_record *rec, int sync);

void sync_all_riders(passdb_slim_context *ctx);

void dump_hashes(passdb_slim_context *ctx);

int find_mag_in_hash(passdb_slim_context *ctx, char *mag);
int find_rf_in_hash(passdb_slim_context *ctx, char *rfid);

void populate_one_cred_rider_record( rider_record_slim_one_cred *, int, void *);
void populate_two_cred_rider_record( rider_record_slim_two_cred *, int, void *);
void populate_spillover_rider_record( rider_record *, int, void *);

int make_rider_record_from_rider_one_cred( passdb_slim_context *ctx, rider_record *rr, rider_record_slim_one_cred *rr1 );
int make_rider_record_from_rider_two_cred( passdb_slim_context *ctx, rider_record *rr, rider_record_slim_two_cred *rr2 );
int make_rider_record_from_rider_spillover( passdb_slim_context *, rider_record *, rider_record *);
void make_rider_record( passdb_slim_context *ctx, rider_record *rr, int idx );

int passdb_slim_get_cred_bank_and_pos( passdb_slim_context *ctx, int *bank, int *pos, int idx );

int passdb_slim_consistency_check( passdb_slim_context *ctx );

// config file functions 
//
void passdb_slim_copy_config( passdb_slim_config *, passdb_slim_context *);
int save_config( passdb_slim_config *, char *);
int read_config( passdb_slim_config *, char *);
int init_context_from_config( passdb_slim_context *, char *);
  
int make_default_config( char *);


// DEBUGGING
//
void passdb_slim_dump( passdb_slim_context * );



#endif

