// 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;
	signal_begin_print().connect(sigc::mem_fun(*this, &PrintOP::begin_print));
    signal_draw_page().connect(sigc::mem_fun(*this, &PrintOP::draw_page));
    signal_done ().connect(sigc::mem_fun(*main_win->print_dialog, &Print_Dialog::save_print_settings));
    signal_status_changed ().connect(sigc::mem_fun(*main_win->print_dialog, &Print_Dialog::print_operation_status));
    set_unit (Gtk::Unit::POINTS); // use pts internally
    orientation = Gtk::PageOrientation::PORTRAIT;
    	print_page = Gtk::PageSetup::create ();
    	print_page->set_orientation (orientation);
    	set_default_page_setup (print_page);
	set_use_full_page (true); //use cairo context without margins
	set_embed_page_setup ();
	set_show_progress (true); //progress not shown ?!
	//set_allow_async (true);
}

PrintOP::~PrintOP()
{
}


void Print_Dialog::prepare_printreport ()
{
   if (print_op) delete print_op;
   print_op = new PrintOP;
    print_op->set_job_name (project->title);

    show_printoptions ();

    return;
}

void Print_Dialog::cancel_printreport ()
{
    close ();

    return;
}

/*
void PrintOP::preview_printreport ()
{
    return;
}
*/

void Print_Dialog::print_report ()
{
    Gtk::PrintOperation::Result res;

    set_printoptions(); // from print dialog

	print_action = Gtk::PrintOperation::Action::PRINT_DIALOG;
	try
  	{
		res = print_op->run(print_action);
	}
	catch (const Glib::Error& ex)
	{
		error_msg (ex.what());
	}

	usleep (1000000); // let user read print status info
	main_win->status_info.set_visible (false);

	return;
}




void Print_Dialog::show_printoptions()		//show printoptions (elements on print page)
{
    //Page Elements
    titlesw.set_active(printoption_title);
    commentsw.set_active(printoption_comment);
    diagsw.set_active(printoption_diag);
    resultsw.set_active(printoption_result);

    //page orientation
    if (print_op->orientation == Gtk::PageOrientation::LANDSCAPE) page_orientation.set_active_text (_("Landscape"));
    else page_orientation.set_active_text (_("Portrait"));

    //margins in mm
    lm.set_value (left_margin);
    rm.set_value (right_margin);
    tm.set_value (top_margin);
    bm.set_value (bottom_margin);

   set_visible (true);

   return;
}

void Print_Dialog::set_printoptions()	//apply page settings
{
    //page elements
	printoption_title = titlesw.get_active();
	printoption_comment = commentsw.get_active();
	printoption_diag = diagsw.get_active();
	printoption_result = resultsw.get_active();
    //margins
	top_margin = tm.get_value ();
	bottom_margin = bm.get_value ();
	left_margin = lm.get_value ();
	right_margin = rm.get_value ();
	if (page_orientation.get_active_text () == _("Portrait")) print_op->orientation = Gtk::PageOrientation::PORTRAIT;
    else print_op->orientation = Gtk::PageOrientation::LANDSCAPE;
    print_op->set_print_settings (print_settings);
    print_op->get_margin (print_op->left, print_op->right, print_op->top, print_op->bottom); // get margins in points
	print_op->set_pagesettings ();

	close ();

  	return;
}

void Print_Dialog::save_print_settings (Gtk::PrintOperation::Result result)
{
    std::string error_str, status;

    if (result == Gtk::PrintOperation::Result::ERROR)
    {
        error_str = (_("Error while printing Document "));
        error_str += project->title;
        error_msg (error_str);
    }
    else if (result == Gtk::PrintOperation::Result::APPLY)
    {
       //Update PrintSettings
       print_settings = print_op->get_print_settings();
       //print_page
    }
    if (print_op->is_finished())
    {
        //list of jobs
        status = _("Print job completed: ");
        status += project->title;
        main_win->info (status);
        delete print_op;
    }

    return;
}

void Print_Dialog::print_operation_status ()
{
    std::string status;

    // Print-Operation finished?
    if (print_op->is_finished())
    {
        status = _("Print job completed: ");
        status += project->title;
        main_win->info (status);
        delete print_op;
    }
    else
    {
        status = print_op->get_status_string();
        main_win->info (status);
    }

    return;
}


void PrintOP::get_margin (double& left, double& right, double& top, double& bottom)
{
     //calculate margins from user input in Points
    left = main_win->print_dialog->left_margin * 2.8347;
    right = main_win->print_dialog->right_margin * 2.8347;
    top = main_win->print_dialog->top_margin * 2.8347;
    bottom = main_win->print_dialog->bottom_margin * 2.8347;
    return;
}

void PrintOP::set_pagesettings ()
{
    print_page->set_top_margin (top, Gtk::Unit::POINTS);
	print_page->set_bottom_margin (bottom, Gtk::Unit::POINTS);
	print_page->set_left_margin (left, Gtk::Unit::POINTS);
	print_page->set_right_margin (right, Gtk::Unit::POINTS);
	print_page->set_orientation (orientation);
	set_default_page_setup (print_page);

    return;
}


void PrintOP::begin_print (const Glib::RefPtr<Gtk::PrintContext>& print_context)
{
	int pg_width, pg_height;
	int print_pages = 1;
	Pango::Rectangle ink_rect, logical_rect;


	///////  Generate Layout  /////////

	// initialize page layout
	page_layout = print_context->create_pango_layout();

	//subtract margins from print context to get page dimensions
	page_width = print_context->get_width() - left - right;
  	page_height = print_context->get_height() - top - bottom;
  	pg_width = static_cast<int>(page_width * pg_scale);    // effective page width
	pg_height = static_cast<int>(page_height * pg_scale); //effective page height
  	page_layout->set_width(pg_width);
    	print_ct = print_context->get_cairo_context ();
    get_printlines (print_ct);    // get printpage height and text to print

	//calculate number of pages
	page_breaks.clear ();
	//Set the number of pages to print by determining the line numbers where page breaks occur
    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)
		{
			page_breaks.push_back(line);
			printpage_height = 0;
		}
		printpage_height += line_height;
	}
 	print_pages += page_breaks.size();
	set_n_pages(print_pages);

	return;
}


