#include "bootpack.h"
#include <stdio.h>

void BitNosMain() { BitNosMainCpp(); }
inline color_16 convC(dword color);
inline dword convC_inv(color_16 color);
color_16 mixC(dword color1, dword color2, int color1_ratio, int color2_ratio);

// グローバルメモリマネージャ
Memory g_km;

Font* font_hankaku;
Font* font_mini;

Screen* pscreen;
SheetController* g_shtctl;

#ifndef DEBUG_MESSAGE
queue<Message*>* task1_fifo;
#else
queue<Message>* task1_fifo;
#endif
DebugWindow* dbg;

int BitNosMainCpp()
{
	BootInfo binfo(reinterpret_cast<BootInfo*>(ADR_BOOTINFO));
	int x, y;
	bool right_pushed = false;

	dword memory_size = g_km.getMemorySize(0x00400000, 0xbfffffff); /* 物理メモリサイズ */
	g_km.init(0x00400000, memory_size); /* メモリマネージャ初期化 */

	// VESAドライバの生成と、スクリーンの設定
	Vesa vesa_driver;
	vesa_driver.init(reinterpret_cast<word*>(binfo.vram), binfo.scrn_x, binfo.scrn_y);
	pscreen = new Screen(&vesa_driver, binfo.scrn_x, binfo.scrn_y);
	Screen& screen = *pscreen;
	g_shtctl = new SheetController();
	g_timerctl = new TimerController();

	Sheet* sheet_back = g_shtctl->alloc();
	sheet_back->init(binfo.scrn_x, binfo.scrn_y);
	Picture& buffer_back = sheet_back->get_buffer();
	sheet_back->set_invisible_color(0x0001);
	g_shtctl->updown(sheet_back, 0);

	char* s = new char[256];

	// 半角フォント（標準）の設定  xsize = 8, ysize = 16
	font_hankaku = new Font(hankaku, 8, 16);
	// ミニフォント（OSAkkie）の設定  xsize = 6, ysize = 12
	font_mini = new Font(minifnt, 6, 12);

	#ifndef DEBUG_MESSAGE
	task1_fifo = new queue<Message*>(512);
	#else
	task1_fifo = new queue<Message>(256);
	#endif
	dbg = new DebugWindow(binfo.scrn_x - 8 * 30, 0, 8 * 30, binfo.scrn_y);
    

	
	// FDDのモーターを止める
	IO8 fd_motor(0x03f2);
	fd_motor << 0x0c;
	
	GDT::init();
	IDT::init();
	
	PIC::init();
	
	PIT::init();
	
	IO8 pic0_imr(PIC::PIC0_IMR);
	IO8 pic1_imr(PIC::PIC1_IMR);
	pic0_imr << 0xf8; // タイマー,キーボード割り込み許可
	pic1_imr << 0xef; // マウス割り込み許可

	
	
//	queue<dword> fifo(1024);

//	int color_ratio = 60;
	// 背景描画
	for (y = 0; y < binfo.scrn_y; y++) {
		for (x = 0; x < binfo.scrn_x; x++) {
			buffer_back.point(x, y, Screen::BACKGROUND_COLOR);
		}
	}
	// デバッグコンソールの初期化
	for (y = 0; y < binfo.scrn_y; y++) {
		for (x = binfo.scrn_x - 8 * 30; x < binfo.scrn_x; x++) {
			buffer_back.point(x, y, 0x0000);
		}
	}

	Sheet* sheet_taskbar = g_shtctl->alloc();
	sheet_taskbar->init(binfo.scrn_x, 26);
	sheet_taskbar->set_invisible_color(0x0001);
	Picture& buffer_taskbar = sheet_taskbar->get_buffer();
	buffer_taskbar.box(0, 0, binfo.scrn_x - 1, 26 - 1, Screen::TASKBAR_COLOR);
	buffer_taskbar.box(0, 0, binfo.scrn_x - 1, 0, 0xffff);
	g_shtctl->slide(sheet_taskbar, 0, binfo.scrn_y - 26);
	g_shtctl->updown(sheet_taskbar, 1);

	Sheet* sheet_win1 = g_shtctl->alloc();
	sheet_win1->init(100, 100);
	Picture& buffer_win1 = sheet_win1->get_buffer();
	buffer_win1.box(0, 0, 100 - 1, 100 - 1, 0xffff);
	g_shtctl->slide(sheet_win1, 100, 10);
	g_shtctl->updown(sheet_win1, 2);

	Sheet* sheet_dframe = 0; // ウィンドウドラッグの枠

/*
	// 文字テーブルを表示
	for (y = 0; y < 16; y++) {
		for (x = 0; x < 16; x++) {
			buffer_back.putchar(x * 11, y * 19, (byte)(y * 16 + x), 0x0000, font_mini);
			buffer_back.putchar(11 * 16 + 20 + x * 11, y * 19, (byte)(y * 16 + x), 0x0000, font_hankaku);
		}
	}
	for (x = 9; x < 176; x += 11) {
		buffer_back.line(x, 0, x, 304 - 2, 0x0000);
	}
	for (y = 17; y < 304; y += 19) {
		buffer_back.line(0, y, 176 - 2, y, 0x0000);
	}
	for (x = -2; x < 176; x += 11) {
		buffer_back.line(196 + x, 0, 196 + x, 304 - 2, 0x0000);
	}
	for (y = 17; y < 304; y += 19) {
		buffer_back.line(196 - 2, y, 196 + 176 - 2, y, 0x0000);
	}


	buffer_back.putstr(0, 400, "ﾆﾎﾝｺﾞﾓ ﾋｮｳｼﾞ ﾃﾞｷﾙ｡", 0x0000, font_mini);
	buffer_back.putstr(0, 412, "ｺｳｾｲﾉｳ ﾃﾞｽﾈ｡", 0x0000, font_mini);
	buffer_back.putchar_expand(0 , 450, 'A',
		0x0000, 1024 * 1024 * 1);
	buffer_back.putchar_expand(16, 450, 'A',
		0x0000, static_cast<dword>(1024 * 1024 * 0.9));
	buffer_back.putchar_expand(32, 450, 'A',
		0x0000, 1024 * 1024 * 4);
	buffer_back.putstr_expand(100, 450, "ｺｳｾｲﾉｳ ﾃﾞｽﾈ｡", 0x0000, 1024 * 1024 * 5, font_mini);
	buffer_back.putstr_expand(0, 550, "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
		0x0000, static_cast<dword>(1024 * 1024 * 0.9));
	//Font font_h(hankaku, 8, 16);
*/

//	screen.box(0, 0, binfo.scrn_x, binfo.scrn_y, 0x3333);

	byte data[5] = {0, 0, 0, 0, 0};
	Message* msg = 0;
	#ifdef DEBUG_MESSAGE
	Message msg_;
	#endif

//	buffer_back.box(0, 424, 8 * 15 - 1, 424 + 16 - 1, 0xffff);
//	sprintf(s, "free:%dB", g_km.getFreeSize());
//	buffer_back.putstr(0, 424, s, 0x0000);

	
	/*
	 * 実験用コード
	 */
/*	class CBase {
	protected:
		DebugWindow& cout;
	public:
		CBase() : cout(*dbg)
		{
			cout << "CBase constructor is called.";
		}
		//virtual ~CBase()
		virtual ~CBase()
		{
			cout << "CBase destructor is called.";
		}
	};
	class CSub1 : public CBase {
	public:
		CSub1()
		{
			cout << "CSub1 constructor is called.";
		}
		virtual ~CSub1()
		{
			cout << "CSub1 destructor is called.";
		}
	};
	class CSub2 : public CBase {
	public:
		CSub2() : CBase()
		{
			cout << "CSub2 constructor is called.";
		}
		virtual ~CSub2()
		{
			cout << "CSub2 destructor is called.";
		}
	};
//	CBase* cbase = new CBase;
//	delete cbase;
//	CBase* csub = new CSub;
//	delete csub;
	CBase* cbase1 = new CSub1;
	delete cbase1; 
	CBase* cbase2 = new CSub2;
	delete cbase2;
*/
	/*
	 * 実験用コードここまで
	 */
	
	g_shtctl->refresh(sheet_back, 0, 0, buffer_back.get_xsize(), buffer_back.get_ysize());

	Sheet* win1 = g_shtctl->alloc();
	win1->init(100, 100);
	Picture& buf = win1->get_buffer();
	buf.box(0, 0, 100 - 1, 100 - 1, 0xffff);
	//g_shtctl->updown(win1, 1);
	//g_shtctl->refresh(win1, 0, 0, 100, 100);

	int m_xpos = binfo.scrn_x / 2, m_ypos = binfo.scrn_y / 2;
	Sheet* sheet_mouse = g_shtctl->alloc(true);
	sheet_mouse->init(Mouse::XSIZE, Mouse::YSIZE);
	Picture& buffer_mouse = sheet_mouse->get_buffer();
	sheet_mouse->set_invisible_color(0x0001);
	//buffer_mouse.box(0, 0, 8 - 1, 16 - 1, 0x0001);
	Mouse::init_cursor(buffer_mouse);
	g_shtctl->slide(sheet_mouse, m_xpos, m_ypos);
	g_shtctl->updown(sheet_mouse, 1000);
	//g_shtctl->refresh(sheet_mouse, 0, 0, 100, 100);
	
//	buffer_back.box(0, 0, 800 - 1, 32 - 1, 0x0000);
	buffer_back.putstr(0, 0,  "INT 21 (IRQ-1) : PS/2 keyboard", 0xffff);
//	buffer_back.box(0, 32, 800 - 1, 64 - 1, 0x0000);
	buffer_back.putstr(0, 32,  "INT 2c (IRQ-12) : PS/2 mouse", 0xffff);
	g_shtctl->refresh(sheet_back, 0, 0, 800, 64);

	g_shtctl->refresh_map(0, 0, binfo.scrn_x, binfo.scrn_y, 0);
	
	Keyboard::init();
	Mouse::enable();
	for (;;) {
		io_cli();
		if (task1_fifo->size() == 0) {
			io_stihlt();
			continue;
		}
		#ifndef DEBUG_MESSAGE
		msg = task1_fifo->back();
		#else
		//msg_ = task1_fifo->back();
		//msg = &msg_;
		msg = &task1_fifo->back();
		#endif
		// Message::create()の中で、newを使って生成しているため、ここでdeleteする
		io_sti();
		if (msg->type == MSG::TYPE::KEYBOARD) {
			data[4] = data[3];
			data[3] = data[2];
			data[2] = data[1];
			data[1] = data[0];
			data[0] = msg->arg1;
			sprintf(s, "KEY CODE : %02X -> %02X -> %02X -> %02X -> %02X",
				data[0], data[1], data[2], data[3], data[4]);
			buffer_back.box(0, 16, strlen(s) * 8 - 1, 32 - 1, Screen::BACKGROUND_COLOR);
			buffer_back.putstr(0, 16, s, 0xffff);
			g_shtctl->refresh(sheet_back, 0, 16, strlen(s) * 8, 32);
			if (data[0] == 0x01) { // ESCキーでリブート
//				pic0_imr << 0xff; // 全割り込み禁止
//				pic1_imr << 0xff;
				Mouse::out(0xf5); // データ送信禁止
				io_cli();
				IO8 KBC(0x64);
				KBC << 0xfe; // reboot
				for (;;) io_hlt();
			}
		} else if (msg->type == MSG::TYPE::MOUSE) {
			if (Mouse::decode(msg->arg1)) {
				// デコード完了
/*				sprintf(s, "(X, Y, Z) = (%4d,%4d,%2d)  button:",
					Mouse::mdec.x,
					Mouse::mdec.y,
					Mouse::mdec.scroll
					);
				buffer_back.box(0, 48, 8 * 40 - 1, 64 - 1, Screen::BACKGROUND_COLOR);
				buffer_back.putstr(0, 48, s, 0xffff);
				strcpy(s, "lcr");
				if (Mouse::mdec.button & 0x01) {
					// 左クリック
					s[0] = 'L';
				}
				if (Mouse::mdec.button & 0x02) {
					// 右クリック
					s[2] = 'R';
				}
				if (Mouse::mdec.button & 0x04) {
					// 中央クリック
					s[1] = 'C';
				}
				buffer_back.putstr(8 * 35, 48, s, 0xffff);
				g_shtctl->refresh(sheet_back, 0, 48, 8 * 40, 64);
*/				m_xpos += Mouse::mdec.x;
				m_ypos += Mouse::mdec.y;
				if (m_xpos < 0) { m_xpos = 0; }
				if (m_xpos > binfo.scrn_x) { m_xpos = binfo.scrn_x; }
				if (m_ypos < 0) { m_ypos = 0; }
				if (m_ypos > binfo.scrn_y) { m_ypos = binfo.scrn_y; }
				g_shtctl->slide(sheet_mouse, m_xpos, m_ypos);
				
				buffer_win1.box(10, 10, 10 + 8 * 8 - 1, 10 + 16 - 1, 0xffff);
				sprintf(s, "%03d, %03d", m_xpos, m_ypos);
				buffer_win1.putstr(10, 10, s, convC(0x555555));
				g_shtctl->refresh(sheet_win1, 10, 10, 10 + 8 * 8, 10 + 16);

				if (right_pushed) {
					g_shtctl->slide(sheet_dframe, m_xpos - 10, m_ypos - 10);
				}

				if (right_pushed == false && Mouse::mdec.button & 0x01) {
					right_pushed = true;
					sheet_dframe = g_shtctl->alloc();
					//sheet_dframe->init(buffer_win1.get_xsize(), buffer_win1.get_ysize());
					sheet_dframe->init(20, 20);
					sheet_dframe->set_invisible_color(0x0000);
					Picture& buffer_dframe = sheet_dframe->get_buffer();
//					buffer_dframe.box(0, 0,
//						buffer_win1.get_xsize() - 1, buffer_win1.get_ysize() - 1,
//						convC(0xC0C0C0));
//					buffer_dframe.box(3, 3,
//						buffer_win1.get_xsize() - 4, buffer_win1.get_ysize() - 4,
//						0x0000);
					buffer_dframe.box(0, 0,
						20 - 1, 20 - 1,
						0x0000);
					buffer_dframe.box(8, 0,
						12 - 1, 20 - 1,
						convC(0xC0C0C0));
					buffer_dframe.box(0, 8,
						20 - 1, 12 - 1,
						convC(0xC0C0C0));
					g_shtctl->slide(sheet_dframe,
						sheet_win1->get_xpos() - 10, sheet_win1->get_ypos() - 10);
					g_shtctl->updown(sheet_dframe, 1000);
				} else if (right_pushed && (Mouse::mdec.button & 0x01) == 0) {
					g_shtctl->updown(sheet_dframe, -1);
					g_shtctl->slide(sheet_win1,
						sheet_dframe->get_xpos() + 10, sheet_dframe->get_ypos() + 10);
					g_shtctl->free(sheet_dframe);
					right_pushed = false;
				}

				int sid = g_shtctl->get_sheetid(m_xpos, m_ypos);
				
				buffer_taskbar.box(0, 0, 8 * 3 - 1, 16 - 1, Screen::TASKBAR_COLOR);
				sprintf(s, "%03d", sid);
				buffer_taskbar.putstr(0, 0, s, 0x0000);
				g_shtctl->refresh(sheet_taskbar, 0, 0, 8 * 3, 16);
			}
/*		} else if (msg->type == MSG::TYPE::TIMER) {
			dword cnt = g_timerctl->get_count();
			if (cnt % 10 == 0) {
				sprintf(s, "%010d", cnt);
				buffer_back.box(0, 64, 8 * 10 - 1, 64 + 16 - 1, 0x0000);
				buffer_back.putstr(0, 64, s, 0xffff);
				g_shtctl->refresh(sheet_back, 0, 64, 8 * 10, 64 + 16);
			}
*/		}
		//buffer_back.box(0, 424, 8 * 15 - 1, 424 + 16 - 1, 0xffff);
		//sprintf(s, "free:%dB", g_km.getFreeSize());
		//buffer_back.putstr(0, 424, s, 0x0000);
		//g_shtctl->refresh(sheet_back, 0, 424, 8 * 15, 424 + 16);
		
		#ifndef DEBUG_MESSAGE
		delete msg;
		#endif
		task1_fifo->pop();
	}
	delete[] s;
	
	for (;;) {
		io_stihlt();
	}
	return 0;
}

