#include "wb_config.h"

#include "grtui/gui_plugin_base.h"
#include <gtkmm.h>
#include "wb_printing.h"
#include "mdc_canvas_view_printing.h"
#include "wbcanvas/model_diagram_impl.h"
#include "grts/structs.workbench.h"
#include "gtk_helpers.h"
#include <stdio.h>
#include <gtk/gtk.h>

namespace linux_printing
{

//==============================================================================
//! WBPageSetup is a way to access persistent(static) page and print settings
//! in linux.
class WBPageSetup
{
  public:
    WBPageSetup(const app_PageSettingsRef& ps);

    static void propagate_print_settings_to_grt_tree();
    virtual void run_setup();

    static Glib::RefPtr<Gtk::PageSetup>      _page_setup;
    static Glib::RefPtr<Gtk::PrintSettings>  _print_settings;

  private:
    static app_PageSettingsRef                      _app_page_settings;
};

Glib::RefPtr<Gtk::PageSetup>      WBPageSetup::_page_setup(0);
Glib::RefPtr<Gtk::PrintSettings>  WBPageSetup::_print_settings(0);
app_PageSettingsRef               WBPageSetup::_app_page_settings;

//------------------------------------------------------------------------------
WBPageSetup::WBPageSetup(const app_PageSettingsRef& ps)
{
  _app_page_settings = ps;
//  if (!_page_setup)
//    _page_setup     = Gtk::PageSetup::create();

  if (!_print_settings)
    _print_settings = Gtk::PrintSettings::create();
}

//------------------------------------------------------------------------------
void WBPageSetup::run_setup()
{
  Glib::RefPtr<Gtk::PageSetup> new_page_setup = Gtk::run_page_setup_dialog(*get_mainwindow(), _page_setup, _print_settings);
  _page_setup = new_page_setup;

  propagate_print_settings_to_grt_tree();
}

//------------------------------------------------------------------------------
void WBPageSetup::propagate_print_settings_to_grt_tree()
{
  std::string page_orientation_as_str;
  
  // Set orientation
  Gtk::PageOrientation page_orient = _page_setup->get_orientation();
  if ( page_orient == Gtk::PAGE_ORIENTATION_PORTRAIT )
    page_orientation_as_str = "portrait";
  else if ( page_orient == Gtk::PAGE_ORIENTATION_LANDSCAPE )
    page_orientation_as_str = "landscape";
  else
  {
    g_message("Unsupported page orientation. Setting page orientation to portrait");
    page_orientation_as_str = "portrait";
  }  
  _app_page_settings->orientation(page_orientation_as_str);
  
  // Set paper type
  Gtk::PaperSize   gtk_paper_size      = _page_setup->get_paper_size();
  app_PaperTypeRef paper_type          = _app_page_settings->paperType();

  const std::string paper_name = bec::replace_string(gtk_paper_size_get_name(gtk_paper_size.gobj()), "_", "-");
  
  paper_type->caption(paper_name);
  paper_type->height(gtk_paper_size.get_height(Gtk::UNIT_POINTS));
  paper_type->width(gtk_paper_size.get_width(Gtk::UNIT_POINTS));
  paper_type->marginTop(0.0);
  paper_type->marginBottom(0.0);
  paper_type->marginLeft(0.0);
  paper_type->marginRight(0.0);

  _app_page_settings->marginBottom(gtk_paper_size.get_default_bottom_margin(Gtk::UNIT_POINTS));
  _app_page_settings->marginLeft(gtk_paper_size.get_default_left_margin(Gtk::UNIT_POINTS));
  _app_page_settings->marginRight(gtk_paper_size.get_default_right_margin(Gtk::UNIT_POINTS));
  _app_page_settings->marginTop(gtk_paper_size.get_default_top_margin(Gtk::UNIT_POINTS));

  g_message("existing scale %f", (float)_app_page_settings->scale());
  //_app_page_settings->scale(_print_settings->get_scale()); //TODO get it from settings
}

//==============================================================================
class WBPrintOperation : public Gtk::PrintOperation
{
  public:
    WBPrintOperation(const model_DiagramRef& diagram);
    static Glib::RefPtr<WBPrintOperation> create(const model_DiagramRef& diagram);
    virtual ~WBPrintOperation();
    
  protected:
    virtual void on_begin_print(const Glib::RefPtr<Gtk::PrintContext>& ctx);    
    virtual void on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& ctx, int page_nr);    
    virtual void on_done(Gtk::PrintOperationResult result);
  
  private:
    model_DiagramRef                  _diagram;
    mdc::CanvasViewExtras             _canvas;
    mdc::CairoCtx                    *_cairoctx;
};

//------------------------------------------------------------------------------
WBPrintOperation::WBPrintOperation(const model_DiagramRef& diagram)
                 : _diagram(diagram)
                 , _canvas(diagram->get_data()->get_canvas_view())
                 , _cairoctx(0)
{}

