#include "wxinc.h"
#include <wx/print.h>
#include <wx/tokenzr.h>
#include <wx/filename.h>

#include "xvt.h"
#include "xvtart.h"

#ifdef __WXMSW__
#include "wx/dcps.h"
#include "wx/msw/printdlg.h"
#include "oswin32.h"
#else
#include "wx/setup.h"
#include "wx/dcps.h"
#include "oslinux.h"
#include "incstr.h"
#endif

#include "xvtpdf.h"
#include "xvtwin.h"

#pragma pack(4)

struct TPRINT_RCD : public PRINT_RCD
{
#ifdef __WXMSW__
	unsigned char m_data[16*1024];
	unsigned int m_size;   // Dimensione della struct DEVMODE

	void SetData(void * data, unsigned int nSize);
#else
	wxPrintNativeDataBase* m_data;
	unsigned int m_size;   // Dimensione di wxPostScriptPrintNativeData
	
	void GetData(wxPrintNativeDataBase* data) const;
	void SetData(void * data);
#endif

  unsigned int GetSize() const { return m_size; } 

  TPRINT_RCD();
	~TPRINT_RCD();
};

#pragma pack()

#ifdef __WXMSW__

void TPRINT_RCD::SetData(void* data, unsigned int nSize)
{
  if (nSize <= sizeof(m_data))
	{
    memset(m_data, 0, sizeof(m_data));  // Azzero per bene anche tutto quanto segue nSize
    memcpy(m_data, data, nSize);
	  m_size = nSize;
	}
  else
    xvt_dm_post_error("Printer info exceeds 16K");
}

TPRINT_RCD::TPRINT_RCD() : m_size(0)
{
	pr = NULL;
	memset(m_data, 0, sizeof(m_data));
}

TPRINT_RCD::~TPRINT_RCD()
{
	memset(m_data, 0, sizeof(m_data));
	m_size = 0;
}

#else

void TPRINT_RCD::GetData(wxPrintNativeDataBase * data) const 
{
  memcpy(data, m_data, GetSize());
}

void TPRINT_RCD::SetData(void* data)
{
  memcpy(m_data, data, GetSize());
}

wxNativePrintFactory __factory;

TPRINT_RCD::TPRINT_RCD()
{
  m_data = __factory.CreatePrintNativeData();
	m_size = sizeof(*m_data);
}

TPRINT_RCD::~TPRINT_RCD()
{
	delete m_data;
}

#endif

///////////////////////////////////////////////////////////
// TwxPrintOut
///////////////////////////////////////////////////////////

class TwxPrintOut : public wxPrintout
{
protected:
  const TPRINT_RCD* m_prcd;
  bool m_bBadDriver;

  virtual bool HasPage(int pageNum);
	virtual bool OnPrintPage(int pageNum);
  void ResetDC();

  wxDC* CreateDC(const TPRINT_RCD* prcd, const char* title);

public:
  void SetBadDriver(bool bd) { m_bBadDriver = bd; }
  bool HasBadDriver() const { return m_bBadDriver; }
   
  wxString PrinterName() const;
  bool IsPDF() const { return !HasBadDriver() && xvt_print_is_pdf(m_prcd) != 0; }
   
  bool InitDC(const TPRINT_RCD* prcd, const char* title);
	TwxPrintOut(const TPRINT_RCD* prcd = NULL);

	virtual ~TwxPrintOut() {}
};

wxString TwxPrintOut::PrinterName() const
{
  char strName[MAX_PATH] = "";
  xvt_print_get_name(m_prcd, strName, sizeof(strName));
  return wxString(strName);
}

bool TwxPrintOut::HasPage(int WXUNUSED(pageNum))
{	return true; }

bool TwxPrintOut::OnPrintPage(int WXUNUSED(pageNum))
{	return false; }

void TwxPrintOut::ResetDC()
{
	wxDC* dc = GetDC();
	if (dc != NULL)
  {
		delete dc;
    SetDC(NULL);
  }
}

static void RCD2data(const TPRINT_RCD* prcd, wxPrintData& data)
{
#ifdef WIN32
  wxWindowsPrintNativeData ndb;
  ndb.SetDevMode(OsWin32_ConvertToNativePrinterInfo((void*)prcd->m_data, prcd->m_size));
  ndb.TransferTo(data); 

  // in assenza di PDEVNAMES aggiorno il nome in questo modo
  char strName[MAX_PATH] = "";
  xvt_print_get_name(prcd, strName, sizeof(strName));
  data.SetPrinterName(strName);
#else
 prcd->GetData(data.GetNativeData());
 data.ConvertFromNative();
#endif
}

static void data2RCD(const wxPrintData& data, TPRINT_RCD* prcd)
{
#ifdef WIN32
  wxWindowsPrintNativeData* pNative = (wxWindowsPrintNativeData*)data.GetNativeData();
  unsigned int nSize = 0;
  void* ptr = OsWin32_ConvertFromNativePrinterInfo(pNative->GetDevMode(), nSize);
  prcd->SetData(ptr, nSize);
  delete ptr;
#else
  ((wxPrintData&)data).ConvertToNative();
  prcd->SetData(data.GetNativeData());
#endif
}

wxDC* TwxPrintOut::CreateDC(const TPRINT_RCD* prcd, const char* title)
{
  m_prcd = prcd;
  wxDC* dc = NULL;

	if (m_prcd == NULL)
	{
    wxPrinter printer;
#ifdef WIN32
    dc = new wxPrinterDC(printer.GetPrintDialogData().GetPrintData());
#else
    dc = new wxPostScriptDC(printer.GetPrintDialogData().GetPrintData());
#endif    
	}
	else 
	{
		wxPrintData data; 
		RCD2data(prcd, data);
    
		const bool ispdf = IsPDF();
    if (ispdf)
      dc = new TwxPDFDC(data, title);
    else
#ifdef WIN32
      dc = new wxPrinterDC(data);
#else
      dc = new wxPostScriptDC(data);
#endif
	}

  if (dc->IsOk())
  {
    wxSize s = dc->GetPPI();
    SetPPIPrinter(s.x, s.y);

    s = dc->GetSize();
    SetPageSizePixels(s.x, s.y);

    SetDC(dc);
    
    wxWindow* pAbort = wxPrinterBase::sm_abortWindow;
    if (pAbort != NULL)
    {
      wxWindow* pStatic = pAbort->FindWindow(wxID_STATIC);
      if (pStatic != NULL)
      {
        wxString strPrompt;
        if (IsPDF())
        {
          const wxFileName fn(title);
          strPrompt << wxT("Generazione di ") << fn.GetFullName();
        }
        else
          strPrompt << wxT("Stampa su ") << PrinterName();
        pStatic->SetLabel(strPrompt);
      }
    }
  }
  else
  {
//    delete dc;
    dc = NULL;
  }
  return dc;
}

