#include "maindlg.h"
#include "wcolgradgen.h"


AttractorItem::AttractorItem(WColImage::Attractor att)
	:ListBoxEx::Item()
{
	this->att = att;
}


void AttractorItem::DrawItem(Bitmap &canvas, int i, int state, const Rect &dstRect) {
	if (parent->AreGridLinesEnabled()) {
		DrawGrid(canvas, i, state, dstRect);
	}

	int s;
	switch (state) {
		case 0:
		case 1:
			s = 0;	break;
		case 2:
		case 3:
			s = 3;	break;
		case 4:
		case 5:
			s = 1;	break;
		case 6:
		case 7:
			s = 2;	break;
	};

	Color fg = parent->GetFontColor(s);
	Color bg = parent->GetShadowColor(s);
	Font fnt = parent->GetFont(s);
	Color tm = parent->GetTextMode();

	int xx = dstRect.x() + 24;
	int xx2 = dstRect.x() + 4;
	int yy = dstRect.y() + (dstRect.h() - fnt.TextHeight())/2;

	static const char *att_type_text[] = { "GLOBAL", "POINT", "HBAR", "VBAR" };
	static const char *att_mode_text[] = { "+", "-" };
	static char buf[64];
	usprintf(buf, "%s%s: (%2.2f,%2.2f) R=%2.2f L=%2.2f P=%2.2f", att_type_text[att.type], att_mode_text[att.mode], att.x, att.y, att.range, att.linearity, att.multiplier);
	rectfill(canvas, xx2, yy, xx2+16, yy+fnt.TextHeight(), makecol(att.r, att.g, att.b));
	rect(canvas, xx2, yy, xx2+16, yy+fnt.TextHeight(), makecol(0,0,0));
	fnt.GUITextout(canvas, buf, Point(xx, yy), fg, bg, tm, 0);
}



