/*
 * 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.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see .
 */
#include "MainWindow.h"
#include "FXMessageBox.h"
#include "fxdefs.h"
#include 
#include 
FXDEFMAP(MainWindow) MainWindow_Map[]=
{
	//________Message_Type____________ID____________Message_Handler_______
	FXMAPFUNC(SEL_PAINT,             MainWindow::ID_CANVAS, MainWindow::on_paint),
	FXMAPFUNC(SEL_LEFTBUTTONPRESS,   MainWindow::ID_CANVAS, MainWindow::on_left_mouse_down),
	FXMAPFUNC(SEL_LEFTBUTTONRELEASE,   MainWindow::ID_CANVAS, MainWindow::on_left_mouse_up),
	FXMAPFUNC(SEL_RIGHTBUTTONPRESS,   MainWindow::ID_CANVAS, MainWindow::on_right_mouse_down),
	FXMAPFUNC(SEL_MOTION,            MainWindow::ID_CANVAS, MainWindow::on_mouse_move),
	FXMAPFUNC(SEL_KEYPRESS, 0, MainWindow::on_key_press),
	FXMAPFUNC(SEL_KEYRELEASE, 0, MainWindow::on_key_release),
	/* toolbox */
	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_INPUT, MainWindow::input_button_press),
	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_OUTPUT, MainWindow::output_button_press),
	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_AND, MainWindow::and_button_press),
	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_OR, MainWindow::or_button_press),
	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_NOT, MainWindow::not_button_press),
};
FXIMPLEMENT(MainWindow, FXMainWindow, MainWindow_Map, ARRAYNUMBER(MainWindow_Map))
MainWindow::MainWindow(FXApp *a)
	: FXMainWindow(a, "foxboxes", nullptr, nullptr, DECOR_ALL, 0, 0, 500, 500)
{
	app = a;
	create_ui();
}
MainWindow::~MainWindow()
{
}
void
MainWindow::create()
{
	FXMainWindow::create();
	INPUT_icon->create();
	OUTPUT_icon->create();
	AND_icon->create();
	OR_icon->create();
	NOT_icon->create();
	canvas_image->create();
	show(PLACEMENT_SCREEN);
}
void
MainWindow::create_ui()
{
	contents=new FXHorizontalFrame(this, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y);
	toolsFrame = new FXVerticalFrame(contents, FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT, 0, 0, 0, 0, 10, 10, 10, 10);
	canvasFrame=new FXVerticalFrame(contents, FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT, 0, 0, 0, 0, 10, 10, 10, 10);
	new FXLabel(canvasFrame,"foxlogicgates", NULL, JUSTIFY_CENTER_X|LAYOUT_FILL_X);
	new FXHorizontalSeparator(canvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X);
	scroll_area = new FXScrollWindow(canvasFrame, FX::SCROLLERS_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN);
	scroll_area->setBackColor(canvasFrame->getBackColor());
	canvas = new FXCanvas(scroll_area, this, ID_CANVAS, FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN);
	canvas_image = new FXBMPImage(app, NULL, 0, 5000, 5000);
	new FXLabel(toolsFrame, "Toolbox", NULL, JUSTIFY_CENTER_X|LAYOUT_FILL_X);
	new FXHorizontalSeparator(toolsFrame, SEPARATOR_RIDGE|LAYOUT_FILL_X);
	INPUT_icon = new FXGIFIcon(app, INPUT_icon_data, IMAGE_KEEP);
	OUTPUT_icon = new FXGIFIcon(app, OUTPUT_icon_data, IMAGE_KEEP);
	AND_icon = new FXGIFIcon(app, AND_icon_data, IMAGE_KEEP);
	OR_icon = new FXGIFIcon(app, OR_icon_data, IMAGE_KEEP);
	NOT_icon = new FXGIFIcon(app, NOT_icon_data, IMAGE_KEEP);
	/* tools */
	new FXButton(toolsFrame, "", INPUT_icon, this, MainWindow::ID_BUTTON_INPUT, BUTTON_NORMAL);
	new FXButton(toolsFrame, "", OUTPUT_icon, this, MainWindow::ID_BUTTON_OUTPUT, BUTTON_NORMAL);
	new FXButton(toolsFrame, "", AND_icon, this, MainWindow::ID_BUTTON_AND, BUTTON_NORMAL);
	new FXButton(toolsFrame, "", OR_icon, this, MainWindow::ID_BUTTON_OR, BUTTON_NORMAL);
	new FXButton(toolsFrame, "", NOT_icon, this, MainWindow::ID_BUTTON_NOT, BUTTON_NORMAL);
}
void
MainWindow::draw()
{
	FXDCWindow dc_image(canvas_image);
	dc_image.setFont(getApp()->getNormalFont());
	dc_image.setForeground(FXRGB(255, 255, 255));
	dc_image.fillRectangle(canvas->getX(), canvas->getY(), canvas->getWidth(), canvas->getHeight());
	dc_image.setForeground(FXRGB(0,0,0));
	/* draw gates */
	Gate *gate1;
	for(auto g1 = gates.begin(); g1 != gates.end(); ++g1)
	{
		gate1 = (*g1).get();
		if (gate1)
			gate1->update_state();
		switch(gate1->get_gate_type())
		{
			case Gate::INPUT:
			{
				if (gate1->get_output_state() == true)
				{
					/* input is switched on, indicate so */
					dc_image.setForeground(FXRGB(255, 255, 0));
					dc_image.fillRectangle(gate1->get_x(), gate1->get_y(), gate1->get_width(), gate1->get_height());
					dc_image.setForeground(FXRGB(0,0,0));
				}
				dc_image.drawIcon(INPUT_icon, gate1->get_x(), gate1->get_y());
				break;
			}
			case Gate::OUTPUT:
			{
				if (gate1->get_output_state() == true)
				{
					/* output ison, indicate so */
					dc_image.setForeground(FXRGB(255, 255, 0));
					dc_image.fillRectangle(gate1->get_x(), gate1->get_y(), gate1->get_width(), gate1->get_height());
					dc_image.setForeground(FXRGB(0,0,0));
				}
				dc_image.drawIcon(OUTPUT_icon, gate1->get_x(), gate1->get_y());
				break;
			}
			case Gate::AND:
			{
				dc_image.drawIcon(AND_icon, gate1->get_x(), gate1->get_y());
				dc_image.drawText(gate1->get_x(), gate1->get_y()+gate1->get_height()+20, "AND");
				break;
			}
			case Gate::OR:
			{
				dc_image.drawIcon(OR_icon, gate1->get_x(), gate1->get_y());
				dc_image.drawText(gate1->get_x(), gate1->get_y()+gate1->get_height()+20, "OR");
				break;
			}
			case Gate::NOT:
			{
				dc_image.drawIcon(NOT_icon, gate1->get_x(), gate1->get_y());
				dc_image.drawText(gate1->get_x(), gate1->get_y()+gate1->get_height()+20, "NOT");
				break;
			}
			case Gate::NONE:
			default:
				break;
		}
	}
	/* draw selected gate border box if one is selected */
	if (selected_gate != nullptr)
	{
		dc_image.drawHashBox(selected_gate->get_x(), selected_gate->get_y(), selected_gate->get_width(), selected_gate->get_height());
	}
	/* draw dragging link */
	if (dragging_link && selected_gate)
	{
		dc_image.drawLine(selected_gate->get_x()+selected_gate->get_width()-5, selected_gate->get_y()+selected_gate->get_height()/2-2, mouse_x, mouse_y);
	}
	/* draw links */
	for(auto g1 = gates.begin(); g1 != gates.end(); ++g1)
	{
		/* draw lines from input gate->output gate */
		gate1 = (*g1).get();
		Gate *in_gate1 = gate1->get_input_gate1();
		Gate *in_gate2 = gate1->get_input_gate2();
		if (!gate1)
			continue;
		if (gate1->get_input_gate1() != nullptr)
		{
			if (gate1->get_gate_type() == Gate::NOT|| gate1->get_gate_type() == Gate::NOR || gate1->get_gate_type() == Gate::OUTPUT)
			{
				/* NOT,NOR,OUTPUT need a special case */
				dc_image.drawLine(in_gate1->get_x()+in_gate1->get_width()-5, in_gate1->get_y()+(in_gate1->get_height()/2),
						gate1->get_x()+10, gate1->get_y()+(gate1->get_height()/2));
			}
			else
			{
				dc_image.drawLine(in_gate1->get_x()+in_gate1->get_width()-5, in_gate1->get_y()+(in_gate1->get_height()/2),
						gate1->get_x()+10, gate1->get_y()+7);
			}
		}
		if (gate1->get_input_gate2() != nullptr)
		{
			if (gate1->get_gate_type() == Gate::NOT)
			{
				/* NOT,NOR,OUTPUT need a special case */
				continue;
			}
			else
			{
			dc_image.drawLine(in_gate2->get_x()+in_gate2->get_width()-5, in_gate2->get_y()+(in_gate2->get_height()/2),
					gate1->get_x()+10, gate1->get_y()+47);
			}
		}
	}
	FXDCWindow dc_canvas(canvas);
	dc_canvas.drawImage(canvas_image, 0, 0);
}
Gate
*MainWindow::find_gate_at(int x, int y)
{
	/* iterate backwards through vector to get box on top */
	Gate *returngate = nullptr;
	Gate *gate = nullptr;
	int bx, by, bw, bh;
	for (auto g = gates.rbegin(); g != gates.rend(); ++g)
	{
		gate = (*g).get();
		bx = gate->get_x();
		by = gate->get_y();
		bw = gate->get_width();
		bh = gate->get_height();
		/* check if x,y pos is intersecting with the gate */
		if (x >= bx && x <= bx+bw &&
				y >= by && y <= by+bh)
		{
			returngate = gate;
			break;
		}
	}
	return returngate;
}
Gate
*MainWindow::find_gate_by_id(int id)
{
	/* iterate backwards through vector to get box on top */
	Gate *gate = nullptr;
	for (auto g = gates.rbegin(); g != gates.rend(); ++g)
	{
		gate = (*g).get();
		if (gate->get_id() == id)
			return gate;
	}
	return gate;
}
long
MainWindow::on_paint(FXObject*, FXSelector, void *ptr)
{
	draw();
	return 1;
}
long
MainWindow::on_left_mouse_down(FXObject*, FXSelector, void *ptr)
{
	FXEvent *ev = (FXEvent*)ptr;
	lmouse_down = true;
	Gate *gate = nullptr;
	if (selected_gate_type != Gate::NONE)
	{
		/* add new gate */
		std::unique_ptr gate(new Gate(selected_gate_type, ev->last_x-70/2, ev->last_y-50/2, 70, 50));
		selected_gate = gate.get();
		gates.push_back(std::move(gate));
		selected_gate_type = Gate::NONE;
	}
	else
	{
		/* do other things */
		gate = find_gate_at(ev->last_x, ev->last_y);
		if (gate)
		{
			/* if we found a gate, select it */
			selected_gate = gate;
			if (lshift_down)
			{
				dragging_link = true;
			}
		}
		else
		{
			selected_gate = nullptr;
		}
		
	}
	draw();
	return 1;
}
long
MainWindow::on_left_mouse_up(FXObject*, FXSelector, void *ptr)
{
	FXEvent *ev = (FXEvent*)ptr;
	lmouse_down = false;
	if (lshift_down && dragging_link && selected_gate)
	{
		Gate *gate;
		gate = find_gate_at(ev->last_x, ev->last_y);
		if (gate)
		{
			int input = -1;
			if (ev->last_y-gate->get_y() <= gate->get_height()/2)
				input = 1;
			else
				input = 2;
			if (gate->get_gate_type() != Gate::NOT && gate->get_gate_type() != Gate::NOR && gate->get_gate_type() != Gate::OUTPUT)
			{
				printf("connecting gate %d with gate %d at input #%d\n", selected_gate->get_id(), gate->get_id(), input);
				if (input == 1)
				{
					gate->set_input_gate1(selected_gate);
				}
				else if (input == 2)
				{
					gate->set_input_gate2(selected_gate);
				}
			}
			else
			{
				/* NOT,NOR,OUTPUT gates needs a special case */
				if (input == 1 || input == 2)
				{
					printf("connecting gate %d with gate %d at input #1\n", selected_gate->get_id(), gate->get_id());
					gate->set_input_gate1(selected_gate);
				}
			}
			selected_gate->set_output_gate(gate);
		}
		dragging_link = false;
	}
	return 1;
}
long
MainWindow::on_right_mouse_down(FXObject*, FXSelector, void *ptr)
{
	FXEvent *ev = (FXEvent*)ptr;
	Gate *gate;
	gate = find_gate_at(ev->last_x, ev->last_y);
	if (gate)
	{
		if (gate->get_gate_type() == Gate::INPUT)
		{
			/* toggle state */
			gate->set_state(!gate->get_output_state());
			draw();
		}
	}
	return 1;
}
long
MainWindow::on_key_press(FXObject *sender, FXSelector sel, void *ptr)
{
	FXEvent* event=(FXEvent*)ptr;
	switch(event->code)
	{
		case KEY_Shift_L:
			lshift_down = true;
			break;
		default:
			this->onKeyPress(sender, sel, ptr);
			break;
	}
	draw();
	return 1;
}
long
MainWindow::on_key_release(FXObject *sender, FXSelector sel, void *ptr)
{
	FXEvent* event=(FXEvent*)ptr;
	switch(event->code)
	{
		case KEY_Shift_L:
			lshift_down = false;
			dragging_link = false;
			break;
		default:
			this->onKeyPress(sender, sel, ptr);
			break;
	}
	draw();
	return 1;
}
long
MainWindow::on_mouse_move(FXObject *sender, FXSelector sel, void *ptr)
{
	FXEvent* event = (FXEvent*)ptr;
	mouse_x = event->last_x;
	mouse_y = event->last_y;
	if (lmouse_down && !dragging_link && selected_gate)
	{
		selected_gate->set_x(event->last_x-selected_gate->get_width()/2);
		selected_gate->set_y(event->last_y-selected_gate->get_height()/2);
	}
	draw();
	return 1;
}
long
MainWindow::input_button_press(FXObject *sender, FXSelector sel, void *ptr)
{
	selected_gate = nullptr;
	selected_gate_type = Gate::INPUT;
	return 1;
}
long
MainWindow::output_button_press(FXObject *sender, FXSelector sel, void *ptr)
{
	selected_gate = nullptr;
	selected_gate_type = Gate::OUTPUT;
	return 1;
}
long
MainWindow::and_button_press(FXObject *sender, FXSelector sel, void *ptr)
{
	selected_gate = nullptr;
	selected_gate_type = Gate::AND;
	return 1;
}
long
MainWindow::or_button_press(FXObject *sender, FXSelector sel, void *ptr)
{
	selected_gate = nullptr;
	selected_gate_type = Gate::OR;
	return 1;
}
long
MainWindow::not_button_press(FXObject *sender, FXSelector sel, void *ptr)
{
	selected_gate = nullptr;
	selected_gate_type = Gate::NOT;
	return 1;
}