bool TwxPrintOut::InitDC(const TPRINT_RCD* prcd, const char* title)
{
  ResetDC();
  wxDC* dc = CreateDC(prcd, title);
  if (dc != NULL)
  {
	  wxSize s = dc->GetPPI();
	  if (s.x > 0)
      SetPPIPrinter(s.x, s.y);

    s = dc->GetSize();
    if (s.x > 0)
      SetPageSizePixels(s.x, s.y);
    SetDC(dc);
  }
  m_bBadDriver = dc == NULL;
  return !m_bBadDriver;
}

TwxPrintOut::TwxPrintOut(const TPRINT_RCD* prcd)
           : wxPrintout(_GetAppTitle()), m_bBadDriver(false)
{
	InitDC(prcd, _GetAppTitle());
}

///////////////////////////////////////////////////////////
// TwxPrintOutCache
///////////////////////////////////////////////////////////

class TwxPrintOutCache
{
  unsigned long m_signature;
  TwxPrintOut* m_po;
  bool m_bLocked;

protected:
  unsigned long Signature(const TPRINT_RCD* prcd) const;

public:
  TwxPrintOut& Get(const TPRINT_RCD* prcd);
  void Reset();

  void Lock() { m_bLocked = true; }
  void Unlock() { m_bLocked = false; }

  bool Ok() const { return m_po != NULL; }
  bool Locked() const { return m_bLocked; }
  bool Printing() const { return Ok() && Locked(); }

  TwxPrintOutCache();
  ~TwxPrintOutCache();
} m_PrintoutCache;

unsigned long TwxPrintOutCache::Signature(const TPRINT_RCD* prcd) const
{
  unsigned long h = 0;
  if (prcd != NULL)
  {
    const unsigned char* data = (const unsigned char*)prcd;
    const size_t sz = prcd->GetSize();
    for (size_t c = 0; c < sz; c++)
    {
      h = (h << 2) + data[c];
      const unsigned long i = h & 0xC0000000;
      if (i) h = (h ^ (i >> 12)) & 0x3FFFFFFF;
    }
  }
  return h;
}

void TwxPrintOutCache::Reset()
{
  wxASSERT(!Locked());
  if (m_po != NULL)
  {
    delete m_po;
    m_po = NULL;
  }
}

TwxPrintOut& TwxPrintOutCache::Get(const TPRINT_RCD* prcd)
{
  if (!Locked())
  {
    if (prcd == NULL)
    {
      if (m_po == NULL)
        m_po = new TwxPrintOut;
    }
    else
    {
      unsigned long signature = Signature(prcd);
      if (m_po != NULL && m_signature == signature)
        return *m_po;
      Reset();
      m_po = new TwxPrintOut(prcd);
      m_signature = signature;
    }
  }
  wxASSERT(m_po != NULL);
  return *m_po;
}

TwxPrintOutCache::TwxPrintOutCache() : m_signature(0), m_po(NULL) 
{ }

TwxPrintOutCache::~TwxPrintOutCache() 
{ 
  // Reset();   // Essendo un oggetto statico la delete m_po non funziona!
}

///////////////////////////////////////////////////////////
// TPrintDC
///////////////////////////////////////////////////////////

// Flag di reset ad inizio pagina
bool TPrintDC::_page_start = false;

void TPrintDC::SetPageStart() 
{ _page_start = true; }

wxDC& TPrintDC::GetDC(bool)
{
	_dc = m_PrintoutCache.Get(NULL).GetDC();         // Forza display context corrente
  if (_page_start)	
  {
    _dirty = -1;
    _page_start = false;
  }
	return TDC::GetDC(false);
}

void TPrintDC::KillDC()
{
  _dc = NULL;  // _dc is owned by wxPrintout
}

TPrintDC::TPrintDC(wxWindow* owner) : TDC(owner)
{ 
  _page_start = false;
}

TPrintDC::~TPrintDC()
{
	KillDC();
}

///////////////////////////////////////////////////////////
// Printing management :-(((((
///////////////////////////////////////////////////////////

BOOLEAN xvt_app_escape(int esc_code, PRINT_RCD* rcd, long* ph, long* pw, long* pvr, long* phr)
{
  switch (esc_code)
	{
	case XVT_ESC_GET_PRINTER_INFO:
    if (ph)	*ph = *pw = 0;
    if (pvr) *pvr = *phr = 0;
    if (rcd == NULL || xvt_print_is_valid(rcd))
		{
      const TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)rcd);
	    int w, h;
      if (ph)
      {
			  po.GetPageSizePixels(&w, &h);
			  *pw = w; *ph = h;
      }
      if (pvr)
      {
			  po.GetPPIPrinter(&w, &h);
			  *phr = w; *pvr = h;
      }
  		return TRUE;
		}
		break;
	case XVT_ESC_SET_PRINTER_INFO:
		if (xvt_print_is_valid(rcd) && ph != NULL && pw != NULL)
		{
      TPRINT_RCD* prcd = (TPRINT_RCD*)rcd;
  		wxPrintData data;

      RCD2data(prcd, data);
      data.SetOrientation(*ph > *pw ? wxPORTRAIT : wxLANDSCAPE);
      data.ConvertToNative();
      data2RCD(data, prcd);

      TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)rcd);
      po.InitDC(prcd, _GetAppTitle());
   
      return true;
    }
    break;
  default:
		break;
	}
	return FALSE;
}