color_16 convC(dword color)
{
	// 24bit color → 16bit color
	return ((color >> 8) & 0xf800) | ((color >> 5) & 0x07e0) | ((color >> 3) & 0x001f);
}

dword convC_inv(color_16 color)
{
	// 16bit color → 24bit color
	return ((color & 0xf800) << 8) | ((color & 0x07e0) << 5) | ((color & 0x001f) << 3);
}

color_16 mixC(dword color1, dword color2, int color1_ratio, int color2_ratio)
{
	// 色を合成する
	byte r, g, b;
	byte r1, g1, b1;
	byte r2, g2, b2;
/*	r1 = (color1 & 0xf800) >> 8;
	g1 = (color1 & 0x07e0) >> 3;
	b1 = (color1 & 0x001f) << 3;
	r2 = (color2 & 0xf800) >> 8;
	g2 = (color2 & 0x07e0) >> 3;
	b2 = (color2 & 0x001f) << 3;
*/
	r1 = (color1 & 0xff0000) >> 16;
	g1 = (color1 & 0x00ff00) >> 8;
	b1 = (color1 & 0x0000ff);
	r2 = (color2 & 0xff0000) >> 16;
	g2 = (color2 & 0x00ff00) >> 8;
	b2 = (color2 & 0x0000ff);
	r = (r1 * color1_ratio + r2 * color2_ratio) / (color1_ratio + color2_ratio);
	g = (g1 * color1_ratio + g2 * color2_ratio) / (color1_ratio + color2_ratio);
	b = (b1 * color1_ratio + b2 * color2_ratio) / (color1_ratio + color2_ratio);
	return convC(r << 16 | g << 8 | b);
}

DebugWindow& DebugWindow::operator <<(const char* str)
{
	int x, y;
	if (cursor_y_ > ysize_ - 16) {
		// ずらしをする
		for (y = 0; y < cursor_y_; y++) {
			for (x = xpos_; x < xpos_ + xsize_; x++) {
				pscreen->point(x, y, pscreen->get_color(x, y + 16));
			}
		}
		pscreen->putstr(xpos_, cursor_y_ - 16, str, 0xffff);
	} else {
		pscreen->putstr(xpos_, cursor_y_, str, 0xffff);
		cursor_y_ += 16;
	}
	return *this;
}
