/* -*- C++ -*-
   This file implements kabīs printing methods. It is a part of the
   TopLevelWidget implementation. 

   the KDE addressbook

   $ Author: Mirko Boehm $
   $ Copyright: (C) 1996-2000, Mirko Boehm $
   $ Contact: mirko@kde.org
         http://www.kde.org $
   $ License: GPL with the following explicit clarification:
         This code may be linked against any version of the Qt toolkit
         from Troll Tech, Norway. $

   $Revision: 1.6 $	
*/

#include "kab_topwidget.h"
#include "kab_mainwidget.h"
#include "look_basic.h"

#include <qprinter.h>
#include <qpaintdevicemetrics.h>

#include <klocale.h>
#include <kdebug.h>
#include <knotifyclient.h>

void TopLevelWidget::slotPrintEntries()
{ // this slot prints all entries in the order they are displayed
  BunchOfKeys bunch;
  // ----- 
  KabKey key;
  for(unsigned counter=0; counter<api->addressbook()->noOfEntries(); ++counter)
    {
      if(api->addressbook()->getKey(counter, key)!=AddressBook::NoError)
	{
	  kdDebug() << "TopLevelWidget::slotPrintEntries: cannot get entry at "
		    << "index " << counter << "!" << endl;
	} else {
	  bunch.push_back(key);
	}
    }
  printEntries(bunch);
}

void TopLevelWidget::slotPrintEntry()
{
  QPrinter printer;
  QPainter painter;
  AddressBook::Entry entry;
  // ----- variables used to define MINIMAL MARGINS entered by the user:
  int marginTop=0, 
    marginLeft=64, // to allow stapling
    marginRight=0, 
    marginBottom=0;
  register int left, top, width, height;
  // -----
  if(!printer.setup(this))
    {
      emit(setStatus(i18n("Rejected.")));
      KNotifyClient::beep();
      return;
    }
  // ----- create the metrics in accordance to the selection:
  printer.setFullPage(true); // use whole page
  QPaintDeviceMetrics metrics(&printer);
  kdDebug() << "TopLevelWidget::slotPrintEntry: printing on a "
	    << metrics.width() << "x" << metrics.height() 
	    << " size area," << endl << "   " 
	    << "margins are " 
	    << printer.margins().width() << " (left/right) and "
	    << printer.margins().height() << " (top/bottom)." << endl;
  left=QMAX(printer.margins().width(), marginLeft); // left margin
  top=QMAX(printer.margins().height(), marginTop); // top margin
  width=metrics.width()-left
    -QMAX(printer.margins().width(), marginRight); // page width
  height=metrics.height()-top
    -QMAX(printer.margins().height(), marginBottom); // page height
  // ----- get the current entry from the view:
  mainwidget->getView()->getEntry(entry);
  // ----- call the painting method:
  painter.begin(&printer);
  // ----- two per page:
  // painter.setViewport(left, top+height, height/2, width);
  // painter.rotate(270);
  // ----- four per page:
  // painter.setViewport(left, top, width/2, height/2);
  // one per page:
  painter.setViewport(left, top, width, height);
  if(printEntry(entry, QRect(0, 0, metrics.width(), metrics.height()), 
		&painter, 0, true))
    {
      emit(setStatus(i18n("Printing finished.")));
    } else {
      emit(setStatus(i18n("Printing failed.")));
    }
  painter.end();
}