BOOLEAN xvt_dm_post_page_setup(PRINT_RCD* precp)
{
	wxPageSetupDialog dlg((wxWindow*)TASK_WIN);
  TPRINT_RCD* rcd = (TPRINT_RCD*)precp;

  wxPageSetupData& pdd = dlg.GetPageSetupData();
  wxPrintData& data = pdd.GetPrintData();

  RCD2data(rcd, data);

  pdd.EnableMargins(false);

  const BOOLEAN ok = dlg.ShowModal() == wxID_OK;
	if (ok)
	{
    data2RCD(data, rcd);
    m_PrintoutCache.Reset();
	}

	return ok;
}

void xvt_dwin_draw_image_on_pdf(WINDOW win, const char* name, RCT* dest)
{
  wxASSERT(win == PRINTER_WIN);
	const wxRect dst = RCT2Rect(dest);
  TwxPDFDC& dc = *wxStaticCast(&GetTDCMapper().GetDC(win), TwxPDFDC);
  dc.DrawImage(name, dst);  
}

long xvt_fmap_get_family_sizes(PRINT_RCD *precp, char *family, long *size_array, BOOLEAN *scalable, long max_sizes)
{
  long size = 0;
	*scalable = FALSE;

#ifdef __WXMSW__
  if (xvt_print_is_valid(precp))
	{
  	const TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)precp);
    if (!po.HasBadDriver())
		  size = OsWin32_EnumerateSizes(po.GetDC()->GetHDC(), family, size_array, scalable, max_sizes);
	}
  else
  {
    size = OsWin32_EnumerateSizes(NULL, family, size_array, scalable, max_sizes);
  }
#else
  size = OsLinux_EnumerateSizes(family, size_array, scalable, max_sizes);
#endif

	return size;
}

long xvt_fmap_get_families(PRINT_RCD *precp, char **family_array, long max_families)
{
	long size = 0;
 	family_array[0] = NULL;

#ifdef __WXMSW__
  if (xvt_print_is_valid(precp))
  {
  	TwxPrintOut& po = m_PrintoutCache.Get((TPRINT_RCD*)precp);
    if (!po.HasBadDriver())
    {
	    size = OsWin32_EnumerateFamilies(po.GetDC()->GetHDC(), family_array, max_families);
      if (size == 0)
        po.SetBadDriver(true);
    }
  }
  else
  {
    wxFrame* tw = (wxFrame*)TASK_WIN;
    wxClientDC dc(tw);
	  size = OsWin32_EnumerateFamilies(dc.GetHDC(), family_array, max_families);
  }
#else
  size = OsLinux_EnumerateFamilies(family_array, max_families);
#endif
	
  return size;
}

void xvt_print_close(void)
{
	// Nothing to do ?
}

BOOLEAN xvt_print_close_page(PRINT_RCD* WXUNUSED(precp))
{
  BOOLEAN ok = m_PrintoutCache.Printing();
  if (ok)
  {
    const TwxPrintOut& po = m_PrintoutCache.Get(NULL);
    wxDC* dc = po.GetDC();
	  dc->EndPage();
  }
  return ok;
}

PRINT_RCD* xvt_print_create(int *sizep)
{
	return xvt_print_create_by_name(sizep, NULL);
}

// Impone il nome ad un PRINT_RCD.
// Utilizzato per la definiz. della stamp. virtuale PDF predefinita
int xvt_print_set_name(PRINT_RCD* precp, const char* name)
{
  if (precp == NULL)
    return 0;

  if (name == NULL || !*name)
    return 0;

  wxString n = name;

#ifdef __WXMSW__
  wxStrncpy(((char*)precp) + 4, name, 32);
#else
	TPRINT_RCD* rcd = (TPRINT_RCD*)precp;
	wxPrintData data;
	
  rcd->GetData(data);
  data.SetPrinterName(n);
  rcd->SetData(data);
#endif

  return n.Length();
}

// Nuova funzione inventata da Aga
PRINT_RCD* xvt_print_create_by_name(int* sizep, const char* name)
{
	TPRINT_RCD* pr = NULL;
  *sizep = 0;
  
  const bool ispdf = name != NULL && xvt_str_compare_ignoring_case(name, XVT_PDF_PRINTER_NAME) == 0;
  if (ispdf)
    name = NULL;

#ifdef __WXMSW__
	void* data = OsWin32_GetPrinterInfo(*sizep, name);
  if (data == NULL)
  {
    SLIST plist = xvt_print_list_devices();
    SLIST_ELT pitem = xvt_slist_get_first(plist);
    name = xvt_slist_get(plist, pitem, NULL);
    data = OsWin32_GetPrinterInfo(*sizep, name);
    xvt_slist_destroy(plist);
  }
	if (data != NULL)
	{
  	pr = new TPRINT_RCD;
		pr->SetData(data, *sizep);
		*sizep += 4;  // Spazio per puntatore iniziale
		delete data;
	}
#else
  wxPrintData data;

  data.SetPrinterName(name == NULL ? "" : name);
	pr = new TPRINT_RCD;
 	data2RCD(data, pr);
	*sizep = pr->GetSize();
#endif

  if (ispdf)
    xvt_print_set_name(pr, XVT_PDF_PRINTER_NAME);
	
  return pr;
}

WINDOW xvt_print_create_win(PRINT_RCD* precp, const char* title)
{
  bool ok = xvt_print_is_valid(precp) != FALSE;
  if (ok)
  {
    TPRINT_RCD* rcd = (TPRINT_RCD*)precp;
    if (m_PrintoutCache.Printing())
    {
      TwxPrintOut& po = m_PrintoutCache.Get(NULL);
      ok = po.InitDC(rcd, title);
      if (ok)
      {
        po.OnBeginPrinting();
        po.OnBeginDocument(1, 32000);
      }
    }
    else
    {
      TwxPrintOut& po = m_PrintoutCache.Get(rcd);
      ok = po.InitDC(rcd, title);
    }
  }
  return ok ? PRINTER_WIN : NULL_WIN;
}

void xvt_print_destroy(PRINT_RCD* precp)
{
  if (precp != NULL)
	  delete precp;
}

RCT* xvt_print_get_next_band(void)
{
	static bool yes = false;
	static RCT rct;
  yes = !yes;

	if (!yes)
		return NULL;

  const TwxPrintOut& po = m_PrintoutCache.Get(NULL);
	int w, h;
	po.GetPageSizePixels(&w, &h);
	rct.left = rct.top = 0;
	rct.right = w;
	rct.bottom = h;

	return &rct;
}

