/* -*- indent-tabs-mode: nil; -*-
 *
 * This file is part of the Mango recipe management distribution.
 * Copyright (c) 2000-2003, Roland Jesse
 * All rights reserved.
 *
 * For license and copyright information see the file COPYRIGHT or 
 * <http://www.opensource.org/licenses/bsd-license.html>.
 *
 * $Id: Printer.cpp,v 1.13 2003/05/11 16:13:06 rjesse Exp $
 */

#include "Printer.h"
#include "Ingredient.h"
#include "resources.h"
#include <qapplication.h>
#include <qpaintdevicemetrics.h>
#include <qprinter.h>

Printer* Printer::m_instance = 0;

Printer*
Printer::getInstance()
{
   if (m_instance == 0)
      m_instance = new Printer();
   return m_instance;
}

Printer*
Printer::getInstance( RecipeModel* model)
{
   if (m_instance == 0)
	  m_instance = new Printer( model);
   else
	  m_instance->setCookbook( model);
   return m_instance;
}

Printer*
Printer::getInstance( const Recipe& recipe)
{
   if (m_instance == 0)
	  m_instance = new Printer( recipe);
   else
	  m_instance->setRecipe( recipe);
   return m_instance;
}

void
Printer::deleteInstance()
{
   if (m_instance != 0) {
	  delete m_instance;
	  m_instance = 0;
   }
}

/*!
  Make sure to call either setCookbook() or setRecipe() bofore using apply().
*/
Printer::Printer()
{
   m_type = UNSET_TYPE;
   init();
}

Printer::Printer( RecipeModel* model)
   : Exporter( model)
{
   init();
}

Printer::Printer( const Recipe& recipe)
   : Exporter( recipe)
{
   init();
}

Printer::~Printer()
{
   delete m_printer;
}

void
Printer::init()					// private
{
   m_printer = new QPrinter( QPrinter::ScreenResolution);
   m_parent = 0;
   setPrinterDefaults();
}

/*! 
  Fill up internal string list.

  Create one string for each line of output. Return a list that is a simple
  concatenation of all those lines.
  */
void
Printer::buildStringList() // private
   throw( Error)
{
   if (m_type == RECIPE)
          convertRecipe();
   else if (m_type == COOKBOOK)
          convertCookbook();
   else {
      QString s( tr( "Printer: unknown type. I only know about recipes and cookbooks."));
      throw Error( s);
   }
}

/*!
  Set the parent widget.

  This widget will be used by apply() for the invocation of the setup
  dialog. In case the parent widget never gets set, the dialog will be
  opened as a top level window.
*/
void
Printer::setParentWidget( QWidget* parent) // public
{
   m_parent = parent;
}

void
Printer::apply()				// public
   throw( Error)
{
   if (! m_printer->setup( m_parent)) {
	  emit message( "Printing aborted", STATUS_NOTICE_DURATION);
	  return;
   }
   
   m_printer->setFullPage( true);
   try {
     render( m_printer);
   }
   catch (Error& e) {
      emit message( e.what(), STATUS_NOTICE_DURATION);
      return;
   }
   emit message( "Printing completed", STATUS_NOTICE_DURATION);
} // apply()

void
Printer::render( const QPaintDevice* pd)   // public
   throw( Error)
{
   const int margin = 10;
   int pageNo = 1;

   try { buildStringList(); }
   catch (Error& e) { throw( e); }
   	  
   if (! m_painter.begin( pd)) throw( Error( tr( "Error opening printer device.")));
   if (m_type == UNSET_TYPE) throw( Error( tr( "Nothing there to print.")));
   if (m_type == COOKBOOK) {    // FIXME: handle cookbooks below
      throw( Error( tr( "Cookbooks not yet printable.")));
   }

   m_painter.setFont( QApplication::font());
   int yPos = 0;                // y-position for each line
   QFontMetrics fm = m_painter.fontMetrics();
   QPaintDeviceMetrics metrics( pd); // width / height
   QStringList::iterator it;
   for (it = m_lines.begin(); it != m_lines.end(); it++) {
      /*
        Make sure that there is enough space left on current page.
        If not, start a new page.
      */
      if (margin + yPos > metrics.height() - margin) {
         QString msg( tr( "Printing (page "));
         msg += QString::number( ++pageNo);
         msg += ")...";
         emit message( msg);
         m_printer->newPage();
         yPos = 0;              // back to top of page.
      }

      m_painter.drawText( margin, margin + yPos, 
                          metrics.width(), fm.lineSpacing(),
                          ExpandTabs | DontClip,
                          (*it));
      yPos += fm.lineSpacing();
   }
   m_painter.end();
} // render()