//------------------------------------------------------------------------------
Glib::RefPtr<WBPrintOperation> WBPrintOperation::create(const model_DiagramRef& diagram)
{
  return Glib::RefPtr<WBPrintOperation>(new WBPrintOperation(diagram));
}

//------------------------------------------------------------------------------
WBPrintOperation::~WBPrintOperation()
{}

//------------------------------------------------------------------------------
void WBPrintOperation::on_begin_print(const Glib::RefPtr<Gtk::PrintContext>& ctx)
{
  set_default_page_setup(WBPageSetup::_page_setup);
  set_print_settings(WBPageSetup::_print_settings);
  set_track_print_status();

  cairo_surface_t *surface = ctx->get_cairo_context()->get_target()->cobj();
  _cairoctx = new mdc::CairoCtx(surface);

  app_PageSettingsRef page(workbench_DocumentRef::cast_from(_diagram.get_grt()->get("/wb/doc"))->pageSettings());

  _canvas.set_paper_size(page->paperType()->width(), page->paperType()->height());
  _canvas.set_page_margins(page->marginTop(), page->marginLeft(), page->marginBottom(), page->marginRight());
  _canvas.set_scale(page->scale());
  //page->scale(WBPageSetup::_print_settings->get_scale());

  g_message("on_begin_print %f %f, scale %f", (double)page->paperType()->width(), (double)page->paperType()->height(), (float)page->scale());

  _canvas.set_paper_size(page->paperType()->width(), page->paperType()->height());
  _canvas.set_orientation(page->orientation() == "landscape" ? mdc::Landscape : mdc::Portrait);
  
  set_n_pages(wbprint::getPageCount(_diagram));
}

//------------------------------------------------------------------------------
void WBPrintOperation::on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& ctx, int page_nr)
{
  app_PageSettingsRef page(workbench_DocumentRef::cast_from(_diagram.get_grt()->get("/wb/doc"))->pageSettings());

  const int width  = page->paperType()->width();
  const int height = page->paperType()->height();
  
  _canvas.print_page(_cairoctx, width, height, page_nr);
}

//------------------------------------------------------------------------------
void WBPrintOperation::on_done(Gtk::PrintOperationResult result)
{
  delete _cairoctx;
  _cairoctx = 0;
  PrintOperation::on_done(result);
}

//==============================================================================
class WBPrintingLinux : public GUIPluginBase
{
  public:
    WBPrintingLinux(grt::Module *m, bec::GRTManager *grtm, const grt::BaseListRef &args);
    virtual void execute();
    virtual void show_plugin();
    
  private:
    void on_print_done(Gtk::PrintOperationResult result, Glib::RefPtr<WBPrintOperation> &op);
    model_DiagramRef    _diagram; //!< TODO: use currently selected diagram!
};

//------------------------------------------------------------------------------
WBPrintingLinux::WBPrintingLinux(grt::Module *m, bec::GRTManager *grtm, const grt::BaseListRef &args)
                : GUIPluginBase(m)
                , _diagram(model_DiagramRef::cast_from(args.get(0)))
{}

//------------------------------------------------------------------------------
void WBPrintingLinux::execute()
{}

//------------------------------------------------------------------------------
void WBPrintingLinux::show_plugin()
{
  try
  {
    Glib::RefPtr<WBPrintOperation> printer = WBPrintOperation::create(_diagram); 

    printer->signal_done().connect(sigc::bind(sigc::mem_fun(this, &WBPrintingLinux::on_print_done), printer));
    Gtk::PrintOperationResult result = printer->run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, *(get_mainwindow()));
  }
  catch (const Gtk::PrintError &e)
  {
    g_message("Error while printing %s", e.what().c_str());
  }
}

//------------------------------------------------------------------------------
void WBPrintingLinux::on_print_done(Gtk::PrintOperationResult result, Glib::RefPtr<WBPrintOperation> &op)
{
  if (result == Gtk::PRINT_OPERATION_RESULT_ERROR)
  {
    Gtk::MessageDialog err_dlg(*get_mainwindow(), "Error printing document", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
    err_dlg.run();
  }
  else if (result == Gtk::PRINT_OPERATION_RESULT_APPLY)
  {
    WBPageSetup::_print_settings = op->get_print_settings();
  }
}

}// end of namespace linux_printing

//------------------------------------------------------------------------------
extern "C" 
{
  GUIPluginBase *createPrintDialog(grt::Module *m, bec::GRTManager *grtm, const grt::BaseListRef &args)
  {
    //return new linux_printing::WBPrintingLinux(m, grtm, args);
    linux_printing::WBPrintingLinux lp(m, grtm, args);
    lp.show_plugin();
    return 0;
  }
};

//------------------------------------------------------------------------------
extern "C" 
{
  GUIPluginBase *createPrintSetupDialog(grt::Module *m, bec::GRTManager *grtm, const grt::BaseListRef &args)
  { 
    linux_printing::WBPageSetup ps(workbench_DocumentRef::cast_from(args.get_grt()->get("/wb/doc"))->pageSettings());

    ps.run_setup();

    return 0;
  }
};