BOOLEAN xvt_print_is_pdf(const PRINT_RCD* precp)
{
  char strName[MAX_PATH];  
  xvt_print_get_name(precp, strName, sizeof(strName));
  return xvt_str_compare_ignoring_case(strName, XVT_PDF_PRINTER_NAME) == 0;
}

BOOLEAN xvt_print_pdf_version(char* version, int size)
{
  const BOOLEAN ok = version && size > 12;
  if (ok)
  {
    wxString str = "PDFlib ";
    str += PDFLIB_VERSIONSTRING;
    wxStrncpy(version, str, size);
  }
  return ok;
}

BOOLEAN xvt_print_is_valid(const PRINT_RCD* precp)
{
  BOOLEAN ok = precp != NULL && precp->pr == NULL;
	if (ok)
	{
		const TPRINT_RCD* rcd = (const TPRINT_RCD*)precp;

#ifdef __WXMSW__
		ok = OsWin32_CheckPrinterInfo(rcd->m_data, rcd->m_size);
#else
		wxPrintData data;
    RCD2data(rcd, data);
		ok = data.Ok();
#endif
	}
	return ok;
}

int xvt_print_get_name(const PRINT_RCD* precp, char* name, int sz_s)
{
  if (!xvt_print_is_valid(precp))
    return 0;

#ifdef __WXMSW__
  wxString n = ((const char*)precp) + 4;
  if (n.Length() >= 30)
  {
    double dBest = 0.0;
    wxString strBest = n;
    SLIST plist = xvt_print_list_devices();
    for (SLIST_ELT pitem = xvt_slist_get_first(plist);
         pitem != NULL; pitem = xvt_slist_get_next(plist, pitem)) 
    {
      const char* pname = xvt_slist_get(plist, pitem, NULL);
      const double dScore = xvt_str_fuzzy_compare_ignoring_case(n, pname);
      if (dScore > dBest)
      {
        dBest = dScore;
        strBest = pname;
        if (dScore >= 1.0)
          break;
      }
    }
    xvt_slist_destroy(plist);
    n = strBest;
  }
#else
	TPRINT_RCD* rcd = (TPRINT_RCD*)precp;
	wxPrintData data;
	
  RCD2data(rcd, data);
  wxString n = data.GetPrinterName();
#endif
  if (name != NULL && sz_s > 0)
  {
    wxStrncpy(name, n, sz_s);
    name[sz_s-1] = '\0';
  }
  return n.Length();
}

BOOLEAN xvt_print_open(void)
{
	return m_PrintoutCache.Ok();
}

///////////////////////////////////////////////////////////

class TwxPrintAbortDialog : public wxPrintAbortDialog
{
public:
  void Pulse();
  TwxPrintAbortDialog();
};

void TwxPrintAbortDialog::Pulse()
{
  wxGauge* pGauge = wxStaticCast(FindWindow(wxID_FORWARD), wxGauge);
  pGauge->Pulse();
}

TwxPrintAbortDialog::TwxPrintAbortDialog()
                   : wxPrintAbortDialog(NULL, _("Stampa"), 
                                        wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
  wxBoxSizer *button_sizer = new wxBoxSizer( wxVERTICAL );

  button_sizer->Add(new wxStaticText(this, wxID_STATIC, _("Stampa in corso..."), 
                                     wxDefaultPosition, wxSize(480,-1)), 0, wxALL, 10 );
  button_sizer->Add(new wxGauge(this, wxID_FORWARD, 1, wxDefaultPosition, wxSize(480,-1)), 0, wxALL, 10 );

  //button_sizer->Add(new wxButton(this, wxID_CANCEL, wxT("Annulla") ), 0, wxALL | wxALIGN_CENTER, 10 );
  button_sizer->Add(new wxBitmapButton(this, wxID_CANCEL, xvtart_GetToolResource(102,32) ), 0, wxALL | wxALIGN_CENTER, 10 );

  SetAutoLayout(true);
  SetSizer(button_sizer);

  button_sizer->Fit(this);
  button_sizer->SetSizeHints(this);

  Show();
  Update();
}

void CreateAbortWindow()
{
  wxPrinterBase::sm_abortWindow = new TwxPrintAbortDialog;
}

void DestroyAbortWindow()
{
  if (wxPrinterBase::sm_abortWindow != NULL)
  {
    wxPrinterBase::sm_abortWindow->Hide();
    delete wxPrinterBase::sm_abortWindow;
    wxPrinterBase::sm_abortWindow = NULL;
  }
}

///////////////////////////////////////////////////////////

BOOLEAN xvt_print_start_thread(BOOLEAN(*print_fcn)(long), long data)
{
  const wxString strDir = ::wxGetCwd(); // Memorizzo la directory corrente (Acrobat la cambia)

  wxBeginBusyCursor();
  m_PrintoutCache.Reset(); // Forza nuovo contesto di stampa
  TwxPrintOut& po = m_PrintoutCache.Get(NULL);
  m_PrintoutCache.Lock();
  wxEndBusyCursor();

  CreateAbortWindow();
  const BOOLEAN success = print_fcn(data);
  DestroyAbortWindow();

  if (!po.HasBadDriver())
  {
    po.OnEndDocument();
    po.OnEndPrinting();
  }

  m_PrintoutCache.Unlock();
  m_PrintoutCache.Reset();

  ::wxSetWorkingDirectory(strDir);    // Ripristino la directory corrente (Acrobat l'ha cambiata)

  return success;
}

BOOLEAN xvt_print_suspend_thread()
{
  BOOLEAN ok = m_PrintoutCache.Printing();
  if (ok)
  {
    TwxPrintOut& po = m_PrintoutCache.Get(NULL);
    po.OnEndDocument(); // Sembra generare una pagina vuota indesiderata
    po.OnEndPrinting();
  }
  return ok;
}

BOOLEAN xvt_print_restart_thread()
{
	TwxPrintOut& po = m_PrintoutCache.Get(NULL);
  po.OnBeginPrinting();
  po.OnBeginDocument(1, 32000);

  return TRUE;
}

