/*
 * Copyright 2017-2021 vh
 * Copyright 2021-2023 kano
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option)
 * any later version.  See COPYING for more details.
 */

#include "math.h"
#include "miner.h"
#include "usbutils.h"
#include "klist.h"

#define JOB_MAX      0x7F
#define BUFFER_MAX   0xFF
#define SAMPLE_SIZE  0x78
#define MS_SECOND_1  1000

#define MS_SECOND_5  (MS_SECOND_1 * 5)
#define MS_SECOND_10 (MS_SECOND_1 * 10)
#define MS_SECOND_15 (MS_SECOND_1 * 15)
#define MS_SECOND_30 (MS_SECOND_1 * 30)
#define MS_MINUTE_1  (MS_SECOND_1 * 60)

#define MS_MINUTE_2  (MS_MINUTE_1 * 2)
#define MS_MINUTE_3  (MS_MINUTE_1 * 3)
#define MS_MINUTE_4  (MS_MINUTE_1 * 4)
#define MS_MINUTE_5  (MS_MINUTE_1 * 5)
#define MS_MINUTE_10 (MS_MINUTE_1 * 10)
#define MS_MINUTE_30 (MS_MINUTE_1 * 30)
#define MS_HOUR_1    (MS_MINUTE_1 * 60)

enum miner_state {
	MINER_INIT = 1,
	MINER_CHIP_COUNT,	// 2
	MINER_CHIP_COUNT_XX,	// 3
	MINER_CHIP_COUNT_OK,	// 4
	MINER_OPEN_CORE,	// 5
	MINER_OPEN_CORE_OK,	// 6
	MINER_MINING,		// 7
	MINER_MINING_DUPS,	// 8
	MINER_SHUTDOWN,	// 9
	MINER_SHUTDOWN_OK,	// 10
	MINER_RESET		// 11
};

enum miner_asic {
	BM1384 = 1,
	BM1387,
	BM1397
};

enum plateau_type {
	PT_NONONCE = 1,
	PT_FREQSET,
	PT_FREQNR,
	PT_DUPNONCE
};

enum micro_command {
	M1_GET_FAN    = (0x00 << 3),
	M1_GET_RPM    = (0x01 << 3),
	M1_GET_VIN    = (0x02 << 3),
	M1_GET_IIN    = (0x03 << 3),
	M1_GET_TEMP   = (0x04 << 3),
	M1_GET_VNODE0 = (0x05 << 3),

	M1_CLR_BEN    = (0x08 << 3),
	M1_SET_BEN    = (0x09 << 3),
	M1_CLR_LED    = (0x0A << 3),
	M1_SET_LED    = (0x0B << 3),
	M1_CLR_RST    = (0x0C << 3),
	M1_SET_RST    = (0x0D << 3),

	M2_SET_FAN    = (0x18 << 3),
	M2_SET_VCORE  = (0x1C << 3)
};

enum asic_state {
	ASIC_HEALTHY = 0,
	ASIC_HALFDEAD,
	ASIC_ALMOST_DEAD,
	ASIC_DEAD
};

// N.B. at 2TH/s R909 ticket 64 = ~1.2 nonces per chip per second
//  thus 20mins is only ~1000 nonces - so variance isn't very low
// time range of each value = 10 minutes
#define CHTIME 600
// number of ranges thus total 1hr
#define CHNUM 6

#define CHOFF(n) (((n) + CHNUM) % CHNUM)

// N.B. uses CLOCK_MONOTONIC
#define CHBASE(_t1) ((int)((_t1) / CHTIME))

#define CHCMP(_t1, _t2) (CHBASE(_t1) == CHBASE(_t2))

struct GEKKOCHIP
{
	// seconds time of [offset]
	time_t zerosec;
	// the position of [0]
	int offset;
	// number of nonces in each range
	int noncenum[CHNUM];
	// number of nonces in 0..last-1
	int noncesum;
	// last used offset 0 based
	int last;
};


struct ASIC_INFO {
	struct timeval last_nonce;              // Last time nonce was found
	float frequency;                        // Current frequency
	float frequency_set;                    // set_frequency
	bool frequency_updated;                 // Initiate check for new frequency
	uint32_t frequency_attempt;             // attempts of set_frequency
	uint32_t dups;                          // Duplicate nonce counter
	uint32_t dupsall;			// Total duplicate nonce counter
	enum asic_state state;
	enum asic_state last_state;
	struct timeval state_change_time;       // Device startup time
	struct timeval last_frequency_adjust;   // Last time of frequency adjust
	struct timeval last_frequency_ping;     // Last time of frequency ping
	struct timeval last_frequency_reply;    // Last time of frequency reply
	uint32_t prev_nonce;         // Last nonce found
	float fullscan_ms;           // Estimated time(ms) for full nonce range
	uint32_t fullscan_us;        // Estimated time(us) for full nonce range
	uint64_t hashrate;           // Estimated hashrate = cores x chips x frequency
	float frequency_reply;
	