MainDialog::MainDialog() : Dialog() {
	srand(time(0));

	desktop.Resize(SCREEN_W, SCREEN_H);
	Add(desktop);

	imgWidget.image = &image;
	int x = 240;
	int y = 8;
	int w = SCREEN_W - x - 8;
	int h = w*3/4;

	imgWidget.Shape(x, y, w, h);
	Add(imgWidget);

	y += h+8;

	int bw = w/6;

	btnNew.Setup(x, y, bw-4, 20, 0, 0, "Clear");
	btnNew.SetCallbackID(WCOL_MSG_NEW);
	Add(btnNew);

	btnLoad.Setup(x+bw, y, bw-4, 20, 0, 0, "Load");
	btnLoad.SetCallbackID(WCOL_MSG_LOAD);
	Add(btnLoad);

	btnSave.Setup(x+2*bw, y, bw-4, 20, 0, 0, "Save");
	btnSave.SetCallbackID(WCOL_MSG_SAVE);
	Add(btnSave);

	btnExport.Setup(x+3*bw, y, bw-4, 20, 0, 0, "Export");
	btnExport.SetCallbackID(WCOL_MSG_EXPORT);
	Add(btnExport);

	btnRandom.Setup(x+4*bw, y, bw-4, 20, 0, 0, "Random");
	btnRandom.SetCallbackID(WCOL_MSG_RANDOM);
	Add(btnRandom);

	btnExit.Setup(x+5*bw, y, bw, 20, 0, D_EXIT, "Exit");
	Add(btnExit);

	w = x - 12;
	x = 4;
	y = 4;

	lblAttractors.Shape(x, y, w, 16);
	lblAttractors.SetText("Attractors");
	Add(lblAttractors);

	lstAttractors.Shape(x, y+=16, w, SCREEN_H-304);
	Add(lstAttractors);

	btnAddAttractor.Setup(x, y+=(lstAttractors.h()+4), w, 18, 0, 0, "Add");
	btnAddAttractor.SetCallbackID(WCOL_MSG_ADD_ATTRACTOR);
	Add(btnAddAttractor);

	btnRemoveAttractor.Setup(x, y+=20, w, 18, 0, 0, "Remove");
	btnRemoveAttractor.SetCallbackID(WCOL_MSG_REMOVE_ATTRACTOR);
	Add(btnRemoveAttractor);

	y += 20;
	btnMoveUp.Setup(x, y, w/2-2, 18, 0, 0, "Move up");
	btnMoveUp.SetCallbackID(WCOL_MSG_MOVE_UP);
	Add(btnMoveUp);
	btnMoveDown.Setup(x+w/2+2, y, w/2-2, 18, 0, 0, "Move down");
	btnMoveDown.SetCallbackID(WCOL_MSG_MOVE_DOWN);
	Add(btnMoveDown);

	btnDuplicate.Setup(x, y+=20, w, 18, 0, 0, "Duplicate");
	btnDuplicate.SetCallbackID(WCOL_MSG_DUPLICATE);
	Add(btnDuplicate);


	btnColourAttractor.Setup(x, y+=20, w, 18, 0, 0, "Colour");
	btnColourAttractor.SetCallbackID(WCOL_MSG_COLOUR_ATTRACTOR);
	Add(btnColourAttractor);

	y += 20;
	lblAttractorType.Setup(x, y, w/4, 18, 0, 0, "Type");
	Add(lblAttractorType);

	lstAttractorType.Shape(x+w/4, y, 3*w/4, 18);
	const char *att_type_text[] = { "GLOBAL", "POINT", "HBAR", "VBAR" };
	for (int i=0; i<4; i++) {
		lstAttractorType.list.InsertItem(new ListItemString(att_type_text[i]), i);
	}
	lstAttractorType.list.Select(0);
	lstAttractorType.list.h(90);
	Add(lstAttractorType);


	y += 20;
	lblAttractorMode.Setup(x, y, w/4, 18, 0, 0, "Mode");
	Add(lblAttractorMode);

	lstAttractorMode.Shape(x+w/4, y, 3*w/4, 18);
	const char *att_mode_text[] = { "ATTRACT", "ABSORB" };
	for (int i=0; i<2; i++) {
		lstAttractorMode.list.InsertItem(new ListItemString(att_mode_text[i]), i);
	}
	lstAttractorMode.list.Select(0);
	lstAttractorMode.list.h(90);
	Add(lstAttractorMode);

	y += 20;
	lblAttractorPos.Setup(x, y, w/4, 18, 0, 0, "Position");
	Add(lblAttractorPos);

	txtAttractorPosX.Setup(x+w/4, y, (3*w/4)/2-2, 18, 0, 0, "0");
	txtAttractorPosY.Setup(x+w/4+(3*w/4)/2+1, y, (3*w/4)/2-2, 18, 0, 0, "0");
	txtAttractorPosX.SetCallbackID(WCOL_MSG_ATTRACTOR_POSX);
	txtAttractorPosY.SetCallbackID(WCOL_MSG_ATTRACTOR_POSY);
	Add(txtAttractorPosX);
	Add(txtAttractorPosY);

	y += 20;
	lblAttractorRange.Setup(x, y, w/4, 18, 0, 0, "Range");
	Add(lblAttractorRange);

	txtAttractorRange.Setup(x+w/4, y, 3*w/4, 18, 0, 0, "0");
	txtAttractorRange.SetCallbackID(WCOL_MSG_ATTRACTOR_RANGE);
	Add(txtAttractorRange);

	y += 20;
	lblAttractorLinearity.Setup(x, y, w/4, 18, 0, 0, "Linearity");
	Add(lblAttractorLinearity);

	sldAttractorLinearity.Setup(x + w/4, y, 3*w/4-48, 18, 0, 0, 0, 1024, 256, 1);
	Add(sldAttractorLinearity);

	txtAttractorLinearity.Setup(x+w-44, y, 40, 18, 0, 0, "1.0");
	txtAttractorLinearity.Disable();
	Add(txtAttractorLinearity);

	y += 20;
	lblAttractorMultiplier.Setup(x, y, w/4, 18, 0, 0, "Multiplier");
	Add(lblAttractorMultiplier);

	sldAttractorMultiplier.Setup(x + w/4, y, 3*w/4-48, 18, 0, 0, 0, 1024, 256, 1);
	Add(sldAttractorMultiplier);

	txtAttractorMultiplier.Setup(x+w-44, y, 40, 18, 0, 0, "1.0");
	txtAttractorMultiplier.Disable();
	Add(txtAttractorMultiplier);

	y += 32;
	lblAuthor.Setup(x, y, w/4, 18, 0, 0, "Author");
	Add(lblAuthor);
	txtAuthor.Setup(x+w/4, y, 3*w/4, 18, 0, 0, "", 64);
	txtAuthor.SetCallbackID(WCOL_MSG_UPDATE_TEXTS);
	Add(txtAuthor);

	y += 20;
	lblTitle.Setup(x, y, w/4, 18, 0, 0, "Title");
	Add(lblTitle);
	txtTitle.Setup(x+w/4, y, 3*w/4, 18, 0, 0, "", 64);
	txtTitle.SetCallbackID(WCOL_MSG_UPDATE_TEXTS);
	Add(txtTitle);

	char win_title[1024];
	usprintf(win_title, "Dynamic Colour Gradient Generator v%d.%02d", WCOL_VERSION_MAJOR, WCOL_VERSION_MINOR);
	set_window_title(win_title);

	char credits[1024];
	usprintf(credits, "%s - GUI by Miran Amon, gradient algorithm by Dennis Busch", win_title);
	lblCredits.ClearFlag(D_AUTOSIZE);
	lblCredits.SetVAlignment(1);
	lblCredits.SetWordWrap(true);
	lblCredits.Setup(SCREEN_W/2, SCREEN_H - 60, SCREEN_W/2-8, 56, 0, 0, credits, 1);
	Add(lblCredits);

	MakeFullPath(path, "");
	escapeExits = false;
}


