// data_rw.cxx
// This file is part of galvani.
// Copyright (C) 2020-2024 Burkard Lutz <b.lutz@online.de>
//
// 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 2 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 <http://www.gnu.org/licenses/>.

#include <iostream>
#include <fstream>
#include <gtk/gtk.h>
#include <glib.h>
#include <gtkmm.h>
#include <charconv>
#include "data_rw.h"
#include <glibmm/i18n.h>

#define UI_FILE "src/data_rw.ui"

using namespace std;


void save_data()
{
	int i=0,j, end;
	double x,y;
	mw_data val;
	ofstream save_file;

	
	if (mw.size() == 0)
	{
		fehler (_("No data"));
		file_dialog->hide();
		return;
	}
	file_dialog->set_title(_("Save data"));
	file_dialog->set_action (Gtk::FILE_CHOOSER_ACTION_SAVE);
	
	file_choose_ok->set_label(_("Save"));
	file_dialog->set_current_folder(datadir);
	file_dialog->set_current_name("messdaten.dat");
	if (!filefilter_dat)
	{
		filefilter_dat = Gtk::FileFilter::create();
		filefilter_dat->add_pattern("*.dat");
	}
	file_dialog->set_filter(filefilter_dat);
	int result = file_dialog->run();
	
	if (result == Gtk::RESPONSE_CANCEL)
	{
		file_dialog->hide(); 
		return;
	}
    filename = file_dialog->get_filename();
	save_file.open (filename); //create file (CSV)
	if (!save_file.is_open())
	{
		fehler (_("Error writing file"));
		file_dialog->hide();
		return;
	}
	
	//write data
	try
	{
		save_file <<file_id<<"\n"; //file-id
		save_file <<"Datum der Messung: "<<project->datum<<"\n";
		save_file <<"Projekt-Titel: "<<project->title<<"\n";
		save_file <<"Messfunktion x: "<<project->funktion_x<<"\n";
		save_file <<"Messfunktion y: "<<project->funktion_y<<"\n";
		save_file <<"Einheit x: "<<project->unit_x<<"\n";
		save_file <<"Einheit y: "<<project->unit_y<<"\n";
		save_file <<"Daten x: "<<dmm[0].dmm_typ<<"\n";
		if (dmm[0].dmm_typ == DMM) save_file <<"Messgerät x: "<<dmm[0].dmm_name<<"\n";
		save_file <<"Daten y: "<<dmm[1].dmm_typ<<"\n";
		if ((dmm[1].dmm_typ == DMM) || (dmm[1].dmm_typ == DEVICE)) save_file <<"Messgerät x: "<<dmm[1].dmm_name<<"\n";
		if (dmm[1].dmm_typ == PH)	// pH simulation
		{
			save_file <<"ha: "<<ha.name<<"\n";
			save_file <<"ca: "<<dmm[1].ca<<"\n";
			save_file <<"va: "<<dmm[1].va<<"\n";
			save_file <<"cb: "<<dmm[1].cb<<"\n";
			save_file <<"speed: "<<tit_volspeed<<"\n";
		}
		save_file <<"Beschreibung: "<<"\n"<<project->comment<<"\n"<<"#"<<"\n"; //Delimiter
		save_file <<"Messdaten: "<<"\n";
		for (unsigned long long int i=0; i<mi;i++)
		{
			val = mw.at(i);
			x = val.x;
			if (abs(x)<1E-30) x=0; //fix stod-error (range of validity for double)
			y = val.y;
			if (abs(y)<1E-30) y=0;
			save_file << x << ","<< y << "\n";
		}
		
		save_file.close();
	}
	catch (const Glib::FileError & ex)
	{
		fehlercode = ex.what();
		fehler (fehlercode);
		file_dialog->hide();
		return;
	}
	file_dialog->hide();
	data_saved = true;
	return;
}