	int nonces;
	struct GEKKOCHIP gc;	// running nonce buffer
};

struct COMPAC_NONCE
{
	int asic;
	unsigned char rx[BUFFER_MAX];
	size_t len;
	size_t prelen;
	struct timeval when;
};

#define DATA_NONCE(_item) ((struct COMPAC_NONCE *)(_item->data))
#define ALLOC_NLIST_ITEMS 256
#define LIMIT_NLIST_ITEMS 0

// BM1397 info->job_id offsets to check (when job_id is wrong)
static int cur_attempt[] = { 0, -4, -8, -12 };
#define CUR_ATTEMPT (sizeof(cur_attempt)/sizeof(int))

// macro to adjust frequency choices to be an integer multple of info->freq_base
#define FREQ_BASE(_f) (ceil((float)(_f) / info->freq_base) * info->freq_base)

// macro to add/subtract from the job_id but roll in the min...max range
#define JOB_ID_ROLL(_jid, _add, _info) \
	((_info)->min_job_id + (((_jid) + (_add) - (_info)->min_job_id) % \
		((_info)->max_job_id + 1 - (_info)->min_job_id)))

// convert chip to address for the 1397
#define CHIPPY1397(_inf, _chi) (((double)(_inf->chips) == 0) ? \
	(unsigned char)0 : \
	((unsigned char)(floor((double)0x100 / (double)(_inf->chips))) * (unsigned char)(_chi)))

// convert address to chip for the 1397
#define TOCHIPPY1397(_inf, _adr) (((double)(_inf->chips) == 0) ? \
	0 : (int)floor((double)(_adr) \
		/ floor((double)0x100 / (double)(_inf->chips))) )

// BM1397 registers
#define BM1397FREQ 0x08
#define BM1397TICKET 0x14

#define GHNUM (60*5)
#define GHOFF(n) (((n) + GHNUM) % GHNUM)
// a time jump without any nonces will reset the GEKKOHASH data
//  this would normally be a miner failure, so should reset anyway,
//  however under normal mining operation, using 10sec,
//   a 6GH/s asic will have this happen, on average, about once every 10 days
//   a 30GH/s asic is unlikely to have this happen in the life of the universe
#define GHLIMsec 10

// number of nonces that should give better than 80% accuracy
// CDF[ERlang] 400 0.8 = 9.0991e-06
#define GHNONCES 400

// a loss of this much hash rate will reduce requested freq and reset
#define GHREQUIRE 0.65

// number of nonces needed before using as the rolling hash rate
// N.B. 200Mhz ticket 16 GSF is around 2/sec
// also, 8 has high variance ... but resets shouldn't be common
// code adds 1 to this value since the first nonce isn't part of the H/s calc
#define GHNONCENEEDED 8

// running 5min nonce diff buffer (for GH/s)
// offset = current second, GHOFF(offset-1) = previous second
// GHOFF(offset-(GHNUM-1)) = GHOFF(offset+1) = oldest possible
// GHOFF(offset-last) = oldest used
// code is all linear except one loop that is almost always only
//  one interation or total max one interation per second elapsed real time
struct GEKKOHASH
{
	// seconds time of [offset]
	time_t zerosec;
	// the position of [0]
	int offset;
	// total diff in each second
	int64_t diff[GHNUM];
	// time of the first nonce in each second
	struct timeval firstt[GHNUM];
	// diff of first nonce in each second
	int64_t firstd[GHNUM];
	// time of the last nonce in each second
	struct timeval lastt[GHNUM];
	// number of nonces in each second
	int noncenum[GHNUM];
	// sum of diff[0..last-1]
	int64_t diffsum;
	// number of nonces in 0..last-1
	int noncesum;
	// last used offset 0 based
	int last;
};

#define JOBMIN 5
#define JOBOFF(n) (((n) + JOBMIN) % JOBMIN)
// a time jump without any work will reset the GEKKOJOB data
//  this would normally be all pool failure or power down due to heat
//  3 = 3 minutes so should never happen
#define JOBLIMn 3

// the arrays are minutes of data
// N.B. uses CLOCK_MONOTONIC
#define JOBTIME(_sec) ((int)((int)(_sec)/(int)60))

struct GEKKOJOB
{
	// JOBTIME of [offset]
	time_t zeromin;
	// time of last job added
	struct timeval lastjob;
	// the position of [0]
	int offset;
	// time of the first job in each
	struct timeval firstj[JOBMIN];
	// time of the last job in each
	struct timeval lastj[JOBMIN];
	// number of job items in each
	int jobnum[JOBMIN];
	// average ms between jobs
	double avgms[JOBMIN];
	// min ms
	double minms[JOBMIN];
	// max ms
	double maxms[JOBMIN];
	// number of jobs in 0..last-1
	int jobsnum;
	// last used offset 0 based
	int last;
};