void MainDialog::HandleEvent(Widget &obj, int msg, int arg1, int arg2) {
	Dialog::HandleEvent(obj, msg, arg1, arg2);

	switch (msg) {
		case MSG_SCROLL:
			if (obj == lstAttractors)			OnSelectAttractor(arg1);	else
			if (obj == sldAttractorLinearity)	OnLinearityScroll(arg1);	else
			if (obj == sldAttractorMultiplier)	OnMultiplierScroll(arg1);
		break;

		case MSG_ACTIVATE:
			if (obj == lstAttractorType)		OnAttractorType(arg1);		else
			if (obj == lstAttractorMode)		OnAttractorMode(arg1);
		break;

		case MSG_LRELEASE:
		case MSG_CHAR:
			if (obj == sldAttractorLinearity)	OnAttractorLinearity();		else
			if (obj == sldAttractorMultiplier)	OnAttractorMultiplier();
		break;

		case WCOL_MSG_ADD_ATTRACTOR:		OnAddAttractor();		break;
		case WCOL_MSG_REMOVE_ATTRACTOR:		OnRemoveAttractor();	break;
		case WCOL_MSG_COLOUR_ATTRACTOR:		OnColourAttractor();	break;
		case WCOL_MSG_ATTRACTOR_TYPE1:		OnAttractorType(0);		break;
		case WCOL_MSG_ATTRACTOR_TYPE2:		OnAttractorType(1);		break;
		case WCOL_MSG_ATTRACTOR_TYPE3:		OnAttractorType(2);		break;
		case WCOL_MSG_ATTRACTOR_TYPE4:		OnAttractorType(3);		break;
		case WCOL_MSG_ATTRACTOR_POSX:		OnAttractorUpdate();	break;
		case WCOL_MSG_ATTRACTOR_POSY:		OnAttractorUpdate();	break;
		case WCOL_MSG_ATTRACTOR_RANGE:		OnAttractorUpdate();	break;
		case WCOL_MSG_UPDATE_TEXTS:			OnUpdateTexts();		break;
		case WCOL_MSG_NEW:					OnNew();				break;
		case WCOL_MSG_SAVE:					OnSave();				break;
		case WCOL_MSG_LOAD:					OnLoad();				break;
		case WCOL_MSG_EXPORT:				OnExport();				break;
		case WCOL_MSG_RANDOM:				OnRandom();				break;
		case WCOL_MSG_UPDATE_GUI:			OnUpdateGUI();			break;
		case WCOL_MSG_MOVE_UP:				OnMoveUp();				break;
		case WCOL_MSG_MOVE_DOWN:			OnMoveDown();			break;
		case WCOL_MSG_DUPLICATE:			OnDuplicate();			break;
		case WCOL_MSG_PLACE_ATTRACTOR:		OnPlaceAttractor(arg1, arg2);		break;
		case WCOL_MSG_SET_ATTRACTOR_RANGE:	OnSetAttractorRange(arg1, arg2);	break;
	};
}