bool TopLevelWidget::printEntry(const AddressBook::Entry& entry, 
				const QRect& window,
				QPainter *painter,
				int top, bool fake, QRect *brect)
{
  // TODO: custom fields, custom (?) for Entry
  const int Width=window.width();
  const int Height=window.height();
  const int Ruler1=Width/32;
  const int Ruler2=2*Ruler1;
  const int Ruler3=3*Ruler1;
  QString text, line1, line2, line3, line4;
  QRect rect;
  // options that will be configured later on in a dialog:
  QFont headline1("utopia", 12, QFont::Normal, true);
  QFontMetrics fmheadline1(headline1);
  QFont headline2("utopia", 10, QFont::Bold, true);
  QFontMetrics fmheadline2(headline2);
  QFont body("utopia", 10, QFont::Normal, false);
  QFontMetrics fmbody(body);
  QFont fixed("courier", 10, QFont::Normal, false);
  QFontMetrics fmfixed(fixed);
  QFont comment("utopia", 10, QFont::Normal, false);
  QFontMetrics fmcomment(comment);
  int y=top;
  int type;
  AddressBook::Entry::Address address;
  // this is used to prepare some fields for printing and decide about
  // the layout later:
  std::list<QStringList> parts;
  // ----- set window the painter works on:
  painter->setWindow(window);
  // ----- first draw a black rectangle on top, containing the entries
  //       name, centered:
  painter->setFont(headline1);
  painter->setBrush(QBrush(black));
  painter->setPen(black);
  api->addressbook()->literalName(entry, text);
  rect=painter->boundingRect(Ruler1, y, Width, Height,
			     Qt::AlignVCenter | Qt::AlignLeft, text);
  rect.setHeight((int)(1.25*rect.height()));
  if(!fake)
    {
      painter->drawRect(0, y, Width, rect.height());
    }
  painter->setPen(white);
  if(!fake)
    {
      painter->drawText(Ruler1, y, Width, rect.height(), 
			Qt::AlignVCenter | Qt::AlignLeft, text);
    }
  //       paint the birthday to the right:
  if(entry.birthday.isValid())
    {
      line1=KGlobal::locale()->formatDate(entry.birthday, true);
      if(!fake)
	{
	  painter->drawText(0, y, Width-Ruler1, rect.height(),
			    Qt::AlignVCenter | Qt::AlignRight, line1);
	}
    }
  y+=rect.height();
  // ----- now draw the data according to the person:
  painter->setFont(body);
  y+=fmbody.lineSpacing()/2;
  painter->setPen(black);
  if(!entry.rank.isEmpty())
    {
      line1=entry.rank;
      line1=line1.stripWhiteSpace();
      if(fake)
	{
	  rect=painter->boundingRect(Ruler1, y, Width-Ruler1, Height, 
				     Qt::AlignTop | Qt::AlignLeft, 
				     line1);
	} else {
	  painter->drawText(Ruler1, y, Width-Ruler1, Height, 
			    Qt::AlignTop | Qt::AlignLeft, 
			    line1, -1, &rect);
	}
      y+=rect.height();
    }
  if(!entry.title.isEmpty())
    {
      line2=entry.title;
      line2=line2.stripWhiteSpace();
      if(fake)
	{
	  rect=painter->boundingRect(Ruler1, y, Width-Ruler1, Height, 
				     Qt::AlignTop | Qt::AlignLeft, 
				     line2);
	} else {
	  painter->drawText(Ruler1, y, Width-Ruler1, Height, 
			    Qt::AlignTop | Qt::AlignLeft, 
			    line2, -1, &rect);
	}
      y+=rect.height();
    }
  if(! (entry.rank.isEmpty() && entry.title.isEmpty()))
    {
      y+=fmbody.lineSpacing()/2;
    }
  // ----- fill the parts stringlist, it contains "parts" (printable
  // areas) that will be combined to fill the page as effectively as
  // possible: 
  //       Email addresses:
  if(!entry.emails.isEmpty())
    {
      QStringList list;
      // ----- 
      list.append(entry.emails.count()==1 
		  ? i18n("Email address:")
		  : i18n("Email addresses:"));
      list+=entry.emails;
      parts.push_back(list);
    }
  //       Telephones:
  if(!entry.telephone.isEmpty())
    {
      QStringList list;
      QString line;
      // ----- 
      list.append(entry.telephone.count()==1
		  ? i18n("Telephone:")
		  : i18n("Telephones:"));
      // ----- this is more complex:
      for(unsigned counter=0; counter<entry.telephone.count()/2; counter++)
	{
	  type=0;
	  line=*entry.telephone.at(2*counter);
	  type=line.toInt();
	  line=AddressBook::phoneType((AddressBook::Telephone)(type+1));
	  line+=": "+(*entry.telephone.at(2*counter+1));
	  line=line.stripWhiteSpace();
	  list.append(line);
	}
      parts.push_back(list);
    }
  //       Web pages/URLs:
  if(!entry.URLs.isEmpty())
    {
      QStringList list;
      // ----- 
      list.append(entry.URLs.count()==1
		  ? i18n("Web Page:")
		  : i18n("Web Pages:"));
      list+=entry.URLs;
      parts.push_back(list);
    }
  //       Talk addresses:
  if(!entry.talk.isEmpty())
    {
      QStringList list;
      // ----- 
      list.append(entry.talk.count()==1
		  ? i18n("Talk Address:")
		  : i18n("Talk Addresses:"));
      list+=entry.talk;
      parts.push_back(list);
    } 
  // ----- 
  QRect limits[]= {
    QRect(0, y, Width/2, Height),
      QRect(Width/2, y, Width/2, Height),
      QRect(0, y, Width/2, Height),
      QRect(Width/2, y, Width/2, Height) };
  int heights[4]= { 0, 0, 0, 0 };
  // -----
  std::list<QStringList>::iterator pos=parts.begin();
  for(unsigned counter=0; counter<parts.size(); ++counter)
    {
      const int Offset=counter>1 ? QMAX(heights[0], heights[1]) : 0;
      QStringList list=*pos;
      // -----
      painter->setFont(headline2);
      if(fake)
	{
	  rect=painter->boundingRect
	    (limits[counter].left(), 
	     limits[counter].top()+heights[counter]+Offset, 
	     limits[counter].width(), 
	     limits[counter].height(),
	     Qt::AlignTop | Qt::AlignLeft, 
	     *list.at(0));
	} else {
	  painter->drawText
	    (limits[counter].left(), 
	     limits[counter].top()+heights[counter]+Offset, 
	     limits[counter].width(), 
	     limits[counter].height(),
	     Qt::AlignTop | Qt::AlignLeft, 
	     *list.at(0), -1, &rect);
	}
      heights[counter]+=rect.height();
      // ----- paint the other elements at Ruler1:
      painter->setFont(fixed);
      for(unsigned c2=1; c2<list.count(); ++c2)
	{ // WORK_TO_DO: implement proper line breaking!
	  if(fake)
	    {
	      rect=painter->boundingRect
		(limits[counter].left()+Ruler1,
		 limits[counter].top()+heights[counter]+Offset,
		 limits[counter].width()-Ruler1, 
		 limits[counter].height(),
		 Qt::AlignTop | Qt::AlignLeft, 
		 *list.at(c2));
	    } else {
	      painter->drawText
		(limits[counter].left()+Ruler1,
		 limits[counter].top()+heights[counter]+Offset,
		 limits[counter].width()-Ruler1, 
		 limits[counter].height(),
		 Qt::AlignTop | Qt::AlignLeft, 
		 *list.at(c2), -1, &rect);
	    }
	  heights[counter]+=rect.height();
	}
      // ----- done
      ++pos;
    }
  y=y+QMAX(heights[0], heights[1])+QMAX(heights[2], heights[3]);
  // ^^^^^ done with emails, telephone, URLs and talk addresses
  // ----- now print the addresses:
  if(entry.noOfAddresses()>0)
    {
      y+=fmbody.lineSpacing()/2;
      painter->setFont(headline2);
      if(fake)
	{
	  rect=painter->boundingRect(0, y, Width, Height, 
				     Qt::AlignTop | Qt::AlignLeft, 
				     entry.noOfAddresses()==1 
				     ? i18n("Address:") : i18n("Addresses:"));
	} else {
	  painter->drawText(0, y, Width, Height, 
			    Qt::AlignTop | Qt::AlignLeft, 
			    entry.noOfAddresses()==1 
			    ? i18n("Address:") : i18n("Addresses:"), 
			    -1, &rect);
	}
      y+=rect.height();
      y+=fmbody.lineSpacing()/4;
      painter->setFont(body);
      for(int counter=0; counter<entry.noOfAddresses(); ++counter)
	{
	  bool org=false;
	  entry.getAddress(counter, address);
	  line1=address.headline;
	  text=QString::null;
	  if(!address.position.isEmpty())
	    {
	      text+=address.position;
	    }
	  if(!address.org.isEmpty())
	    {
	      if(!text.isEmpty())
		{
		  text+=", ";
		}
	      text+=address.org;
	      org=true;
	    }
	  if(!address.orgUnit.isEmpty())
	    {
	      if(!org)
		{
		  text+=", ";
		} else {
		  text+=" / ";
		}
	      text+=address.orgUnit;
	      org=true;
	    }
	  if(!address.orgSubUnit.isEmpty())
	    {
	      if(!org)
		{
		  text+=", ";
		} else {
		  text+=" / ";
		}
	      text+=address.orgSubUnit;
	      org=true;
	    }
	  text=text.stripWhiteSpace();
	  if(!text.isEmpty())
	    {
	      line1=line1+" ("+text+")";
	    } 
	  line1=line1.stripWhiteSpace();
	  line2=address.address;
	  // ----- print address in american style, this will need
	  // localisation:
	  line3=address.town
	    +(address.state.isEmpty() ? QString::fromLatin1("") : QString::fromLatin1(", ")+address.state)
	    +(address.zip.isEmpty() ? QString::fromLatin1("") : QString::fromLatin1(" ")+address.zip);
	  line4=address.country;
	  // -----
	  if(line1.isEmpty())
	    {
	      line1=i18n("Unnamed address:");
	    }
	  if(fake)
	    {
	      rect=painter->boundingRect(Ruler1, y, Width-Ruler1, Height, 
					 Qt::AlignTop | Qt::AlignLeft, 
					 line1);
	    } else {
	      painter->drawText(Ruler1, y, Width-Ruler1, Height, 
				Qt::AlignTop | Qt::AlignLeft, 
				line1, -1, &rect);
	    }
	  y+=rect.height();
	  if(!line2.isEmpty())
	    {
	      if(fake)
		{
		  rect=painter->boundingRect(Ruler2, y, Width-Ruler2, Height, 
					     Qt::AlignTop | Qt::AlignLeft, 
					     line2);
		} else {
		  painter->drawText(Ruler2, y, Width-Ruler2, Height, 
				    Qt::AlignTop | Qt::AlignLeft, 
				    line2, -1, &rect);
		}
	      y+=rect.height();
	    }
	  if(!line3.isEmpty())
	    {
	      if(fake)
		{
		  rect=painter->boundingRect(Ruler2, y, Width-Ruler2, Height, 
					     Qt::AlignTop | Qt::AlignLeft, 
					     line3);
		} else {
		  painter->drawText(Ruler2, y, Width-Ruler2, Height, 
				    Qt::AlignTop | Qt::AlignLeft, 
				    line3, -1, &rect);
		}
	      y+=rect.height();
	    }
	  if(!line4.isEmpty())
	    {
	      if(fake)
		{
		  rect=painter->boundingRect(Ruler2, y, Width-Ruler2, Height, 
					     Qt::AlignTop | Qt::AlignLeft, 
					     line4);
		} else {
		  painter->drawText(Ruler2, y, Width-Ruler2, Height, 
				    Qt::AlignTop | Qt::AlignLeft, 
				    line4, -1, &rect);
		}
	      y+=rect.height();
	    }
	  y+=fmbody.lineSpacing()/4;
	  if(!address.deliveryLabel.isEmpty())
	    {
	      if(fake)
		{
		  rect=painter->boundingRect(Ruler2, y, Width-Ruler2, Height, 
					     Qt::AlignTop | Qt::AlignLeft, 
					     i18n("(Deliver to:)"));
		} else {
		  painter->drawText(Ruler2, y, Width-Ruler2, Height, 
				    Qt::AlignTop | Qt::AlignLeft, 
				    i18n("(Deliver to:)"), -1, &rect);
		}
	      y+=rect.height();
	      y+=fmbody.lineSpacing()/4;
	      if(fake)
		{
		  rect=painter->boundingRect(Ruler3, y, Width-Ruler3, Height, 
					     Qt::AlignTop | Qt::AlignLeft, 
					     address.deliveryLabel);
		} else {
		  painter->drawText(Ruler3, y, Width-Ruler3, Height, 
				    Qt::AlignTop | Qt::AlignLeft, 
				    address.deliveryLabel, -1, &rect);
		}
	      y+=rect.height();
	      y+=fmbody.lineSpacing()/2;
	    }
	}
    }
  if(!entry.comment.isEmpty())
    {
      y+=fmbody.lineSpacing()/2;
      if(fake)
	{
	  rect=painter->boundingRect(0, y, Width, Height, 
				     Qt::AlignTop | Qt::AlignLeft | Qt::WordBreak, 
				     entry.comment);
	} else {
	  painter->drawText(0, y, Width, Height, 
			    Qt::AlignTop | Qt::AlignLeft | Qt::WordBreak, 
			    entry.comment, -1, &rect);
	}
      y+=rect.height();
    }
  y+=fmbody.lineSpacing()/2;
  // ----- we are done:
  if(brect!=0)
    {
      *brect=QRect(0, top, Width, y-top);
    }
  if(y<Height)
    {
      return true;
    } else {
      return false;
    }
}

