/*
 * Copyright Daniel Jones 2016-2018
 *
 * This file is part of RGBController.
 *
 * RGBController is free software: you can redistribute it and/or modifiy
 * 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.
 *
 * RGBController 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 RGBController.  If not, see .
 */
#include "controllerwindow.h" 
controllerWindow::controllerWindow(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::controllerWindow)
{
	/* this is our setup function, we set things up here */
	ui->setupUi(this);
	info_log("RGB Controller started");
	/* serial */
	ser = new QSerialPort(this);
	connect(ser, &QSerialPort::readyRead, this, &controllerWindow::read);
	ptimer = new QTimer(this);
	connect(ptimer, SIGNAL(timeout()), this, SLOT(ping()));
	connect(&ircbot, SIGNAL(sendcmd(QString)), this, SLOT(cmdrecv(QString)));
	connect(&srv, SIGNAL(sendcmd(QString)), this, SLOT(cmdrecv(QString))); 
	pingtries = 0;
	irccon = false;
	pingcount = 0;
	server_started = false;
	/* disable buttons and widgets that should not be enabled yet, set slider values to 0 */
	ui->disconnect_button->setEnabled(false);
	ui->connect_button->setEnabled(false);
	ui->red_button->setEnabled(false);
	ui->green_button->setEnabled(false);
	ui->blue_button->setEnabled(false);
	ui->off_button->setEnabled(false);
	ui->reload_preset_button->setEnabled(false);
	ui->set_preset_button->setEnabled(false);
	ui->preset_save_button->setEnabled(false);
	ui->preset_delete_button->setEnabled(false);
	ui->preset_name_textbox->setEnabled(false);
	ui->presets_dropdown->setEnabled(false);
	ui->r_slider->setEnabled(false);
	ui->g_slider->setEnabled(false);
	ui->b_slider->setEnabled(false);
	ui->r_slider->setValue(0);
	ui->g_slider->setValue(0);
	ui->b_slider->setValue(0);
	ui->red_fade_button->setEnabled(false);
	ui->green_fade_button->setEnabled(false);
	ui->blue_fade_button->setEnabled(false);
	ui->speed_button->setEnabled(false);
	ui->rfrom->setEnabled(false);
	ui->rto->setEnabled(false);
	ui->gfrom->setEnabled(false);
	ui->gto->setEnabled(false);
	ui->bfrom->setEnabled(false);
	ui->bto->setEnabled(false);
	ui->r_speed_slider->setEnabled(false);
	ui->g_speed_slider->setEnabled(false);
	ui->b_speed_slider->setEnabled(false);
	ui->speed_slider->setEnabled(false);
	ui->r_speed_slider->setValue(0);
	ui->g_speed_slider->setValue(0);
	ui->b_speed_slider->setValue(0);
	ui->speed_slider->setValue(0);
	presetsfile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/presets.txt";
	tempfile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tmp.file";
	/* set some crucial ints */
	preset_index = 0, r = 0, g = 0, b = 0;
	/* populate our serial port dropdown box */
	populate_serial_list();
	/* load our presets file */
	load_presets();
}
controllerWindow::~controllerWindow()
{
	/* this function will end our program and clean up */
	delete ui;
}
void controllerWindow::info_log(QString text)
{
	/* this function is used to append logging information to our text area */
	ui->info_log_textarea->append(text);
}
void controllerWindow::populate_serial_list()
{
	/*
	 * things to be done here:
	 * - clear anything inside our serial dropdown box
	 * - re-populate the serial dropdown box
	 */
	/* remove anything in the list */
	ui->arduino_port_dropdown->clear();
	/* populate the list */
	for (int x = 0; x < QSerialPortInfo::availablePorts().size(); x++)
	{
		if (!QSerialPortInfo::availablePorts().at(x).description().isEmpty())
			ui->arduino_port_dropdown->addItem(QSerialPortInfo::availablePorts().at(x).portName());
	}
	if (!ui->arduino_port_dropdown->itemText(0).isEmpty())
	{
		ui->connect_button->setEnabled(true);
		info_log("Serial port list populated");
	} else
	{
		info_log("No serial port found!");
		ui->arduino_port_dropdown->addItem("None");
	}
}
void controllerWindow::load_presets()
{
	/*
	 * this function will load presets from file and put them into a drop down box
	 * How i plan for it to work:
	 * -> load file
	 * -> parse line by line
	 * -> split line at delimeter
	 * -> add [0] to dropdown (name), add [1] to an array that stores all the values
	 */
	QFile inputFile(presetsfile);
	if (inputFile.open(QIODevice::ReadOnly))
	{
		QTextStream in(&inputFile);
		while (!in.atEnd())
		{
			QString line = in.readLine();
			if (line.contains("="))
			{
				ui->presets_dropdown->addItem(line.split("=")[0]);
				presets.append(line.split("=")[1]);
			}
		}
		inputFile.close();
		info_log("Presets loaded");
	} else
	{
		show_msgbox("Unable to load the presets file.\n");
		info_log("Presets file not found.");
	}
}
void controllerWindow::save_preset(QString name)
{
	/* this function will save our preset to file */
	if (!QDir("Folder").exists())
		QDir().mkdir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
	QFile file(presetsfile);
	if(!file.open(QIODevice::Append)) {
		show_msgbox("Fatal error opening presets for appending text.");
	} else
	{
		QTextStream stream(&file);
		stream << name << "=" << ui->r_slider->value() << "," << ui->g_slider->value() << "," << ui->b_slider->value() << endl;
		file.close();
		info_log("Saved preset: " + name);
		/* things to fix the presets list */
		ui->presets_dropdown->clear();
		presets.clear();
		preset_index = 0;
		load_presets();
	}
}
void controllerWindow::delete_preset(QString name)
{
	/*
	 * here we will delete a preset from the presets file
	 * process:
	 * retrieve preset name
	 * go through preset file line by line and write it to a seperate tmp file
	 * if the [0] of split('=') equals the preset name, don't write it to the tmp file
	 * delete the old preset file
	 * rename the tmp file to presets.txt
	 */
	int ret = show_question_box("Are you sure want to delete this preset?", "This process cannot be reverted.");
	switch (ret) 
	{
		case QMessageBox::Ok:
			{
				info_log("deleting preset: " + name);
				QFile file(tempfile);
				if(!file.open(QIODevice::Append)) {
					show_msgbox("Fatal error opening temp file for writing");
				} else
				{
					QTextStream stream(&file);
					//stream << "test tmp file" << endl;
					for (int x = 0; x < ui->presets_dropdown->count(); x++)
					{
						if(name != ui->presets_dropdown->itemText(x))
						{
							stream << ui->presets_dropdown->itemText(x) << "=" << presets.at(x) << endl;
						}
					}
					file.close();
					/* remove the current presets file then rename the temp file to presets.txt */
					QFile::remove(presetsfile);
					QFile::rename(tempfile, presetsfile);
					/* reload presets into memory and clear the drop down box */
					ui->presets_dropdown->clear();
					presets.clear();
					preset_index = 0;
					load_presets();
					break;
				}
			}
		case QMessageBox::Cancel:
			show_msgbox("Preset was not deleted.");
			break;
		default:
			info_log("Unknown response received.");
			break;
	}
}
void controllerWindow::serial_rgb_change(int r, int g, int b)
{
	/* un used function */
	rgb_change(r, g, b);
}
void controllerWindow::show_msgbox(QString message)
{
	/* show message box to the user */
	QMessageBox msgbox;
	msgbox.setText(message);
	msgbox.exec();
}
int controllerWindow::show_question_box(QString message, QString omessage)
{
	/*
	 * shows a question box to the user with ok/cancel, returns result
	 */
	QMessageBox qbox;
	qbox.setText(message);
	qbox.setInformativeText(omessage);
	qbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
	qbox.setDefaultButton(QMessageBox::Cancel);
	return qbox.exec();
}
/*
 *
 * button and widget functions below
 *
 */