void MainDialog::OnAddAttractor() {
	image.AddAttractor();
	OnUpdateGUI();
	lstAttractors.Select(lstAttractors.GetItemCount()-1);
	lstAttractors.ScrollToItem(lstAttractors.GetItemCount()-1);
	OnSelectAttractor(lstAttractors.GetSelectedIndex());
	imgWidget.OnChanged();
}


void MainDialog::OnRemoveAttractor() {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		image.RemoveAttractor(sel);
		OnUpdateGUI();
		imgWidget.OnChanged();
		OnSelectAttractor(lstAttractors.GetSelectedIndex());
		imgWidget.OnChanged();
	}
}


void MainDialog::OnColourAttractor() {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		int old = makecol(image.GetAttractor(sel).r, image.GetAttractor(sel).g, image.GetAttractor(sel).b);
		ColorSelect dlg("Select colour", old);
		int col = dlg.Popup(this);
		if (col != -1) {
			image.GetAttractor(sel).r = getr(col);
			image.GetAttractor(sel).g = getg(col);
			image.GetAttractor(sel).b = getb(col);
			OnUpdateGUI();
			imgWidget.OnChanged();
		}
	}
}


void MainDialog::OnAttractorType(int type) {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		image.GetAttractor(sel).type = type;
		OnUpdateGUI();
		imgWidget.OnChanged();
	}
}


void MainDialog::OnAttractorMode(int mode) {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		image.GetAttractor(sel).mode = mode;
		OnUpdateGUI();
		imgWidget.OnChanged();
	}
}


void MainDialog::OnAttractorLinearity() {
	int sel = lstAttractors.GetSelectedIndex();
	float l = sldAttractorLinearity.GetPosition() / 256.0;
	if (sel >= 0 && sel < image.GetAttractorCount() && image.GetAttractor(sel).linearity != l) {
		image.GetAttractor(sel).linearity = l;
		OnUpdateGUI();
		imgWidget.OnChanged();
	}
}


void MainDialog::OnLinearityScroll(int l) {
	txtAttractorLinearity.SetNumber(l/256.0f);
}


void MainDialog::OnAttractorMultiplier() {
	int sel = lstAttractors.GetSelectedIndex();
	float p = sldAttractorMultiplier.GetPosition() / 256.0f;
	if (sel >= 0 && sel < image.GetAttractorCount() && image.GetAttractor(sel).multiplier != p) {
		image.GetAttractor(sel).multiplier = p;
		OnUpdateGUI();
		imgWidget.OnChanged();
	}
}


void MainDialog::OnMultiplierScroll(int p) {
	txtAttractorMultiplier.SetNumber(p/256.0f);
}


void MainDialog::OnAttractorUpdate() {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		image.GetAttractor(sel).x = txtAttractorPosX.GetDouble();
		image.GetAttractor(sel).y = txtAttractorPosY.GetDouble();
		image.GetAttractor(sel).range = txtAttractorRange.GetDouble();
		OnUpdateGUI();
		imgWidget.OnChanged();
	}
}


void MainDialog::OnUpdateTexts() {
	image.SetAuthor(std::string(txtAuthor.GetText()));
	image.SetTitle(std::string(txtTitle.GetText()));
}


void MainDialog::OnMoveUp() {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 1) {
		image.MoveAttractor(sel, -1);
		OnUpdateGUI();
		lstAttractors.Select(sel-1);
		lstAttractors.ScrollToItem(sel-1);
		OnSelectAttractor(lstAttractors.GetSelectedIndex());
		imgWidget.OnChanged();
	}
}


