/*
* bit_display.cc
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/

#include "bit_display.h"
#include "dingl.h"

bit_display::bit_display (int w, int h) {
	num_bits = 0;
	lsnr = 0;
	prev_id = -1;
	set_size (w, h);
}

void bit_display::set_size (int w, int h) {
	set_extents (extents.left, extents.bottom, extents.left + w, extents.bottom + h);
	set_pos (posx, posy);
}

void bit_display::set_pos (int x, int y) {
	widget::set_pos (x, y);
	calc_visual_params ();
}

void bit_display::set (void* bytes, int num_bytes) { // show bits of the given bytes 
	num_bits = 8 * num_bytes;
	last_bit = num_bits - 1;
	bits.resize (num_bits); // init bit array
	unsigned char* pchar = (unsigned char *) bytes; // treat as character array
	for (int i = 0, k = 0; i < num_bytes; ++i) { // number of bytes = number of characters now
		unsigned char ci = pchar[i]; // ith character
		for (int j = 0; j < 8; ++j) { // extract bits of this character and store in bit array
			unsigned char tb = (ci & 0x80) >> 7; // extract left most bit
			bits[k++] = tb;
			ci = ci << 1; // left shift character by 1 bit 
		}
	}
	calc_visual_params ();
}

void bit_display::calc_visual_params () {
	if (num_bits) {
		d_width = int (extents.width / num_bits); // display width for each bit
		int width = d_width * num_bits; // total width occupied by displayed bits
		xstart = extents.left;
		xend = xstart + width;
	}
}

extern int mousex, mouseyy;

int bit_display::find_bit_id () {
	if (mousex < xstart) return 0; 
	else if (mousex > xend) return last_bit;
	return (mousex - xstart) / d_width;;
}

int bit_display::handle_input () {
	int ret = widget::handle_input ();
	if (hover) {
		if (lmb) {
			cur_id = find_bit_id ();
			if (cur_id != prev_id) {
				prev_id = cur_id;
				bits[cur_id] = !bits[cur_id];
				if (lsnr) lsnr->changed (*this);
			}
		} else prev_id = -1;
	}
	return ret;
}

void bit_display::draw () {
	glColor3f (clr.r, clr.g, clr.b);
	glPolygonMode (GL_FRONT, GL_LINE);
	glRecti (xstart, extents.bottom, xend, extents.top);
	int xl = xstart, xr = xl;
	for (int i = 0; i < num_bits; ++i) {
		unsigned char bi = bits[i];
		if (bi) glPolygonMode (GL_FRONT, GL_FILL); else glPolygonMode (GL_FRONT, GL_LINE);
		xr = xl + d_width;
		glRecti (xl, extents.bottom, xr, extents.top);
		xl = xr;
	}
	glPolygonMode (GL_FRONT, GL_FILL);
}

void bit_display::get_color (ucolor_t* uc) {
	unsigned char* pchar = (unsigned char*) uc;
	for (int i = 0, j = 0; i < 3; ++i) {
		unsigned char uci = 0;
		for (int k = 7; k > -1; --k) {
			unsigned char bj = bits[j++];
			uci = uci | (bj << k);
		}
		pchar[i] = uci;
	}
}
