// print.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 <gtk/gtk.h>
#include <glib.h>
#include <gtkmm.h>
#include <glibmm/i18n.h>
#include "print.h"

PrintOP::PrintOP()
{
	//set some properties of the print page
	pg_scale = PANGO_SCALE;
	ps = 2.0;	// set scaling factor for printing lines in diagramm
	pf = 1.5;	// set scaling factor for printing fonts in diagramm
	dpi = 300;	
	print_op = Gtk::PrintOperation::create();
	print_op->set_job_name (project->title);
	print_op->signal_begin_print().connect(sigc::mem_fun(this, &PrintOP::begin_print));
    print_op->signal_draw_page().connect(sigc::mem_fun(this, &PrintOP::draw_page));
	print_op->signal_create_custom_widget().connect(sigc::mem_fun(this, &PrintOP::create_custom_widget));
	print_op->signal_custom_widget_apply().connect(sigc::mem_fun(this, &PrintOP::set_printoptions));
	print_op->set_custom_tab_label (_("Page Elements"));
	print_op->set_unit (Gtk::UNIT_POINTS);	
	print_page = Gtk::PageSetup::create ();
	orientation = Gtk::PAGE_ORIENTATION_PORTRAIT;
	print_page->set_orientation (orientation);
	print_settings = Gtk::PrintSettings::create();
	print_settings->set_resolution (dpi);
	print_settings->set_quality (Gtk::PRINT_QUALITY_HIGH);
	top_margin = 30;
	bottom_margin = 40;
	left_margin = 30;
	right_margin = 30;
	print_page->set_top_margin (top_margin, Gtk::UNIT_POINTS);
	print_page->set_bottom_margin (bottom_margin, Gtk::UNIT_POINTS);
	print_page->set_left_margin (left_margin, Gtk::UNIT_POINTS);
	print_page->set_right_margin (right_margin, Gtk::UNIT_POINTS);	
	print_op->set_default_page_setup (print_page);
	print_op->set_print_settings (print_settings);
	print_op->set_use_full_page (false);
	print_op->set_embed_page_setup ();
}

PrintOP::~PrintOP()
{
}

void print_report ()
{
	
	printjob = new PrintOP;	
	
	printjob->print_action = Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG;
	try
  	{
		Gtk::PrintOperationResult result = printjob->print_op->run(printjob->print_action);
	}
	catch (const Gtk::PrintError& ex)
	{
		fehler (ex.what());
	}	
	return;
}

void PrintOP::set_printoptions(Gtk::Widget* printoption)	//apply page settings in print dialog (custom widget)
{

	printoption_title = titlesw.get_state();
	printoption_comment = commentsw.get_state();
	printoption_diag = diagsw.get_state();
	printoption_result = resultsw.get_state();
	
  	return;
}

Gtk::Widget* PrintOP::create_custom_widget()		//create printoptions (elements on print page)
{
	auto printpage_options = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL);
	
	// print title?
	auto titlebox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, 100);
	titlebox->set_margin_top (50);
	printpage_options->pack_start (*titlebox, false, false);
	auto title = Gtk::make_managed<Gtk::Label>(_("Title"));
	title->set_halign (Gtk::ALIGN_START);
	title->set_margin_start (20);
	title->set_size_request (100);
	titlebox->pack_start(*title, false, false);
	title->show();
	titlebox->pack_start(titlesw, false, false);
	titlesw.set_state(printoption_title);
	titlesw.show();
	titlebox->show();
	
	// print comment?
	auto commentbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, 100);
	commentbox->set_margin_top (20);
	printpage_options->pack_start (*commentbox, false, false);
	auto comment = Gtk::make_managed<Gtk::Label>(_("Comment"));
	comment->set_halign(Gtk::ALIGN_START);
	comment->set_margin_start(20);
	comment->set_size_request (100);
	commentbox->pack_start(*comment, false, false);
	comment->show();
	commentbox->pack_start(commentsw, false, false);
	commentsw.set_state(printoption_comment);
	commentsw.show();
	commentbox->show();
	
	// print diagram?
	auto diagrambox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, 100);
	diagrambox->set_margin_top (20);
	printpage_options->pack_start (*diagrambox, false, false);
	auto diagram = Gtk::make_managed<Gtk::Label>(_("Diagram"));
	diagram->set_halign(Gtk::ALIGN_START);
	diagram->set_margin_start(20);
	diagram->set_size_request (100);
	diagrambox->pack_start(*diagram, false, false);
	diagram->show();
	diagrambox->pack_start(diagsw, false, false);
	diagsw.set_state(printoption_diag);
	diagsw.show();
	diagrambox->show();

	// print result?
	auto resultbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, 100);
	resultbox->set_margin_top (20);
	printpage_options->pack_start (*resultbox, false, false);
	auto result = Gtk::make_managed<Gtk::Label>(_("Evaluation"));
	result->set_halign(Gtk::ALIGN_START);
	result->set_margin_start(20);
	result->set_size_request (100);
	resultbox->pack_start(*result, false, false);
	result->show();
	resultbox->pack_start(resultsw, false, false);
	resultsw.set_state(printoption_result);
	resultsw.show();
	resultbox->show();	

	printpage_options->show();
	return printpage_options;
}