void MainDialog::OnMoveDown() {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()-1) {
		image.MoveAttractor(sel, 1);
		OnUpdateGUI();
		lstAttractors.Select(sel+1);
		lstAttractors.ScrollToItem(sel+1);
		OnSelectAttractor(lstAttractors.GetSelectedIndex());
		imgWidget.OnChanged();
	}
}


void MainDialog::OnDuplicate() {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		image.DuplicateAttractor(sel);
		OnUpdateGUI();
		lstAttractors.Select(lstAttractors.GetItemCount()-1);
		lstAttractors.ScrollToItem(lstAttractors.GetItemCount()-1);
		OnSelectAttractor(lstAttractors.GetSelectedIndex());
		imgWidget.OnChanged();
	}
}


void MainDialog::OnNew() {
	image.Clear();
	imgWidget.OnChanged();
	OnUpdateGUI();
}


void MainDialog::OnSave() {
	FileSelect dlg("Save file", path, "WCol Gradients (*.wcol)|All files (*.*)");
	char *selPath = dlg.Popup(this);
	if (selPath) {
		OnUpdateTexts();

		ustrcpy(path, selPath);
		char *ext = get_extension(selPath);
		if (ustrlen(ext) == 0) {
			ustrcat(path, ".wcol");
		}
		image.Save(path);
		imgWidget.OnChanged();
	}
}


void MainDialog::OnLoad() {
	FileSelect dlg("Load file", path, "WCol Gradients (*.wcol)|All files (*.*)");
	char *selPath = dlg.Popup(this);
	if (selPath) {
		ustrcpy(path, selPath);
		if (image.Load(path) != 0) {
			MAS::MessageBox msg("Error", "Couldn't load file:", path, 0, "OK");
			msg.Popup(Root());
		}
		imgWidget.OnChanged();
		OnUpdateGUI();

		if (image.GetAttractorCount() > 0) {
			lstAttractors.Select(0);
			OnSelectAttractor(0);
		}
		else {
			lstAttractors.Select(-1);
			OnSelectAttractor(-1);
		}
	}
}


void MainDialog::OnExport() {
	Window wnd;
	Label lblSize;
	ComboBox lstSize;
	Button btnOK;
	Button btnCancel;

	wnd.GetClientArea()->Resize(200, 68);

	lblSize.Setup(8, 8, 60, 16, 0, 0, "size", 0);
	wnd.Add(lblSize);

	int w[] = { 640, 800, 1024, 1280, 1600 };
	int h[] = { 480, 600,  768,  960, 1200 };
	for (int i=0; i<5; i++) {
		char tmp[32];
		usprintf(tmp, "%dx%d", w[i], h[i]);
		lstSize.list.InsertItem(new ListItemString(tmp), i);
	}
	lstSize.list.Select(0);
	lstSize.Shape(72, 8, 120, 24);
	lstSize.list.h(108);
	wnd.Add(lstSize);

	btnOK.Setup(8, 40, 88, 24, 0, D_EXIT, "OK");
	btnCancel.Setup(104, 40, 88, 24, 0, D_EXIT, "Cancel");
	wnd.Add(btnOK);
	wnd.Add(btnCancel);

	wnd.title.SetText("Bitmap size");
	wnd.Resize(200,68);
	wnd.Centre();
	if (wnd.Popup(this) != &btnOK) {
		return;
	}

	replace_extension(path, path, "tga", 1024);
	FileSelect dlg("Export file", path, "TGA images (*.tga)|All files (*.*)");
	char *selPath = dlg.Popup(this);
	if (selPath) {
		ustrcpy(path, selPath);
		char *ext = get_extension(selPath);
		if (ustrlen(ext) == 0) {
			ustrcat(path, ".tga");
		}

		Bitmap bmp(w[lstSize.list.Selection()], h[lstSize.list.Selection()]);
		image.Render(bmp);
		bmp.Save(path);
	}
}