struct COMPAC_INFO {

	enum sub_ident ident;		// Miner identity
	enum miner_state mining_state;	// Miner state
	enum miner_asic asic_type;	// ASIC Type
	struct thr_info *thr;		// Running Thread
	struct thr_info rthr;		// Listening Thread
	struct thr_info wthr;		// Miner Work Thread

	pthread_mutex_t lock;		// Mutex
	pthread_mutex_t wlock;		// Mutex Serialize Writes
	pthread_mutex_t rlock;		// Mutex Serialize Reads

	struct thr_info nthr;		// GSF Nonce Thread
	K_LIST *nlist;			// GSF Nonce list
	K_LIST *nstore;			// GSF Nonce store
	pthread_mutex_t nlock;		// GSF lock
	pthread_cond_t ncond;		// GSF wait
	uint64_t ntimeout;		// GSF number of cond timeouts
	uint64_t ntrigger;		// GSF number of cond tiggered

	float freq_mult;	     // frequency multiplier
	float freq_base;	     // frequency mod value
	float step_freq;	     // frequency step value
	float min_freq;              // Lowest frequency mine2 will tune down to
	int ramp_time;               // time to allow for initial frequency ramp
	float frequency;             // Chip Average Frequency
	float frequency_asic;        // Highest of current asics.
	float frequency_default;     // ASIC Frequency on RESET
	float frequency_requested;   // Requested Frequency
	float frequency_selected;    // Initial Requested Frequency
	float frequency_start;       // Starting Frequency
	float frequency_fail_high;   // Highest Frequency of Chip Failure
	float frequency_fail_low;    // Lowest Frequency of Chip Failure
	float frequency_computed;    // Highest hashrate seen as a frequency value
	float eff_gs;                // hash : expected hash
	float eff_tm;                // hash : expected hash
	float eff_li;                // hash : expected hash
	float eff_1m;                // hash : expected hash
	float eff_5m;                // hash : expected hash
	float eff_15;                // hash : expected hash
	float eff_wu;                // wu : expected wu
	float tune_up;               // Increase frequency when eff_gs is above value
	float tune_down;             // Decrease frequency when eff_gs is below value
	float freq_fail;	     // last freq set failure on BM1397
	float hr_scale;		     // scale adjustment for hashrate

	float micro_temp;            // Micro Reported Temp
	float wait_factor0;          // Base setting from opt value
	float wait_factor;           // Used to compute max_task_wait
	bool lock_freq;		     // When true disable all but safety,reset,shutdown and API freq changes
	int usb_prop;		     // Number of usec to wait after certain usb commands

	float fullscan_ms;           // Estimated time(ms) for full nonce range
	float task_ms;               // Avg time(ms) between task sent to device
	uint32_t fullscan_us;        // Estimated time(us) for full nonce range
	uint64_t hashrate;           // Estimated hashrate = cores x chips x frequency x hr_scale
	uint64_t busy_work;

	uint64_t task_hcn;           // Hash Count Number - max nonce iter.
	uint32_t prev_nonce;         // Last nonce found

	int failing;                 // Flag failing sticks
	int fail_count;              // Track failures = resets
	int frequency_fo;            // Frequency check token
	int frequency_of;            // Frequency check token
	int accepted;                // Nonces accepted
	int dups;                    // Duplicates found (for plateau code)
	int dupsall;                 // Duplicate nonce counter (total)
	int dupsreset;		     // Duplicates since reset
	int tracker;                 // Track code execution path
	int interface;               // USB interface
	int init_count;              // USB interface initialization counter
	int low_eff_resets;          // Count of low_eff resets
	int midstates;               // Number of midstates
	int nonceless;               // Tasks sent.  Resets when nonce is found.
	int nonces;                  // Nonces found
	int plateau_reset;           // Count plateau based resets
	int zero_check;              // Received nonces from zero work
	int vcore;                   // Core voltage
	int micro_found;             // Found a micro to communicate with

	bool can_boost;		     // true if boost is possible
	bool vmask;                  // Current pool's vmask
	bool boosted;                // Good nonce found for midstate2/3/4
	bool report;
	bool frequency_syncd;        // All asics share same frequency

	double wu;
	double wu_max;               // Max WU since last frequency change
	double rolling;