void PrintOP::begin_print (const Glib::RefPtr<Gtk::PrintContext>& print_context)
{
	int width, height;
	int pg_width, pg_height, img_width, img_height;
	int text_width, text_height;
	double img_space_width, img_space_height;
	double new_img_width=0, new_img_height=0;
	int print_pages = 1;
	double printpage_height = 0;
	double line_height;
	double scale_factor;
	Pango::Rectangle ink_rect, logical_rect;
			
		
	///////  Generate Layout  /////////
		
	// initialize Print-Context	
	page_layout = print_context->create_pango_layout();
	page_width = print_context->get_width();
  	page_height = print_context->get_height();
	pg_width = static_cast<int>(page_width * pg_scale);
	pg_height = static_cast<int>(page_height * pg_scale);
  	page_layout->set_width(pg_width);	
	
	//calculate number of pages
	printpage_height = 0;
	PageBreaks.clear ();
	font = Pango::FontDescription("Serif 8"); 
	page_layout->set_font_description(font);
	
	if (printoption_title)
	{
		font.set_size(18*pg_scale);
		page_layout->set_font_description(font);
		report_title = project->title;
  		page_layout->set_text(report_title);
		page_layout->get_pixel_size(text_width, text_height);
		printpage_height += text_height + 10;
	}
	if (printoption_comment)
	{
		font.set_size(10*pg_scale);
		page_layout->set_font_description(font);
		report_comment = project->comment;
  		page_layout->set_text(report_comment);
		page_layout->get_pixel_size(text_width, text_height);
		printpage_height += text_height;
	}
	if (printoption_diag)
	{
		img_space_height = page_height - printpage_height - bottom_margin - text_height;
		img_space_width = page_width - left_margin - right_margin;
		img_width = diag->diag_sf->get_width();
		img_height = diag->diag_sf->get_height();
		new_img_width = img_width;
		new_img_height = img_height;
		if (img_width > img_space_width)
		{
			new_img_width = img_space_width;
			new_img_height = new_img_width * img_height/img_width;
			if (new_img_height > img_space_height)
			{
				new_img_height = img_space_height;
				new_img_width = new_img_height * img_width/img_height;
			}
		}
		else if (img_height > img_space_height)
		{
			new_img_height = img_space_height;
			new_img_width = new_img_height * img_width/img_height;
			if (new_img_width > img_space_width)
			{
				new_img_width = img_space_width;
				new_img_height = new_img_width * img_height/img_width;
			}
		}
		printpage_height += new_img_height + 50;
	}
	
	if (printoption_result)
	{
		font.set_size(10*pg_scale);
		page_layout->set_font_description(font);
		if (eval_integration) 
		{
			report_results = _("Peak-Integration:");
			report_results += "\n";
		}
		else if (eval_reglin) 
		{
			report_results = _("Linear Regression:");
			report_results += "\n";
		}
		report_results += project->evalresults;
		page_layout->set_text(report_results);
		page_layout->get_pixel_size(text_width, text_height);	
	}
	
	//Set the number of pages to print by determining the line numbers where page breaks occur
	const int line_count = page_layout->get_line_count(); 
	
	for (int line = 0; line < line_count; ++line)
	{
		layout_line = page_layout->get_line(line);
		layout_line->get_extents(ink_rect, logical_rect);
		line_height = logical_rect.get_height() / pg_scale;
		if (printpage_height + line_height > page_height)
		{
			PageBreaks.push_back(line);
			printpage_height = 0;
		}
		printpage_height += line_height;
	}
	print_pages += PageBreaks.size();
	print_op->set_n_pages(print_pages);
	
	return;
}

void PrintOP::draw_pagelayout(Glib::RefPtr<Pango::Layout> page_layout, double start, int page_nr)
{
	int start_page_line = 0;
	int end_page_line = 0;
	int line_index = 0;
	int baseline;
	double start_pos = 0;
	Pango::LayoutIter iter;
	Pango::Rectangle logical_rect;

	if(page_nr == 0) start_page_line = 0;
  	else 
	{
		start_page_line = PageBreaks[page_nr - 1];
		start = top_margin;
	}
  	if(page_nr < static_cast<int>(PageBreaks.size())) end_page_line = PageBreaks[page_nr];
  	else end_page_line = page_layout->get_line_count();
	page_layout->update_from_cairo_context (print_ct);
	
  	//Render Pango LayoutLines over the Cairo context:
  	iter = page_layout->get_iter();
	line_index = 0;
	do
	{
		if(line_index >= start_page_line)
		{
			auto layout_line = iter.get_line();
			logical_rect = iter.get_line_logical_extents();
			baseline = iter.get_baseline();
			if (line_index == start_page_line) start_pos = logical_rect.get_y() / pg_scale;
			print_ct->move_to(left_margin + logical_rect.get_x() / pg_scale, start + baseline / pg_scale - start_pos);
			layout_line->show_in_cairo_context(print_ct);
		}

		line_index++;
		  
	}
	while(line_index < end_page_line && iter.next_line());
	
	return;
}