BOOLEAN xvt_print_open_page(PRINT_RCD* WXUNUSED(precp))
{
  BOOLEAN ok = m_PrintoutCache.Printing();
	if (ok)
	{
    if (wxPrinterBase::sm_abortIt)
      ok = FALSE;
    else
    {
      TwxPrintOut& po = m_PrintoutCache.Get(NULL);
		  po.GetDC()->StartPage();
      TPrintDC::SetPageStart();   // Flag per azzeramento dati DC
      
      // Aggiorna barra di attesa
      TwxPrintAbortDialog* pad = wxDynamicCast(wxPrinterBase::sm_abortWindow, TwxPrintAbortDialog);
      if (pad != NULL)
      {
        pad->Pulse();
        wxWakeUpIdle();
      }
    }
	}
	return ok;
}

///////////////////////////////////////////////////////////
// Added by XVAGA
///////////////////////////////////////////////////////////

#ifdef LINUX
static const char * cups_file = "/etc/cups/printers.conf";
static const char * cups_local_file = "./printers.conf";
static const char * prcap_local_file = "./printcap";

static bool is_cups()
{
  static int printer_system = -1;

  if (printer_system < 0)
    printer_system = xvt_fsys_file_exists(cups_file) ? 1 : 2;
  
  return printer_system == 1;
}
#endif

SLIST xvt_print_list_devices()
{
  SLIST list = xvt_slist_create();
#ifdef __WXMSW__
  const DWORD dwFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
  const int level = xvt_sys_get_os_version() >= XVT_WS_WIN_NT ? 4 : 5;
  DWORD dwSize = 0, dwPrinters = 0;
  ::EnumPrinters (dwFlags, NULL, level, NULL, 0, &dwSize, &dwPrinters); 
  if (dwSize > 0)
  {
    BYTE* pBuffer = new BYTE[dwSize];
    ::EnumPrinters (dwFlags, NULL, level, pBuffer, dwSize, &dwSize, &dwPrinters); 
    if (level == 4)
    {
      const PRINTER_INFO_4* pPrnInfo = (const PRINTER_INFO_4*)pBuffer;
      for (UINT i=0; i < dwPrinters; i++) 
      {
        xvt_slist_add_at_elt(list, NULL, pPrnInfo->pPrinterName, NULL);
        pPrnInfo++;
      }
    }
    else
    {
      const PRINTER_INFO_5* pPrnInfo = (const PRINTER_INFO_5*)pBuffer;
      for (UINT i=0; i < dwPrinters; i++) 
      {
        xvt_slist_add_at_elt(list, NULL, pPrnInfo->pPrinterName, NULL);
        pPrnInfo++;
      }
    }
    delete[] pBuffer;
  }
#else
  if (is_cups())
  {
    ifstream p(cups_local_file); 
    char line[4096];
    const char * str_to_find = "Printer";
    
    while (p.getline(line, sizeof(line)))
    {
      char * s;

      if (line[0] == '<' && line[1] != '/' &&
          (s = strstr(line, str_to_find)) != NULL)
      {
        s += strlen(str_to_find);

        while (isspace(*s))
          s++;

        if (*s)
        {
          char * l = s + strlen(s) - 1;

          while (*l == '>' || isspace(*l))
              l--;
          *(l + 1) = '\0';
          xvt_slist_add_at_elt(list, NULL, s, 0L);
        }
      }
    }
  }
  else
  {
    ifstream p(prcap_local_file); // vedere
    char line[4096];

    while (p.getline(line, sizeof(line)))
    {
      if (line[0] != '#')
      {
        const int len = strlen(line);
        int i;

        for (i = 0; i < len; i++)
        {
          const char c = line[i];

          if (!(isalpha(c) || isdigit(c) || isblank(c)))
            break;
        }
        line[i] = '\0';
        xvt_slist_add_at_elt(list, NULL, line, 0L);
      }
    }
  }
#endif
  return list;
}

BOOLEAN xvt_print_set_default_device(const char* name)
{
  BOOLEAN ok = name != NULL && *name > ' ';
#ifdef WIN32
  if (ok)
  {
    wxString pdev(name);
    if (pdev.Find(',') < 0)
    {
      char szDevice[256];
      ::GetProfileString ("devices", pdev, "", szDevice, sizeof(szDevice));
      pdev << ',' << szDevice;
    }
    ok = ::WriteProfileString("windows", "device", pdev) != 0;
  }
#endif
  return ok;
}

BOOLEAN xvt_print_get_default_device(char* name, int namesize)
{
  bool ok = FALSE;

#ifdef __WXMSW__
  ok = ::GetProfileString ("windows", "device", ",,,", name, namesize) != 0;
#else
  *name = '\0';
  if (is_cups())
  {
    ifstream p(cups_local_file); 
    char line[4096];
    const char * str_to_find = "<DefaultPrinter";

    while (p.getline(line, sizeof(line)))
    {
      char * s = strstr(line, str_to_find) ;

      if (s != NULL)
      {
        s += strlen(str_to_find);

        while (isspace(*s))
	        s++;

        if (*s)
        {
          char * l = s + strlen(s) - 1;

          while (*l == '>' || isspace(*l))
            l--;
          *(l + 1) = '\0';
          strcpy(name, s);
        }
        ok = TRUE;
      }
    }
  }
#endif

  return ok;
}

///////////////////////////////////////////////////////////
// Gestione files di configurazione
///////////////////////////////////////////////////////////

int xvt_fsys_get_campo_stp_value(const char* name, char* value, int valsize)
{
  BOOLEAN bFound = FALSE;
#ifdef __WXMSW__
  const char* const stpfile = "c:/campo.stp";
  int p;
  DIRECTORY dir; 
  char exedir[_MAX_PATH], path[_MAX_PATH];
  xvt_fsys_get_default_dir(&dir);
  xvt_fsys_convert_dir_to_str(&dir, exedir, sizeof(exedir));

  for (p = 1; ; p++)
  {
    char para[4]; sprintf(para, "%d", p);
    int len = xvt_sys_get_profile_string(stpfile, para, "Program", "", path, sizeof(path));
    if (len <= 0)
      break;
    if (wxEndsWithPathSeparator(path))
    {
      len--;
      path[len] = '\0';
    }
    if (xvt_str_compare_ignoring_case(path, exedir) == 0)
    {
      xvt_sys_get_profile_string(stpfile, para, name, "", value, valsize);
      bFound = *value > ' ';
      break;
    }
  }
#endif

  return bFound;
}