	uint32_t bauddiv;            // Baudrate divider
	uint32_t chips;              // Stores number of chips found
	uint32_t cores;              // Stores number of core per chp
	uint32_t difficulty;         // For computing hashrate
	float nonce_expect;	     // For PT_NONONCE
	float nonce_limit;	     // For PT_NONONCE
	uint32_t expected_chips;     // Number of chips for device
	uint64_t hashes;             // Hashes completed
	uint64_t xhashes;            // Hashes completed / 0xffffffffull
	uint32_t job_id;             // JobId incrementer
	int32_t log_wide;            // Extra output in widescreen mode
	uint32_t low_hash;           // Tracks of low hashrate
	uint32_t min_job_id;         // JobId start/rollover
	uint32_t add_job_id;         // JobId increment
	uint32_t max_job_id;         // JobId cap
	uint64_t max_task_wait;      // Micro seconds to wait before next task is sent
	uint32_t ramping;            // Ramping incrementer
	uint32_t rx_len;             // rx length
	uint32_t task_len;           // task length
	uint32_t ticket_mask;        // Used to reduce flashes per second
	uint32_t update_work;        // Notification of work update

	struct timeval start_time;              // Device startup time
	struct timeval monitor_time;            // Health check reference point
	struct timeval last_computed_increase;  // Last frequency computed change
	struct timeval last_scanhash;           // Last time inside scanhash loop
	struct timeval last_dup_time;           // Last time nonce dup detected was attempted
	struct timeval last_reset;              // Last time reset was triggered
	struct timeval last_task;               // Last time work was sent
	struct timeval last_nonce;              // Last time nonce was found
	struct timeval last_hwerror;            // Last time hw error was detected
	struct timeval last_fast_forward;       // Last time of ramp jump to peak
	struct timeval last_frequency_adjust;   // Last time of frequency adjust
	struct timeval last_frequency_ping;     // Last time of frequency poll
	struct timeval last_frequency_report;   // Last change of frequency report
	struct timeval last_frequency_invalid;  // Last change of frequency report anomaly
	struct timeval last_chain_inactive;     // Last sent chain inactive
	struct timeval last_low_eff_reset;      // Last time responded to low_eff condition
	struct timeval last_micro_ping;         // Last time of micro controller poll
	struct timeval last_write_error;        // Last usb write error message
	struct timeval last_wu_increase;        // Last wu_max change
	struct timeval last_pool_lost;          // Last time we lost pool
	struct timeval last_update_rates;       // Last time we called compac_update_rates()

	struct timeval first_task;
	uint64_t tasks;
	uint64_t cur_off[CUR_ATTEMPT];
	double work_usec_avg;
	uint64_t work_usec_num;

	double last_work_diff;			// Diff of last work sent
	struct timeval last_ticket_attempt;	// List attempt to set ticket
	int ticket_number;			// offset in ticket array
	int ticket_work;			// work sent since ticket set
	int64_t ticket_nonces;			// nonces since ticket set
	int64_t below_nonces;			// nonces too low since ticket set
	bool ticket_ok;				// ticket working ok
	bool ticket_got_low;			// nonce found close to but >= diff
	int ticket_failures;			// Must not exceed MAX_TICKET_CHECK
	struct ASIC_INFO asics[255];
	int64_t noncebyte[256];			// Count of nonces with the given byte[3] value
	bool nb2c_setup;			// BM1397 true = nb2chip is setup (false = all 0)
	uint16_t nb2chip[256];			// BM1397 map nonce byte to: chip that produced it
	bool active_work[JOB_MAX+1];            // Tag good and stale work
	struct work *work[JOB_MAX+1];           // Work ring buffer

	pthread_mutex_t ghlock;			// Mutex for all access to gh
	struct GEKKOHASH gh;			// running hash rate buffer
	float ghrequire;			// Ratio of expected HR required (GHREQUIRE) 0.0-0.8
	pthread_mutex_t joblock;		// Mutex for all access to jb
	struct GEKKOJOB job;			// running job rate buffer

	pthread_mutex_t slock;			// usleep() stats
	uint64_t num0;
	uint64_t num;
	double req;
	double fac;
	uint64_t num1_1;
	double req1_1;
	double fac1_1;
	uint64_t num1_5;
	double req1_5;
	double fac1_5;
	uint64_t inv;

	double workgen;				// work timing overrun stats
	int64_t over1num;
	double over1amt;
	int64_t over2num;
	double over2amt;

	struct timeval tune_limit;		// time between tune checks
	struct timeval last_tune_up;		// time of last tune up attempt

	unsigned char task[BUFFER_MAX];         // Task transmit buffer
	unsigned char cmd[BUFFER_MAX];          // Command transmit buffer
	unsigned char rx[BUFFER_MAX];           // Receive buffer
	unsigned char tx[BUFFER_MAX];           // Transmit buffer
	unsigned char end[1024];                // buffer overrun test
};

void stuff_lsb(unsigned char *dst, uint32_t x);
void stuff_msb(unsigned char *dst, uint32_t x);
void stuff_reverse(unsigned char *dst, unsigned char *src, uint32_t len);
uint64_t bound(uint64_t value, uint64_t lower_bound, uint64_t upper_bound);