void PrintOP::draw_page(const Glib::RefPtr<Gtk::PrintContext>& print_context, int page_nr)
{
	int width, height;
	int img_width, img_height;
	int text_width, text_height;
	double img_space_width, img_space_height;
	int new_img_width=0, new_img_height=0;
	double scale_factor, pc_posx, pc_posy;
	Pango::TabArray tabs;
	
	
	print_ct = print_context->get_cairo_context ();	
	// fill page with text and diagram
	print_ct->set_source_rgb(0.0, 0.0, 0.0);
	if (page_nr == 0)
	{
		font = Pango::FontDescription("Serif 8"); 
		page_layout->set_font_description(font);
			
		print_ct->move_to (left_margin, top_margin);
		if (printoption_title)
		{
			font.set_size(18*pg_scale);
			page_layout->set_font_description(font);
			report_title = project->title;
  			page_layout->set_text(report_title);
			page_layout->update_from_cairo_context (print_ct);
			page_layout->show_in_cairo_context(print_ct);
			page_layout->get_pixel_size(text_width, text_height);
			print_ct->get_current_point(pc_posx, pc_posy);
			print_ct->move_to (left_margin, pc_posy + text_height + 10);
		}
		if (printoption_comment)
		{
			font.set_size(10*pg_scale);
			page_layout->set_font_description(font);
			report_comment = project->comment;
  			page_layout->set_text(report_comment);
			page_layout->update_from_cairo_context (print_ct);
			page_layout->show_in_cairo_context(print_ct);	
			page_layout->get_pixel_size(text_width, text_height);
			print_ct->get_current_point(pc_posx, pc_posy);
			print_ct->move_to (left_margin, pc_posy + text_height);
		}
		
		if (printoption_diag)
		{
			page_layout->get_pixel_size(text_width, text_height);
			print_ct->get_current_point(pc_posx, pc_posy);		
			text_height += trunc(pc_posy)-top_margin;
			img_space_height = page_height - bottom_margin - text_height;
			img_space_width = page_width - left_margin - right_margin;
			img_width = diag->diag_sf->get_width();
			img_height = diag->diag_sf->get_height();	
			new_img_width = img_width;
			new_img_height = img_height;
			width = diag->width;
			height = diag->height;
			//correct width and height of image
			if (img_width > img_space_width)
			{
				new_img_width = img_space_width;
				new_img_height = new_img_width * img_height/img_width;
				if (new_img_height > img_space_height)
				{
					new_img_height = img_space_height;
					new_img_width = new_img_height * img_width/img_height;
				}				
			}
			else if (img_height > img_space_height)
			{
				new_img_height = img_space_height;
				new_img_width = new_img_height * img_width/img_height;
				if (new_img_width > img_space_width)
				{
					new_img_width = img_space_width;
					new_img_height = new_img_width * img_height/img_width;
				}				
			}
			scale_factor = new_img_width;
			scale_factor /= img_width;						
			//print_ct mit pattern füllen:
			auto pattern = Cairo::SurfacePattern::create(diag->diag_sf);
            auto m = pattern->get_matrix();			
			m.scale(1/scale_factor, 1/scale_factor);
			m.translate(-left_margin,-text_height);
			pattern->set_matrix(m);
            print_ct->set_source(pattern);
            print_ct->rectangle (left_margin, text_height, new_img_width, new_img_height);
			print_ct->fill();			
		}
	
		if (printoption_result)
		{
			print_ct->set_source_rgb(0.0, 0.0, 0.0);
			font.set_size(10*pg_scale);
			page_layout->set_font_description(font);
			if (eval_integration) 
			{
				report_results = _("Peak-Integration:");
				report_results += "\n";
				tabs = Pango::TabArray (3, false);
				tabs.set_tab (0, Pango::TAB_LEFT, 60* pg_scale);
				tabs.set_tab (1, Pango::TAB_LEFT, 120* pg_scale);
				tabs.set_tab (2, Pango::TAB_LEFT, 200* pg_scale);
			}
			else if (eval_reglin) 
			{
				report_results = _("Linear Regression:");
				report_results += "\n";
			}
			else if (eval_diff) 
			{
				report_results = _("Differential Function:");
				report_results += "\n";
			}
			print_ct->move_to (left_margin, pc_posy + new_img_height + 20);
			report_results += project->evalresults;
			page_layout->set_text(report_results);
			if (eval_integration) page_layout->set_tabs(tabs);
			print_ct->get_current_point(pc_posx, pc_posy);
			draw_pagelayout (page_layout, pc_posy, page_nr);
		}
	}	
	else if (printoption_result) draw_pagelayout (page_layout, 0, page_nr);
	ps = 1.0;		//reset scaling factor
	pf = 1.0;
	return;
}