void MainDialog::OnRandom() {
	image.Clear();

	int n = rand()%16+4;
	for (int i=0; i<n; i++) {
		image.AddAttractor();
		image.GetAttractor(i).type = rand()%4;
		image.GetAttractor(i).mode = rand()%2;
		image.GetAttractor(i).x = (float)(rand()%100)/100.0;
		image.GetAttractor(i).y = (float)(rand()%100)/100.0;
		image.GetAttractor(i).range = (float)(rand()%200)/100.0;
		image.GetAttractor(i).r = rand()%256;
		image.GetAttractor(i).g = rand()%256;
		image.GetAttractor(i).b = rand()%256;
		image.GetAttractor(i).linearity = (float)(rand()%400)/100.0;
		image.GetAttractor(i).multiplier = (float)(rand()%400)/100.0;
	}

	image.SetAuthor("Random number generator");
	image.SetTitle("Random image");

	imgWidget.OnChanged();
	OnUpdateGUI();
	lstAttractors.Select(0);
	OnSelectAttractor(0);
}


void MainDialog::OnUpdateGUI() {
	int sel = lstAttractors.GetSelectedIndex();
	lstAttractors.DeleteAllItems();
	for (int i=0; i<image.GetAttractorCount(); i++) {
		WColImage::Attractor a = image.GetAttractor(i);
		lstAttractors.InsertItem(new AttractorItem(a));
	}
	if (sel < 0 || sel >= image.GetAttractorCount()) {
		sel = 0;
	}
	lstAttractors.Select(sel);
	lstAttractors.ScrollToItem(sel);
	txtAuthor.SetText(image.GetAuthor().c_str());
	txtTitle.SetText(image.GetTitle().c_str());
}


void MainDialog::OnSelectAttractor(int i) {
	if (i >= image.GetAttractorCount()) return;

	if (i >= 0) {
		WColImage::Attractor a = image.GetAttractor(i);

		lstAttractorType.list.Select(a.type);
		lstAttractorType.editBox.SetText(lstAttractorType.list.GetSelectedItem()->GetText());

		lstAttractorMode.list.Select(a.mode);
		lstAttractorMode.editBox.SetText(lstAttractorMode.list.GetSelectedItem()->GetText());

		txtAttractorPosX.SetNumber(a.x);
		txtAttractorPosY.SetNumber(a.y);
		txtAttractorRange.SetNumber(a.range);

		sldAttractorLinearity.SetPosition((int)(a.linearity*256.0));
		txtAttractorLinearity.SetNumber(a.linearity);

		sldAttractorMultiplier.SetPosition((int)(a.multiplier*256.0));
		txtAttractorMultiplier.SetNumber(a.multiplier);

		imgWidget.selectedAttractor = i;
		imgWidget.Redraw();
	}
}


void MainDialog::OnPlaceAttractor(int x, int y) {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		WColImage::Attractor a = image.GetAttractor(sel);
		if (a.type != CA_GLOBAL) {
			float fx = (float)x / (float)imgWidget.w();
			float fy = (float)y / (float)imgWidget.h();
			image.GetAttractor(sel).x = fx;
			image.GetAttractor(sel).y = fy;
			OnUpdateGUI();
			txtAttractorPosX.SetNumber(fx);
			txtAttractorPosY.SetNumber(fy);
			imgWidget.OnChanged();
		}
	}
}


void MainDialog::OnSetAttractorRange(int x, int y) {
	int sel = lstAttractors.GetSelectedIndex();
	if (sel >= 0 && sel < image.GetAttractorCount()) {
		WColImage::Attractor a = image.GetAttractor(sel);
		if (a.type != CA_GLOBAL) {
			float fx = (float)x / (float)imgWidget.w();
			float fy = (float)y / (float)imgWidget.h();

			if (a.type == CA_POINT) {
				fx -= a.x;
				fy -= a.y;
				image.GetAttractor(sel).range = sqrt(fx*fx + fy*fy);
			}
			else if (a.type == CA_VBAR) {
				image.GetAttractor(sel).range = fabs(fx - a.x);
			}
			else if (a.type == CA_HBAR) {
				image.GetAttractor(sel).range = fabs(fy - a.y);
			}
			OnUpdateGUI();
			txtAttractorRange.SetNumber(image.GetAttractor(sel).range);
			imgWidget.OnChanged();
		}
	}
}