/*
   @($) CGetCampoIni  FILES

   @(ID)
   Restituisce il nome del file che contiene il prefisso corrente.
   @(FD)

   @(ISV)
   s,s1 = stringhe di lavoro.

   Versione WIN32 e LINUX.
   @(FSV)
*/
const char* xvt_fsys_get_campo_ini()
{
  static wxString prawin;
  if (prawin.IsEmpty())
  {
    BOOLEAN bFound = FALSE;
    char exedir[_MAX_PATH], path[_MAX_PATH];
    // Nelle installazioni sfigate con programmi in rete cerca di stabilire il percorso locale di Campo.ini
    DIRECTORY dir;

    xvt_fsys_get_default_dir(&dir);
    xvt_fsys_convert_dir_to_str(&dir, exedir, sizeof(exedir));
#ifdef WIN32
    if (xvt_fsys_is_network_drive(exedir))
      bFound = xvt_fsys_get_campo_stp_value("CampoIni", path, sizeof(path));

    if (!bFound)
    {
      if (OsWin32_IsWindowsServer())
      {
        xvt_fsys_build_pathname(path, NULL, wxGetHomeDir(), "campo", "ini", NULL);
        bFound = xvt_fsys_file_exists(path);
        if (!bFound)
        {
				  char pathstd[_MAX_PATH];
   	  	  xvt_fsys_build_pathname(pathstd, NULL, exedir, "campo", "ini", NULL);
   	  	  if (xvt_fsys_file_exists(pathstd))
					  wxCopyFile(pathstd, path);
        }
      }
    }

		if (!bFound)
  	  xvt_fsys_build_pathname(path, NULL, exedir, "campo", "ini", NULL);
#else
		if (!bFound)
		{
			char username[32];
   		char ininame[_MAX_FNAME];

	    xvt_sys_get_user_name(username, sizeof(username));
			if (xvt_str_compare_ignoring_case(username, "root") == 0)
				*username = '\0';

	    sprintf(ininame, "campo%s", username);
  	  xvt_fsys_build_pathname(path, NULL, exedir, ininame, "ini", NULL);
   		if (!xvt_fsys_file_exists(path) && *username > ' ')
   		{
				char pathstd[_MAX_PATH];
   	  	xvt_fsys_build_pathname(pathstd, NULL, exedir, "campo", "ini", NULL);
   	  	if (xvt_fsys_file_exists(pathstd))
					wxCopyFile(pathstd, path);
			}
		}
#endif
 		if (!xvt_fsys_file_exists(path))
		{
   		char msg[_MAX_PATH];
     	sprintf(msg, _("Impossibile aprire '%s'"), path);
    	xvt_dm_post_fatal_exit(msg);
		}
    prawin = path;
  }
  return prawin;
}

///////////////////////////////////////////////////////////
// Gestione MD5
///////////////////////////////////////////////////////////

#include "MD5Checksum.h"

BOOLEAN xvt_str_md5(const char* instr, char* outstr)
{
  wxASSERT(outstr != NULL);
  BOOLEAN ok = instr && *instr && outstr;
  if (ok)
  {
    wxStrcpy(outstr, wxMD5Checksum::GetMD5((unsigned char*)instr, strlen(instr)));
    ok = *outstr != '\0';
  }
  return ok;
}

BOOLEAN xvt_fsys_file_md5(const char* path, char* outstr)
{
  wxASSERT(outstr != NULL);
  BOOLEAN ok = path && *path && outstr;
  if (ok)
  {
    wxStrcpy(outstr, wxMD5Checksum::GetMD5(wxString(path)));
    ok = *outstr != '\0';
  }
  return ok;
}

///////////////////////////////////////////////////////////
// Gestione firma digitale
///////////////////////////////////////////////////////////

class TwxSignatureDlg : public wxDialog
{
private:
  virtual bool TransferDataToWindow();
  virtual bool TransferDataFromWindow();

protected:
  wxTextCtrl* AddString(wxSizer* ctlSizer, int id, const char* label, 
                        wxString* str, int nFlags = 0);

public:
  wxString m_strUser, m_strPIN;
  bool m_bMark;

  TwxSignatureDlg();
};

bool TwxSignatureDlg::TransferDataToWindow()
{
  FindWindowById(1001, this)->SetLabel(m_strUser);
  // FindWindowById(1002, this)->SetLabel(m_strPin); // Non riproporre
  wxCheckBox* cb = wxStaticCast(FindWindowById(1003, this), wxCheckBox);
  cb->SetValue(m_bMark);
  return true;
}

bool TwxSignatureDlg::TransferDataFromWindow()
{
  m_strUser = FindWindowById(1001, this)->GetLabel();
  m_strPIN = FindWindowById(1002, this)->GetLabel();
  wxCheckBox* cb = wxStaticCast(FindWindowById(1003, this), wxCheckBox);
  m_bMark = cb->IsEnabled() && cb->GetValue();
  return !m_strUser.IsEmpty() && !m_strPIN.IsEmpty();
}

wxTextCtrl* TwxSignatureDlg::AddString(wxSizer* ctlSizer, int id, const char* label, wxString* str, int nFlags)
{
  wxStaticText* lbl = new wxStaticText(this, wxID_ANY, label);
  wxTextCtrl* txt = new wxTextCtrl(this, id, wxEmptyString, wxDefaultPosition, wxSize(120, -1), 
                                   nFlags, wxTextValidator(wxFILTER_ASCII, str));
  ctlSizer->Add(lbl, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 4);
  ctlSizer->Add(txt, 1, wxALIGN_LEFT | wxALL, 4);

  return txt;
}