void
Printer::createCookbookHeader() // protected
{
}

void
Printer::createRecipes() // protected
{
}

void
Printer::createRecipeHeader() // protected
{
}

/*!
  Acts on m_recipe.
*/
void
Printer::createRecipeBody()
{
   QString titleString( tr( "Title: "));
   titleString.append( m_recipe.title());
   m_lines << titleString;

   QString catString( tr( "Categories: "));
   catString.append( m_recipe.categories().join( ", "));
   m_lines << catString;

   QString servString( tr( "Servings: "));
   servString.append( QString::number( m_recipe.yieldQty()));
   int yrange = m_recipe.yieldRange();
   if (yrange) {
      servString.append( "-");
      servString.append( QString::number( yrange));
   }
   servString.append( " ");
   servString.append( m_recipe.yieldUnit());
   m_lines << servString << "";

   // iterate over ingredient categories
   Recipe::Ingredients::const_iterator it;
   for (it = m_recipe.ingredients().begin();
        it != m_recipe.ingredients().end();
        it++) {
      QString catName( it.key());
      if (! catName.isEmpty()) {
         QString cat;
         cat.append( "<b>");
         cat.append( catName);
         cat.append( "</b>");
         m_lines << cat;
      }

      Recipe::IngredientList ingList = it.data();
      Recipe::IngredientList::const_iterator listIt;
      for (listIt = ingList.begin(); listIt != ingList.end(); listIt++) {
         // 7 characters for the amount.
         QString amtString( QString::number( (*listIt).amountQty()));
         int fillchars = 7 - amtString.length();
         QString fillString;
         fillString.fill( ' ', fillchars);
         amtString.prepend( fillString);
         
         QString str;
         str.append( amtString);
         str.append( " ");
         str.append( (*listIt).measurement().description());
         str.append( " ");
         str.append( (*listIt).name());
         m_lines << str;
      } // ingredient list
   } // ingredient categories

   if (!m_recipe.source().isEmpty() || !m_recipe.typist().isEmpty()
       || !m_recipe.created().isNull() || !m_recipe.modified().isNull()) {
      QString str( tr( "Source: "));
      m_lines << str;
   }
   if (!m_recipe.source().isEmpty()) {
      QString str( "           - ");
      str.append( m_recipe.source());
      m_lines << str;
   }
   if (!m_recipe.typist().isEmpty()) {
      QString str( "           - ");
      str.append( tr( "Typed in by "));
      str.append( m_recipe.typist());
      m_lines << str;
   }
   if (!m_recipe.created().isNull()) {
      QString str( "           - ");
      str.append( tr( "Created: "));
      str.append( m_recipe.created().toString( Qt::LocalDate));
      m_lines << str;
   }         
   if (!m_recipe.modified().isNull()) {
      QString str( "           - ");
      str.append( tr( "Last modified: "));
      str.append( m_recipe.modified().toString( Qt::LocalDate));
      m_lines << str;
   }
   
   m_lines << "";
   m_lines << m_recipe.directions();
   m_lines << "\n";
} // createRecipeBody()

/*!
  Acts on m_recipe.
*/
void
Printer::convertRecipe()
{
   createRecipeHeader();
   createRecipeBody();
}

/*!
  Acts on m_cookbook.
*/
void
Printer::convertCookbook()
{
   createCookbookHeader();
   createRecipes();
}

void
Printer::setPrinterDefaults()	// private
{
   m_printer->setColorMode( QPrinter::GrayScale);
   m_printer->setOrientation( QPrinter::Portrait);
   m_printer->setPageOrder( QPrinter::FirstPageFirst);
   m_printer->setPageSize( QPrinter::A4);
}