void open_datafile()
{
	mw_data val;
	string f_name, line, xstr, ystr, dezimal;
	size_t pos,dotpos;
	  		
	if (!data_saved) 
	{
		if (dialog_ok(_("Save data?"))) save_data();
	}
	file_dialog->set_title(_("Import data"));
	file_dialog->set_action (Gtk::FILE_CHOOSER_ACTION_OPEN);
	file_choose_ok->set_label(_("Open"));
	file_dialog->set_current_folder(datadir);
	if (!filefilter_dat)
	{
		filefilter_dat = Gtk::FileFilter::create();
		filefilter_dat->add_pattern("*.dat");
	}
	file_dialog->set_filter(filefilter_dat);
	
	int result = file_dialog->run();
	
	if (result != Gtk::RESPONSE_ACCEPT)
	{
		file_dialog->hide(); 
		return;
	}
    f_name = file_dialog->get_filename();
	
	open_file.open (f_name); 
	
	//import data
	if (open_file.is_open())
	{
		getline(open_file,line);
		if (line != file_id)
		{
			fehler (_("No Galvani data file"));
			open_file.close();
			file_dialog->hide();
			return;
		}
		project->datum = read_item("Datum der Messung: ");
		project->title = read_item("Projekt-Titel: ");		
		project->funktion_x = read_item("Messfunktion x: ");
		project->funktion_y = read_item("Messfunktion y: ");
		project->unit_x = read_item("Einheit x: ");
		project->unit_y = read_item("Einheit y: ");
		dmm[0].dmm_typ = get_dmmtyp (read_item("Daten x: "));
		if (dmm[0].dmm_typ == DMM) dmm[0].dmm_name = read_item("Messgerät x: ");
		else dmm[0].dmm_name = "";
		dmm[1].dmm_typ = get_dmmtyp(read_item("Daten y: "));
		if ((dmm[1].dmm_typ == DMM) || (dmm[1].dmm_typ == DEVICE)) dmm[1].dmm_name = read_item("Messgerät y: ");
		else dmm[1].dmm_name = "";		
		
		if (dmm[1].dmm_typ == PH)	// pH simulation
		{			
			ha.name = read_item("ha: ");
			dmm[1].ca = get_dbstr (read_item("ca: "));
			dmm[1].va = get_dbstr (read_item("va: "));
			dmm[1].cb = get_dbstr (read_item("cb: "));
			tit_volspeed = get_dbstr (read_item("speed: "));
			dmm[1].dmm_typ = PH;
		}
		project->comment = read_item("Beschreibung: ");	//end: "#" (Delimiter)
		while ((line.find ("#")>0) && (!open_file.eof())) 
		{
			getline(open_file,line);
			if (line.find ("#")==0) break;
			project->comment.append(line);
			project->comment.append("\n");
		}
		read_item("Messdaten: "); //values follow
			// set read position
		reset_mw();
		mi= 0;
		while (!open_file.eof())
		{
			getline(open_file,line);
			pos = line.find (",");
			if (pos > line.length()) break;
			xstr = line.substr(0,pos);
			val.x = get_dbstr (xstr);
			ystr = line.substr(pos+1);
			val.y = get_dbstr (ystr);			
			new_val(val);			
		}
		if ((!is_known_function (project->funktion_x)) || (!is_known_function (project->funktion_x))) translate_functions ();
		if (project->funktion_x == _("Wavelength")) func_x->append (project->funktion_x);
		if (project->funktion_y == _("Absorption")) func_y->append (project->funktion_y);		
		if (project->funktion_x != _("Time")) dmm[0].m_funkt = project->funktion_x;
		dmm[1].m_funkt = project->funktion_y;
		open_file.close();
	}
	else fehler (_("Error opening file"));
	file_dialog->hide();
	
	define_project ();
	reset_evaloptions ();
	show_diag ();
	menu_eval_data->set_sensitive (true);
	data_saved = true;
	return;
}


bool is_known_function (std::string funct)
{
	bool known;

	known = false;
	for (int i=0; i<m_functions.size(); i++)
	{
		if (funct == m_functions.at(i))
		{
			known = true;
			break;
		}
	}
	return known;
}