TwxSignatureDlg::TwxSignatureDlg() : wxDialog(NULL, wxID_ANY, "ESigner", wxDefaultPosition) 
{
  wxSizer* ctlSizer = new wxFlexGridSizer(4, 2, 4, 4);
  AddString(ctlSizer, 1001, "Utente", &m_strUser);
  AddString(ctlSizer, 1002, "PIN", &m_strPIN, wxTE_PASSWORD);

  wxCheckBox* ctlMark = new wxCheckBox(this, 1003, "Marcatura temporale");
  if (xvt_net_get_status() <= 0)
    ctlMark->Disable();
  ctlSizer->AddSpacer(4);
  ctlSizer->Add(ctlMark, 0, wxALIGN_LEFT | wxALL, 4);

  wxSizer* ctlButtonSizer = CreateButtonSizer(wxOK | wxCANCEL);

  wxBoxSizer* ctlTopSizer = new wxBoxSizer(wxVERTICAL);
  ctlTopSizer->Add(ctlSizer,   0, wxALIGN_CENTER);
  ctlTopSizer->Add(ctlButtonSizer, 0, wxALIGN_CENTER);

  SetSizer(ctlTopSizer);
  ctlTopSizer->SetSizeHints(this);
}

typedef 
  int (*dllSignMethod)   (
    char *operation,                 //operazione : S     (firma file), 
                                     //             S+D   (firma directory), 
                                     //             S+T   (firma + marca file),
                                     //             S+T+D (firma + marca directory)
    char *method,                    //metodo     : F (file cert), P (file PFX), T (token/smartcard)
    char *inputFile,                 //percorso file di input
    char *outputFile,                //percorso file di output (senza estensione)
    char *timestampOutputFile,       //percorso file di output TSR (marca temporale separata)
    char *extension,                 //estensione in caso di input PDF
    char *certificateFile,           //nome file .cer o .crt contenente il certificato
    char *kprivFile,                 //nome file .pem contenente la chiave privata
    char *pfxFile,                   //nome file pfx contenente certificato e chiave privata
    char *pkcs11Dll,                 //nome dll PKCS11
    char *pin_passphrase,            //passphrase o PIN (per chiave privata, pfx o token)
    char *directoryTokenCache,       //directory cache certificati estratti da token
    char *indexCertificateOnToken,   //numero certificato del token 
    char *tsaURI,                    //URI della TSA (http:\\....)
    char *tsaUsername,               //Username TSA
    char *tsaPassword,               //Password TSA
    char *tsaPolicy,                 //Policy TSA
    char *tsaCoding,                 //Codifica TSA: binary o base64
    char *rootCADir)                 //percorso directory contenente i certificati della rootca
    ;

typedef int (*dllVerifyMethod) (
    char *operation,           //operazione : V     (verifica file), 
                                     //             V+D   (verifica directory), 
    char *inputFileName,       //percorso file di input
    char *responseFileName,    //percorso file di esito
    char *rootCADir,           //percorso directory contenente i certificati della rootca
    char *rootTSADir,          //percorso directory contenente i certificati delle rootTSA
    char *resultDescription    //parametro di output contenente l'esito della verifica
);

class TEsigner
{
#ifdef WIN32
  HMODULE m_hESigner;
  dllSignMethod _SignPDF;
  dllVerifyMethod _VerifyPDF;
#endif
  
  wxString m_strUser, m_strPin, m_strDllFile, m_strCertificate;
  wxString m_strTSAuri, m_strTSAuser, m_strTSApwd, m_strPolicy, m_strTSAcoding;
  bool m_bMark;

public:
  bool Init(bool on);
  bool Sign(const wxString& strInput, wxString& strOutput);
  bool Verify(const wxString& strInput);

  TEsigner();
  ~TEsigner();
} __TheSigner;

TEsigner::TEsigner() : m_bMark(false) 
#ifdef WIN32
   , m_hESigner(NULL), _SignPDF(NULL), _VerifyPDF(NULL)
#endif
{}

TEsigner::~TEsigner() 
{ Init(false); }

bool TEsigner::Init(bool bLoad)
{
  bool ok = false;
#ifdef WIN32
  if (bLoad)
  {
    char str[_MAX_PATH] = ""; 
    xvt_sys_get_profile_string(NULL, NULL, "Study", "", str, sizeof(str));
    wxString strConfig; strConfig = str;
    if (!wxEndsWithPathSeparator(strConfig))
      strConfig << wxFILE_SEP_PATH;
    strConfig << "config" << wxFILE_SEP_PATH << wxGetHostName() << ".ini";

    if (m_hESigner == NULL)
    {
      m_hESigner = ::LoadLibrary("esigner.dll");
      if (m_hESigner != NULL)
      {
        _SignPDF   = (dllSignMethod)::GetProcAddress(m_hESigner, "Sign");
        _VerifyPDF = (dllVerifyMethod)::GetProcAddress(m_hESigner, "Verify");
        m_strPin.Empty();

        TwxSignatureDlg dlg;
        if (m_strUser.IsEmpty())
          m_strUser = wxGetUserId();
        dlg.m_strUser = m_strUser;
        dlg.m_bMark = m_bMark;
        if (dlg.ShowModal() == wxID_OK)
        {
          m_strUser = dlg.m_strUser;
          m_strPin= dlg.m_strPIN;
          m_bMark = dlg.m_bMark;
        }
        else
          return ok = false;

        xvt_sys_get_profile_string(strConfig, "fd", "Cert_"+m_strUser, "", str, sizeof(str));
        if (*str == '\0') // Se non trova l'utente corrente di riprova con Guest
          xvt_sys_get_profile_string(strConfig, "fd", "Cert_Guest", "", str, sizeof(str));

        wxStringTokenizer strDllCert(str, ",");
        m_strDllFile = strDllCert.GetNextToken(); m_strDllFile.MakeLower();
        m_strCertificate = strDllCert.GetNextToken();
        if (m_strCertificate.IsEmpty() && m_strDllFile.EndsWith(".dll"))
          m_strCertificate = "0";

        if (m_bMark)
        {
          m_strTSAuri = strDllCert.GetNextToken();
          m_strTSAuser = strDllCert.GetNextToken();
          m_strTSApwd = strDllCert.GetNextToken();
          m_strPolicy = strDllCert.GetNextToken();
          m_strTSAcoding = strDllCert.GetNextToken();

          if (m_strTSAcoding.IsEmpty())
            m_strTSAcoding = wxT("binary");
          
            if (m_strTSAuri.IsEmpty())
          {
            wxString msg;
            msg << "Mancano i parametri per la marcatura temporale"
                << "\nControllare Cert_" << m_strUser 
                << " nel paragrafo [fd]\ndel file " << strConfig;
            xvt_dm_post_warning(msg);
            m_bMark = false;
          }
        }
      }
    }
    ok = m_hESigner != NULL && _SignPDF != NULL && _VerifyPDF != NULL;
    if (ok)
    {
      ok = wxFileExists(m_strDllFile);
      if (!ok)
      {
        wxString msg;
        msg << "Impossibile caricare il certificato/driver " << m_strDllFile
            << "\nControllare Cert_" << m_strUser 
            << " nel paragrafo [fd]\ndel file " << strConfig;
        xvt_dm_post_error(msg);
      }
    }
    else
      xvt_dm_post_error("Impossibile caricare 'ESigner.dll'");
  }
  else
  {
    if (m_hESigner != NULL)
    {
      ::FreeLibrary(m_hESigner);
      m_hESigner = NULL;
      _SignPDF = NULL;
      _VerifyPDF = NULL;
      m_strPin.Empty();
    }
  }
#endif
  return ok;
}