bool TopLevelWidget::printEntries(const BunchOfKeys keys)
{
  QPrinter printer;
  QPainter painter;
  // QRect rect;
  // ----- variables used to define MINIMAL MARGINS entered by the user:
  int marginTop=0, 
    marginLeft=64, // to allow stapling
    marginRight=0, 
    marginBottom=0;
  register int left, top, width, height;
  // -----
  if(!printer.setup(this))
    {
      emit(setStatus(i18n("Rejected.")));
      KNotifyClient::beep();
      return false;
    }
  // ----- create the metrics in accordance to the selection:
  printer.setFullPage(true); // use whole page
  QPaintDeviceMetrics metrics(&printer);
  kdDebug() << "TopLevelWidget::slotPrintEntries: printing on a "
	    << metrics.width() << "x" << metrics.height() 
	    << " size area," << endl << "   " 
	    << "margins are " 
	    << printer.margins().width() << " (left/right) and "
	    << printer.margins().height() << " (top/bottom)." << endl;
  left=QMAX(printer.margins().width(), marginLeft); // left margin
  top=QMAX(printer.margins().height(), marginTop); // top margin
  width=metrics.width()-left
    -QMAX(printer.margins().width(), marginRight); // page width
  height=metrics.height()-top
    -QMAX(printer.margins().height(), marginBottom); // page height
  // ----- 
  painter.begin(&printer);
  painter.setViewport(left, top, width, height);
  return printEntries(keys, &printer, &painter, 
		      QRect(0, 0, metrics.width(), metrics.height()));
  painter.end();
}

bool TopLevelWidget::printEntries(const BunchOfKeys keys, 
				  QPrinter *printer,
				  QPainter *painter,
				  const QRect& window)
{
  BunchOfKeys::const_iterator pos;
  AddressBook::Entry entry;
  QRect brect;
  int ypos=0;
  // -----
  for(pos=keys.begin(); pos!=keys.end(); ++pos)
    {
      if(api->addressbook()->getEntry(*pos, entry)==AddressBook::NoError)
	{ 
	  // ----- do a faked print to get the bounding rect:
	  if(!printEntry(entry, window, painter, ypos, true, &brect))
	    { // it does not fit on the page beginning at ypos:
	      printer->newPage();
	      ypos=0; // WORK_TO_DO: this assumes the entry fits on the whole page
	    }
	  printEntry(entry, window, painter, ypos, false, &brect);
	  ypos+=brect.height();
	} else {
	  kdDebug() << "TopLevelWidget::printEntries: cannot get entry "
		    << (*pos).getKey() << ", skipping." << endl;
	}
    }
  return true;
}