void translate_functions ()
{
	
	file_function_x->set_text (project->funktion_x);	//functions read from file
	file_function_y->set_text (project->funktion_y);
	function_x->set_active_text(project->funktion_x);	//show function if known
	function_y->set_active_text(project->funktion_y);
	int result = map_func->run();
	switch(result)
  	{
    	case(Gtk::RESPONSE_ACCEPT):
    	{			
			set_messfunkt (0, function_x->get_active_text());	//correct functions
			set_messfunkt (1, function_y->get_active_text());
			break;
		}
		case(Gtk::RESPONSE_CANCEL):
    	{
			break;
    	}
	}
	map_func->hide();
	return;
}

////////// export Diagram  //////////////////

void export_diag()
{
	std:string f_name, img_filename, ext;
	int pos;
	Cairo::RefPtr<Cairo::SvgSurface> svg;
	Cairo::RefPtr<Cairo::Context> svg_context;
	Glib::RefPtr<Gdk::Pixbuf> img;

	pos = filename.find_last_of("/");
	if (pos <= filename.length()) f_name = filename.erase(0,pos+1);
	pos = filename.find(".");
	if (pos <= filename.length())
	{
		f_name = filename.erase(pos+1);
		f_name = f_name.append("png");
	}
	else f_name = "messdaten.png";
	file_dialog->set_action (Gtk::FILE_CHOOSER_ACTION_SAVE);
	file_dialog->set_title(_("Export diagram"));
	file_choose_ok->set_label(_("Save"));
	file_dialog->set_current_folder(datadir);
	file_dialog->set_current_name(f_name);
	file_dialog->set_tooltip_text (_("filetype ist defined by extension"));
	if (!filefilter_img)
	{
		filefilter_img = Gtk::FileFilter::create();
		filefilter_img->add_pattern("*.png");
		filefilter_img->add_pattern("*.svg");
	}
	file_dialog->set_filter(filefilter_img);

	int result = file_dialog->run();
	if (result == Gtk::RESPONSE_CANCEL)
	{
		file_dialog->hide(); 
		return;
	}
    
	img_filename = file_dialog->get_filename();
		
	pos = img_filename.find(".");
	ext = img_filename.substr(pos);
	if (ext == ".svg")
	{
		try 
		{
			svg = Cairo::SvgSurface::create(img_filename, diag->width, diag->height);
			svg_context = Cairo::Context::create (svg);
			svg_context->set_source (diag->diag_sf, 0, 0);
			svg_context->paint ();
		}
		catch (const Glib::FileError & ex)
		{
			fehlercode = ex.what();
			fehler(fehlercode);
		}
	}
	else	
	{
		
		try 
		{
			img = Gdk::Pixbuf::create(diag->diag_sf, 0, 0, diag->width, diag->height);
			img->save(img_filename,"png");
		}
		catch (const Gdk::PixbufError & ex)
		{
			fehlercode = ex.what();
			fehler(fehlercode);
		}
		catch (const Glib::FileError & ex)
		{
			fehlercode = ex.what();
			fehler(fehlercode);
		}
		
	}
		
	file_dialog->hide();
	return;
}


///////////////////// Read Items from data file  ///////////////////
typ get_dmmtyp(std::string t)
{
	typ dmmtyp;

	if (t == "0") dmmtyp = TIME;
	else if (t == "1") dmmtyp = DMM;
	else if (t == "2") dmmtyp = DEVICE;
	else if (t == "3") dmmtyp = LINEAR;
	else if (t == "4") dmmtyp = EXP;
	else if (t == "5") dmmtyp = PH;
	else if (t == "6") dmmtyp = PEAK;
	else if (t == "7") dmmtyp = SPEKTRUM;
	else dmmtyp = DMM;
	
	return dmmtyp;
}

std::string read_item(std::string opt_item)
{
	std::string opt = "", line;
	int pos;
	
	open_file.seekg(0,std::ios::beg);
	while (open_file.peek() != EOF)
	{
		getline(open_file,line);
		pos = line.find (opt_item);
		if (pos <= line.length()) 
		{
			opt = line.substr (pos + opt_item.length ());
			break;
		}
	} 
	return opt;
}