#include "SCK.H"

__s32 cct[65536];

static int cct_valid;
__u16 max_cc;

int check_cct(void)
{
	int r;
	unsigned i, j;

	int ccerr, dataerr, secerr;

	cct_valid = 0;
	max_cc = 0;

	status_printf(0, "checking crash count table...");

	memset(cct, 0, 4 * 65536);
	for (i = 0; i <= tx.cc; i++) cct[i] = 0x7fffffff;

	if (__unlikely(r = alloc_blocks(super.cct, CCT_SIZE >> SSIZE_BITS))) {
		if (r == 1) log_printf(0, "CRASH COUNT TABLE ALLOCATION ERROR: %s", alloc_error);
		return 0;
	}

	ccerr = 0;
	dataerr = 0;
	secerr = 0;
	for (i = 0; i < CCT_SIZE / 4; i += (1 << SSIZE_BITS) / 4) {
		__u32 p[128];
		__u32 *pp;
		int invl_elem;
		print_progress(CCT_SIZE / 4, i, 0);
		if (__unlikely(!(pp = read_buffer(super.cct + i / ((1 << SSIZE_BITS) / 4), 1)))) {
			if (ccerr < MAX_SAME_ERRORS) {
				log_printf(0, "CAN'T READ CRASH COUNT TABLE SECTOR %d (ABS %"blk_format"X): %s", i / ((1 << SSIZE_BITS) / 4), super.cct + i / ((1 << SSIZE_BITS) / 4), buffer_error);
				if (++ccerr == MAX_SAME_ERRORS) log_suppress();
			}
			continue;
		}
		memcpy(p, pp, 512);
		invl_elem = 0;
		for (j = 0; j < (1 << SSIZE_BITS) / 4; j++) {
			if (__unlikely(p[j] & 0x80000000) || (__likely(i + j > tx.cc) && __unlikely(p[j]))) {
				if (i + j <= tx.cc) p[j] = 0x7fffffff;
				else p[j] = 0;
				invl_elem++;
			}
		}
		if (__unlikely(invl_elem)) {
			if (invl_elem <= 2) {
				if (dataerr < MAX_SAME_ERRORS) {
					log_printf(0, "INVALID ENTRIES IN CRASH COUNT TABLE SECTOR %d (ABS %"blk_format"X): %d ERRORS", i / ((1 << SSIZE_BITS) / 4), super.cct + i / ((1 << SSIZE_BITS) / 4), invl_elem);
					if (++dataerr == MAX_SAME_ERRORS) log_suppress();
				}
			} else {
				if (secerr < MAX_SAME_ERRORS) {
					log_printf(0, "INVALID DATA IN CRASH COUNT TABLE SECTOR %d (ABS %"blk_format"X)", i / ((1 << SSIZE_BITS) / 4), super.cct + i / ((1 << SSIZE_BITS) / 4));
					if (++secerr == MAX_SAME_ERRORS) log_suppress();
				}
				goto skip_memcpy;
			}
		}
		memcpy(cct + i, p, (1 << SSIZE_BITS));
		skip_memcpy:;
	}

	if (__unlikely(ccerr | dataerr | secerr | !tx_valid)) {
		if (__unlikely(r = free_blocks(super.cct, CCT_SIZE >> SSIZE_BITS))) return r;
	} else {
		cct_valid = 1;
	}

	return 0;
}

int recover_cct_allocs(void)
{
	if (cct_valid) {
		int r;
		if (__unlikely(r = alloc_blocks(super.cct, CCT_SIZE >> SSIZE_BITS))) {
			if (r == 1) log_printf(1, "FAILED TO RECOVER CRASH COUNT TABLE ALLOCATION: %s", alloc_error);
			return 2;
		}
	}
	return 0;
}

int fix_cct(void)
{
	if (__unlikely(!cct_valid)) {
		int r;
		if (!tx_valid) {
			int i;
			tx.cc = max_cc;
			for (i = tx.cc + 1; i < CCT_SIZE / 4; i++) cct[i] = 0;
		}
		status_printf(0, "fixing crash count table...");
		r = alloc_try(super.cct, CCT_SIZE >> SSIZE_BITS);
		if (!r) {
			if (!query(qprefix 0, "OVERWRITE CRASH COUNT TABLE?")) return 0;
		} else {
			blk_t new_cct;
			if (__unlikely(r > 0)) return r;
			if (!query(qprefix 0, "ALLOC NEW CRASH COUNT TABLE?")) return 0;
			if (__unlikely(r = alloc_space(1, "CRASH COUNT TABLE", CCT_SIZE >> SSIZE_BITS, 0, &new_cct))) return r;
			super.cct = new_cct;
			write_super();
		}
		cct_valid = 1;
		if (reset_cc) goto do_reset;
		goto do_write;
	}
	if (reset_cc) {
		status_printf(0, "resetting crash count table...");
		do_reset:
		memset(cct, 0, CCT_SIZE);
		tx.cc = 0;
		write_txblock();
		do_write:
		write_buffer(super.cct, CCT_SIZE >> SSIZE_BITS, cct);
	}
	return 0;
}

__finline__ int cc_valid(__u16 cc, __s32 txc)
{
	if (__unlikely(cc > max_cc)) max_cc = cc;
	return (__s32)((__u32)cct[cc] - (__u32)txc) >= 0;
}

int cc_check(__u16 *cc, __s32 *txc, char *str1, char *(*get_filename)(void *p), void *p, blk_t blk)
{
	char *msg;
	if (__unlikely(*cc > tx.cc)) {
		char *fn;
		msg = "CRASH COUNT IS POINTING FORWARD";
		err:
		fn = NULL;
		if (get_filename) fn = get_filename(p);
		if (!fn) fn = "";
		log_printf(0, "%s (TXC=%08X, CC=%04X, CCT[CC]=%08X, TXC.CC=%04X) FOR %s%s%s, BLOCK %"blk_format"X", msg, *txc, *cc, cct[*cc], tx.cc, str1, *fn ? " " : "", fn, blk);
		if (query(qprefix 0, "FIX?")) goto fix_it;
		goto ret_z;
	}
	if (__unlikely((__u32)(*txc & 0x7fffffff) > (__u32)cct[*cc] + (*cc < tx.cc))) {
		msg = "TRANSACTION COUNT IS TOO HIGH";
		goto err;
	}
	if (__likely(!reset_cc)) {
		ret_z:
		if (__unlikely(*cc > max_cc)) max_cc = *cc;
		return 0;
	}
	if (!(*cc | (*txc & 0x7fffffff))) return 0;
	*txc = (~(__u32)cc_valid(*cc, *txc)) << 31;
	*cc = 0;
	return 1;
	fix_it:
		/* in case of error, it is more likely that crash count table has been
		   damaged (for example overwritten with zeros). Act as if there was no
		   crash count table */
	*txc &= 0x80000000;
	*cc = 0;
	return 1;
}