void controllerWindow::on_connect_button_clicked()
{
	/*
	 * here we need to call our serial port connect function
	 * if our connection is sucessful we need to enable and disable a few buttons and widgets
	 * we must also change our rgb sliders etc
	 */
	pingtries = 0;
	port = ui->arduino_port_dropdown->currentText();
	if (serial_connect(port))
	{ // if true we are connected
		info_log("Connection established");
		ui->arduino_status_label->setText("Connected");
		/* now we need to disabled and enable some buttons and objects */
		ui->disconnect_button->setEnabled(true);
		ui->connect_button->setEnabled(false);
		ui->red_button->setEnabled(true);
		ui->green_button->setEnabled(true);
		ui->blue_button->setEnabled(true);
		ui->off_button->setEnabled(true);
		ui->reload_preset_button->setEnabled(true);
		ui->set_preset_button->setEnabled(true);
		ui->preset_save_button->setEnabled(true);
		ui->preset_delete_button->setEnabled(true);
		ui->preset_name_textbox->setEnabled(true);
		ui->presets_dropdown->setEnabled(true);
		ui->r_slider->setEnabled(true);
		ui->g_slider->setEnabled(true);
		ui->b_slider->setEnabled(true);
		ui->refresh_port_button->setEnabled(false);
		ui->arduino_port_dropdown->setEnabled(false);
		ui->red_fade_button->setEnabled(true);
		ui->green_fade_button->setEnabled(true);
		ui->blue_fade_button->setEnabled(true);
		ui->speed_button->setEnabled(true);
		ui->rfrom->setEnabled(true);
		ui->rto->setEnabled(true);
		ui->gfrom->setEnabled(true);
		ui->gto->setEnabled(true);
		ui->bfrom->setEnabled(true);
		ui->bto->setEnabled(true);
		ui->r_speed_slider->setEnabled(true);
		ui->g_speed_slider->setEnabled(true);
		ui->b_speed_slider->setEnabled(true);
		ui->speed_slider->setEnabled(true);
		ptimer->start(60000);
	} else
	{
		info_log("Unable to connect (port in use?)");
	}
}
void controllerWindow::on_disconnect_button_clicked()
{
	/*
	 * here we will attempt to disconnect from our serial port
	 * we will also enable/disable some buttons and widgets and set slider values to 0
	 */
	if (serial_disconnect())
	{
		info_log("Disconnected from serial port");
		ui->arduino_status_label->setText("Disconnected");
		ui->disconnect_button->setEnabled(false);
		ui->connect_button->setEnabled(true);
		ui->red_button->setEnabled(false);
		ui->green_button->setEnabled(false);
		ui->blue_button->setEnabled(false);
		ui->off_button->setEnabled(false);
		ui->reload_preset_button->setEnabled(false);
		ui->preset_save_button->setEnabled(false);
		ui->preset_delete_button->setEnabled(false);
		ui->preset_name_textbox->setEnabled(false);
		ui->set_preset_button->setEnabled(false);
		ui->presets_dropdown->setEnabled(false);
		ui->refresh_port_button->setEnabled(true);
		ui->arduino_port_dropdown->setEnabled(true);
		ui->r_slider->setEnabled(false);
		ui->g_slider->setEnabled(false);
		ui->b_slider->setEnabled(false);
		ui->r_slider->setValue(0);
		ui->g_slider->setValue(0);
		ui->b_slider->setValue(0);
		ui->red_fade_button->setEnabled(false);
		ui->green_fade_button->setEnabled(false);
		ui->blue_fade_button->setEnabled(false);
		ui->speed_button->setEnabled(false);
		ui->rfrom->setEnabled(false);
		ui->rto->setEnabled(false);
		ui->gfrom->setEnabled(false);
		ui->gto->setEnabled(false);
		ui->bfrom->setEnabled(false);
		ui->bto->setEnabled(false);
		ui->r_speed_slider->setEnabled(false);
		ui->g_speed_slider->setEnabled(false);
		ui->b_speed_slider->setEnabled(false);
		ui->speed_slider->setEnabled(false);
		ui->r_speed_slider->setValue(0);
		ui->g_speed_slider->setValue(0);
		ui->b_speed_slider->setValue(0);
		ui->speed_slider->setValue(0);
	} else
	{
		/* I have no clue how we'd get here */
		info_log("Cannot disconnect");
	}
}
void controllerWindow::on_refresh_port_button_clicked()
{
	populate_serial_list();
}
void controllerWindow::on_reload_preset_button_clicked()
{
	ui->presets_dropdown->clear();
	presets.clear();
	preset_index = 0;
	load_presets();
}
void controllerWindow::on_r_slider_valueChanged(int value)
{
	r = value;
	//serial_rgb_change(r, g, b);
	send("red=" + QString::number(r));
}
void controllerWindow::on_g_slider_valueChanged(int value)
{
	g = value;
	//serial_rgb_change(r, g, b);
	send("green=" + QString::number(g));
}
void controllerWindow::on_b_slider_valueChanged(int value)
{
	b = value;
	//serial_rgb_change(r, g, b);
	send("blue=" + QString::number(b));
}
void controllerWindow::on_red_button_clicked()
{
	ui->r_slider->setValue(255);
	ui->g_slider->setValue(0);
	ui->b_slider->setValue(0);
}
void controllerWindow::on_green_button_clicked()
{
	ui->r_slider->setValue(0);
	ui->g_slider->setValue(255);
	ui->b_slider->setValue(0);
}
void controllerWindow::on_blue_button_clicked()
{
	ui->r_slider->setValue(0);
	ui->g_slider->setValue(0);
	ui->b_slider->setValue(255);
}
void controllerWindow::on_off_button_clicked()
{
	ui->r_slider->setValue(1);
	ui->g_slider->setValue(1);
	ui->b_slider->setValue(1);
	ui->r_slider->setValue(0);
	ui->g_slider->setValue(0);
	ui->b_slider->setValue(0);
	send("off");
}
void controllerWindow::on_set_preset_button_clicked()
{
	if (!presets.empty())
	{
		QString tempstore = presets.at(preset_index);
		QStringList temparray = tempstore.split(",");
		//info_log("Preset selected: " + temparray[0] + " " + temparray[1] + " " + temparray[2]);
		ui->r_slider->setValue(temparray[0].toInt());
		ui->g_slider->setValue(temparray[1].toInt());
		ui->b_slider->setValue(temparray[2].toInt());
	} else
		show_msgbox("There are no loaded presets!");
}
void controllerWindow::on_presets_dropdown_currentIndexChanged(int index)
{
	preset_index = index;
}
void controllerWindow::on_preset_save_button_clicked()
{
	QString tempname = ui->preset_name_textbox->text();
	if (tempname.contains("="))
	{
		show_msgbox("Cannot save a preset with a name that contains an '='.");
	} else if (tempname.isEmpty())
	{
		show_msgbox("Cannot save a preset without a name.");
	} else
	{
		save_preset(tempname);
	}
	ui->preset_name_textbox->clear();
}
void controllerWindow::on_preset_delete_button_clicked()
{
	delete_preset(ui->presets_dropdown->currentText());
}
void controllerWindow::on_red_fade_button_clicked()
{
	send("redfade");
}
void controllerWindow::on_green_fade_button_clicked()
{
	send("greenfade");
}
void controllerWindow::on_blue_fade_button_clicked()
{
	send("bluefade");
}
void controllerWindow::on_speed_button_clicked()
{
	send("speed=" + QString::number(ui->speed_slider->value()));
}
void controllerWindow::on_r_speed_slider_valueChanged(int value)
{
	send("rspeed=" + QString::number(value));
}
void controllerWindow::on_g_speed_slider_valueChanged(int value)
{
	send("gspeed=" + QString::number(value));
}
void controllerWindow::on_b_speed_slider_valueChanged(int value)
{
	send("bspeed=" + QString::number(value));
}
void controllerWindow::on_rfrom_valueChanged(int arg1)
{
	send("rf=" + QString::number(arg1));
	//ui->r_slider->setValue(arg1);
}
void controllerWindow::on_rto_valueChanged(int arg1)
{
	send("rt=" + QString::number(arg1));
}
void controllerWindow::on_gfrom_valueChanged(int arg1)
{
	send("gf=" + QString::number(arg1));
	//ui->g_slider->setValue(arg1);
}
void controllerWindow::on_gto_valueChanged(int arg1)
{
	send("gt=" + QString::number(arg1));
}
void controllerWindow::on_bfrom_valueChanged(int arg1)
{
	send("bf=" + QString::number(arg1));
	//ui->b_slider->setValue(arg1);
}
void controllerWindow::on_bto_valueChanged(int arg1)
{
	send("bt=" + QString::number(arg1));
}
bool controllerWindow::serial_connect(QString port)
{
	/* this function will attempt a ser.connection if we are not already connected */
	if (!ser->isOpen())
	{
		ser->setPortName(port);
		ser->setBaudRate(QSerialPort::Baud9600);
		ser->setDataBits(QSerialPort::Data8);
		ser->setParity(QSerialPort::NoParity);
		ser->setStopBits(QSerialPort::OneStop);
		ser->setFlowControl(QSerialPort::NoFlowControl);
		ser->open(QIODevice::ReadWrite);
		ser->waitForBytesWritten(9000);
		if (ser->isWritable())
		{
			return true;
		}
	}
	return false;
}
bool controllerWindow::serial_disconnect()
{
	/* this function disconnects from the serial port if it is connected already */
	if (ser->isOpen())
	{
		ser->close();
		ptimer->stop();
		return true;
	}
	return false;
}
void controllerWindow::send(QString com)
{
	data = "";
	data.append(com + "\n");
	if (ser->isOpen())
		ser->write(data);
}
void controllerWindow::rgb_change(int r, int g, int b)
{
	// NOT USED FUNCTION
	/* here we send our rgb values to the serial port */
	data = "";
	/* the 0 is a hack, i need to look into it at some point */
	data.append("red=" + QString::number(r) + "," + QString::number(g) + "," + QString::number(b) + "\n");
	if (ser->isOpen())
		ser->write(data);
}
void controllerWindow::read()
{
	if (serdata[serdata.size() - 1] == '\n')
	{
		parse(QString(serdata));
		serdata = "";
	}
	else
		serdata = serdata + ser->readAll();
}
void controllerWindow::parse(QString message)
{
	qDebug() << message;
	if (message.contains("="))
	{
		QString command = message.split("=")[0].remove(QRegExp("[\\n\\t\\r]"));
		QString text = message.split("=")[1].remove(QRegExp("[\\n\\t\\r]"));
		if (command == "msgbox")
			show_msgbox(text);
		if (command == "ping")
		{
			qDebug() << "pong from arduino";
			tping = true;
		}
	}
}
void controllerWindow::on_serial_send_button_clicked()
{
	send(ui->serial_input->text()); 
}
// timer after ping send, if the timer timeout()'s, disconnect, claim no connection
void controllerWindow::ping()
{
	qDebug() << "sending ping";
	send("ping\n");
	tping = false;
	QTimer::singleShot(1000, this, SLOT(check_ping()));
	pingcount++;
	ui->ping_count_label->setText("Ping: " + QString::number(pingcount));
}
void controllerWindow::check_ping()
{
	if (tping)
	{
		qDebug() << "connection fine";
		tping = false;
		pingtries = 0;
	}
	else
	{
		if (pingtries >= 3)
		{
			qDebug() << "connection lost";
			on_disconnect_button_clicked();
			show_msgbox("Disconnected from Arduino.\nNo ping response after 3 tries.");
		} 
		else
		{
			pingtries++;
			qDebug() << "no ping response try" << pingtries;
		}
	}
}
void controllerWindow::on_irc_connect_button_clicked()
{
	if (irccon == false)
	{
		irccon = true;
		ircbot.setup(ui->irc_server_input->text(), ui->irc_bot_spinbox->value(), ui->irc_channel_input->text(), ui->irc_name_input->text());
		ui->irc_connect_button->setText("Disconnect");
	}
	else
	{
		irccon = false;
		ircbot.discon();
		ui->irc_connect_button->setText("Connect");
	}
}
void controllerWindow::on_server_start_button_clicked()
{
	if (!server_started)
	{
		if (srv.server_start())
		{
			server_started = true;
			ui->server_start_button->setText("Stop server");
			info_log("Server started");
		}
		else
		{
			info_log("Server could not start");
		}
	}
	else if (server_started)
	{
		if (srv.server_stop())
		{
			server_started = false;
			ui->server_start_button->setText("Start server");
			info_log("Server stopped");
		}
		else
		{
			info_log("Server not not be stopped");
		}
	}
}
void controllerWindow::cmdrecv(QString cmd)
{
	qDebug() << "received command:" << cmd;
	send(cmd);
}