void PrintOP::draw_page(const Glib::RefPtr<Gtk::PrintContext>& print_context, int page_nr)
{
    int start_page_line=0;
	int end_page_line=0;
	int line_index=0;
	int baseline;
	int text_width, text_height=0;
	double start_pos=0;
	Pango::LayoutIter iter;
	Pango::Rectangle logical_rect;
	Pango::TabArray tabs;

    print_ct = print_context->get_cairo_context ();
    print_ct->set_source_rgb(0.0, 0.0, 0.0);
    printpage_height = top; //cairo-context starts at (0,0), but printpage starts at top margin

  	//Render Pango LayoutLines:
    page_layout->update_from_cairo_context (print_ct);
    font = Pango::FontDescription("Serif 8");
	page_layout->set_font_description(font);
	print_ct->move_to (left, top);
	if(page_nr == 0)    //print title and diagram on page 1
	{
	    if (main_win->print_dialog->printoption_title)
	    {
		    font.set_size(18*pg_scale);
		    page_layout->set_font_description(font);
		    page_layout->set_text(report_title);
		    page_layout->get_pixel_size(text_width, text_height);
		    printpage_height += text_height;
		    page_layout->update_from_cairo_context (print_ct);
		    page_layout->show_in_cairo_context(print_ct);
		    print_ct->move_to (left, printpage_height);
		}
	    if (main_win->print_dialog->printoption_diag)
	    {
		    //fill print_ct with pattern constructed from diagram
		    auto pattern = Cairo::SurfacePattern::create(main_win->diag.diag_sf);
            auto m = pattern->get_matrix();
            scale_factor = img_width;
            scale_factor /= main_win->diag.width;
		    m.scale(1/scale_factor, 1/scale_factor);
		    m.translate(-left,-printpage_height);
		    pattern->set_matrix(m);
            print_ct->set_source(pattern);
            print_ct->rectangle (left, printpage_height, img_width, img_height);
            print_ct->fill();
	        printpage_height += img_height;
	        print_ct->move_to (left, printpage_height);
	    }
	    // landscape: print comment and/or evaluation on 2. page; diagram will almost fill the first page
	    if (orientation == Gtk::PageOrientation::LANDSCAPE) return;
	}

    // Up to this point the printpage should fit the page size.
    // Additional text could need a new page. So we have to calculate space on page.
    line_index = 0;
    if(page_nr == 0) start_page_line = line_index;
  	else start_page_line = page_breaks[page_nr-1];
  	if(page_nr < page_breaks.size()) end_page_line = page_breaks[page_nr];
  	else end_page_line = line_count;

    print_text = "";
    print_ct->set_source_rgb(0.0, 0.0, 0.0);
    if (main_win->print_dialog->printoption_comment)
	{
		font.set_size(10*pg_scale);
		page_layout->set_font_description(font);
		print_text = report_comment;
		print_text += "\n";
	}
    if (main_win->print_dialog->printoption_result)
	{
	   	font.set_size(10*pg_scale);
		page_layout->set_font_description(font);
		print_text += report_results;
		tabs = Pango::TabArray (3, false);
		tabs.set_tab (0, Pango::TabAlign::LEFT, 60* pg_scale);
		tabs.set_tab (1, Pango::TabAlign::LEFT, 120* pg_scale);
		tabs.set_tab (2, Pango::TabAlign::LEFT, 200* pg_scale);
		if (eval_integration) page_layout->set_tabs(tabs);
	}
	page_layout->set_text(print_text);
	iter = page_layout->get_iter();

    do
	    {
		    if(line_index >= start_page_line)
		    {
		        	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 + logical_rect.get_x()/pg_scale, printpage_height + 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::get_image_ext (int& img_width, int& img_height)
{
    img_width = static_cast<int>(page_width);
    img_height = 0.75 * img_width; //aspect ratio = 4:3
    if (img_height > page_height + top - printpage_height)
    {
        img_height = static_cast<int>(page_height +top - printpage_height);
        img_width = trunc (img_height * 4 /3);
    }

    return;
}

void PrintOP::get_printlines (Cairo::RefPtr<Cairo::Context>& print_ct) // get lines on print page
{
    	int text_width, text_height;

	text_height = 0;
	printpage_height = 0;
	print_text = "";

    font = Pango::FontDescription("Serif 8");
    if (main_win->print_dialog->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;
	}
	if (main_win->print_dialog->printoption_diag)
	{
        get_image_ext(img_width, img_height);
	    printpage_height += img_height;
	}
	if (main_win->print_dialog->printoption_comment)
	{
		report_comment = project->comment;
		print_text += report_comment;
		print_text += "\n";
	}
    if (main_win->print_dialog->printoption_result)
	{
		if (eval_integration)
		{
			report_results = _("Peak-Integration:");
			report_results += "\n\n";
		}
		else if (eval_reglin)
		{
			report_results = _("Linear Regression:");
			report_results += "\n\n";
		}
		else if (eval_diff)
		{
			report_results = _("Differential Function:");
			report_results += "\n\n";
		}
		report_results += project->evalresults;
		print_text += report_results;
	}
	font.set_size(10*pg_scale);
	page_layout->set_font_description(font);
	page_layout->set_text(print_text);

    return;
}