bool TEsigner::Sign(const wxString& strInput, wxString& strOutput)
{
  if ((m_strPin.IsEmpty() || m_strDllFile.IsEmpty()) && !Init(true))
    return false;

  const char* ext = strInput.Lower().EndsWith(".pdf") ? ".pdf.p7m" : "";

  if (strOutput.IsEmpty())
    strOutput = strInput + ".p7m";
  ::wxRemoveFile(strOutput);  // Altrimenti la fantastica dll s'incazza come una biscia

  const wxString strFile = strOutput.BeforeFirst('.');

  int res = 0;
  if (m_strDllFile.Lower().EndsWith(".dll"))
  {
    if (m_bMark && !m_strTSAuri.IsEmpty()) // Firma con marcatura temporale 
    {
      res = _SignPDF("S", "T", // "S"ignature with "T"oken or smartcard
                     (char*)(const char*)strInput, (char*)(const char*)strFile,
                     NULL, (char*)ext, NULL, NULL, NULL, (char*)(const char*)m_strDllFile,
                     (char*)(const char*)m_strPin, NULL, (char*)(const char*)m_strCertificate,
                     (char*)(const char*)m_strTSAuri, (char*)(const char*)m_strTSAuser, 
                     (char*)(const char*)m_strTSApwd, (char*)(const char*)m_strPolicy, 
                     (char*)(const char*)m_strTSAcoding, NULL);
    }
    else                                      // Firma normale
    { 
      res = _SignPDF("S", "T", // "S"ignature with "T"oken or smartcard
                     (char*)(const char*)strInput, (char*)(const char*)strFile,
                     NULL, (char*)ext, NULL, NULL, NULL, (char*)(const char*)m_strDllFile,
                     (char*)(const char*)m_strPin, NULL, (char*)(const char*)m_strCertificate,
                     NULL, NULL, NULL, NULL, NULL, NULL);
    }
  }
  else
  {
    if (m_bMark && !m_strTSAuri.IsEmpty()) // Firma con marcatura temporale 
    {
      res = _SignPDF("S", "P", // "S"ignature with "P"fx file
                     (char*)(const char*)strInput, (char*)(const char*)strFile,
                     NULL, (char*)ext, NULL, NULL, (char*)(const char*)m_strDllFile, NULL,
                     (char*)(const char*)m_strPin,  NULL, NULL,
                     (char*)(const char*)m_strTSAuri, (char*)(const char*)m_strTSAuser, 
                     (char*)(const char*)m_strTSApwd, (char*)(const char*)m_strPolicy, 
                     (char*)(const char*)m_strTSAcoding, NULL);
    }
    else
    {
      res = _SignPDF("S", "P", // "S"ignature with "P"fx file
                     (char*)(const char*)strInput, (char*)(const char*)strFile,
                     NULL, (char*)ext, NULL, NULL, (char*)(const char*)m_strDllFile, NULL,
                     (char*)(const char*)m_strPin,  NULL, NULL,
                     NULL, NULL, NULL, NULL, NULL, NULL);
    }
  }

  if (res == 0)
  {
    if (!xvt_fsys_file_exists(strOutput))
    {
      wxString str = "ESigner.dll can't create " + strOutput;
      xvt_dm_post_error(str);
      res = -1;
    }
  }
  else
  {
    wxString str;
    str.Printf("ESigner.dll error %d", res);
    xvt_dm_post_error(str);
  }

  return res == 0;
}

bool TEsigner::Verify(const wxString& strInput)
{
  int res = -1;
  if (Init(!strInput.IsEmpty()))
  {
    char file[_MAX_PATH], result[_MAX_PATH];
    wxStrncpy(file, strInput, _MAX_PATH);
    res = _VerifyPDF("V", file, NULL, NULL, NULL, result);
  }
  return res == 0;
}

BOOLEAN xvt_sign_start()
{
  return __TheSigner.Init(true);
}

BOOLEAN xvt_sign_stop()
{
  __TheSigner.Init(false);
  return TRUE;
}

BOOLEAN xvt_sign_file(const char* input_name, char* output_name)
{
  wxString strInput = input_name, strOutput = output_name;
  if (strInput.IsEmpty())
  {
    FILE_SPEC fs; 
    xvt_fsys_convert_str_to_fspec(strInput, &fs);
    if (xvt_dm_post_file_open(&fs, "File") != FL_OK)
      return false;
  }
  strInput.MakeLower();
  BOOLEAN ok = __TheSigner.Sign(strInput, strOutput);
  if (ok && output_name != NULL)
    wxStrncpy(output_name, strOutput, _MAX_PATH);
  return ok;
}


BOOLEAN xvt_sign_test(const char* input_name)
{
  wxString strInput = input_name;
  if (strInput.IsEmpty())
  {
    FILE_SPEC fs; 
    xvt_fsys_convert_str_to_fspec(strInput, &fs);
    if (xvt_dm_post_file_open(&fs, "File") != FL_OK)
      return FALSE;
  }
  return __TheSigner.Verify(strInput);
}