/* -*- 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: MMExporter.cpp,v 1.24 2003/05/11 14:45:23 rjesse Exp $
 */

#include "MMExporter.h"
#include "Ingredient.h"
#include "RecipeModel.h"
#include "resources.h"
#include <qfile.h>
#include <qregexp.h>

MMExporter::MMExporter( RecipeModel* model)
   : Exporter( model)
{
}

MMExporter::MMExporter( const Recipe& recipe)
   : Exporter( recipe)
{
}

/*! 
  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
MMExporter::buildStringList() // private
   throw( Error)
{
   if (m_type == RECIPE)
	  convertRecipe( m_recipe);
   else if (m_type == COOKBOOK)
	  convertCookbook();
   else
	  throw Error( tr( "MMExporter: unknown type. I only know about recipes and cookbooks."));
}

QString
MMExporter::getExtensions()     // public
{
   return QString( "Meal-Master recipes (*.mm *.MM *.mmf *.MMF *.mealmaster *.MealMaster *.meal-master *.Meal-Master)");
}

void
MMExporter::apply()		// public
   throw( Error)
{
   if (m_fname.isEmpty())
	  throw Error( tr( "MMExporter: no file name set"));

   try {
	  buildStringList();
   }
   catch (Error& e) {
	  throw e;
   }

   QString fullString = m_lines.join( "\n");
   QFile file( m_fname);
   if (!file.open( IO_WriteOnly)) {
      QString msg( tr( "Could not open file %1 for writing") .arg( m_fname));
	  throw Error( msg);
   }
   QTextStream ts( &file);
   ts << fullString << flush;
   file.close();
} // apply()

/*!
  Creates the header of the internal cookbook.

  The resulting string list is still of generic type and only includes the header
  information. Go and complete it before confronting anyone with it, please.

  BTW: A cookbook header for MealMaster document type is simply a list of
  all recipes in case more than one recipe is going to be exported. No more, no less.
*/
void
MMExporter::createCookbookHeader()
{
   if ((m_type != COOKBOOK) || (m_cookbook->getRecipeList().size() <= 1))
	  return;

   RecipeModel::RecipeList rl = m_cookbook->getRecipeList();
   RecipeModel::RecipeList::const_iterator it = rl.begin();
   while (it != rl.end()) {
	  m_lines << (*it).title();
	  it++;
   }
   m_lines << "\n\n";
}

void
MMExporter::createRecipes()
{
   RecipeModel::RecipeList recipes = m_cookbook->getRecipeList();
   RecipeModel::RecipeList::const_iterator it = recipes.begin();
   while (it != recipes.end()) {
	  convertRecipe( (*it));
	  it++;
   }
}

void
MMExporter::createRecipeHeader()
{
   QString str( "MMMMM----- Meal-Master ");
   str.append( tr( "recipe via Mango", "Means: The recipe was exported by using (= via) Mango."));
   str.append( " " MANGO_VERSION_STRING);
   str.append( "\n");
   m_lines << str;
}

/*!
  Acts on m_recipe.
*/
void
MMExporter::createRecipeBody()
{
   QString titleString( "      Title: ");
     titleString.append( m_recipe.title());
     m_lines << titleString;
   QString catString( " Categories: ");
     catString.append( m_recipe.categories().join( ", "));
     m_lines << catString;
   QString yieldString;
     yieldString.setNum( m_recipe.yieldQty());
     QString servString( "      Yield: ");
       servString.append( yieldString);
       servString.append( " ");
       servString.append( m_recipe.yieldUnit());
       m_lines << servString << "";

   QStringList ingLines;

   // Handle a group with an eventually empty name first
   QString emptyString;
   Recipe::Ingredients::iterator aListIt = m_recipe.ingredients().find( emptyString);
   Recipe::IngredientList aList = aListIt.data();
   if (aList.count() > 0) ingLines += ingredientList2StringList( aList);

   Recipe::Ingredients ings = m_recipe.ingredients();
   Recipe::Ingredients::const_iterator it;
   for (it = ings.begin(); it != ings.end(); it++)
   {
      /* In case an ingredient category name is set:
         export it as:
         MMMMM----------------NAME----------------------
         Thereby: 
           - line length <= 60 char
           - five M's at the beginning
           - category name in the middle
           - rest is filled up with "-"
      */
      QString groupName( it.key());
      if (groupName.isEmpty()) {
         continue;              // next group, empty name already handled
      } else {
         QString group;
         group.append( "MMMMM");
         int numDashes = (60 - 5 - groupName.length()) / 2;
         for (int a = 0; a < numDashes; a++) group.append( "-");
         group.append( groupName);
         for (int b = 0; b < numDashes; b++) group.append( "-");
         ingLines << group;
      }

      // Now go for the ingredient list associated with this category.
      Recipe::IngredientList ingList = it.data();
      ingLines += ingredientList2StringList( ingList);
   } // ingredient categories
   m_lines += ingLines;

   if (!m_recipe.source().isEmpty() || !m_recipe.typist().isEmpty() 
	   || !m_recipe.created().isNull() || !m_recipe.modified().isNull()) {
      QString str;
      QTextOStream( &str)
         << "MMMMM------------------------ 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 ", "This label prefixes the name of the person who typed in the recipe."));
      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 << "";
   QStringList directionLines = wrapText( m_recipe.directions(), 72);
   m_lines += directionLines;

   QString fp( ":Fingerprint: ");
   fp.append( m_recipe.fingerprint());
   m_lines << "" << fp;

   m_lines << "\nMMMMM\n";
} // createRecipeBody()

/*!
  amount: column 0-6
  measurement: column 8-9
  name: column 11+
*/
QStringList
MMExporter::ingredientList2StringList( const Recipe::IngredientList& lst) const
{
   QStringList ret;
   Recipe::IngredientList::const_iterator it;
   for (it = lst.begin(); it != lst.end(); it++) {
      // 7 characters for the amount.
      QString amtString( QString::number( (*it).amountQty()));
      int fillchars = 7 - amtString.length();
      QString fillString;
      fillString.fill( ' ', fillchars);
      amtString.prepend( fillString);
  
      QString meaString( (*it).measurement().mmAbbrev());
      meaString.simplifyWhiteSpace();
      while (meaString.length() < 2)
         meaString.append( ' ');
    
      QString str;
      str.append( amtString);   // col 0..6
      str.append( " ");         // col 7
      str.append( meaString);   // col 8-9
      str.append( " ");         // col 10
      str.append( (*it).name());  // col 11+
      ret << str;
   }
   return ret;
}

void
MMExporter::convertRecipe( const Recipe& recipe)
{
   m_recipe = recipe;
   createRecipeHeader();
   createRecipeBody();
}

void
MMExporter::convertCookbook()
{
   createCookbookHeader();
   createRecipes();
}

/*!
  Wrap text \a str into several lines. Each line is wrapped at position \a at.
  \return stringlist containing a string for each line of text.
*/
QStringList
MMExporter::wrapText( const QString& str, int at) const
{
   QStringList ret;
   QString copy( str);
   bool stop = false;
   while (!stop) {
      QString line( copy.left( at));
      if (line.length() >= copy.length()) stop = true;
      QRegExp rxp( "(\\s\\S*)$", false); // last word in the new line
      rxp.setMinimal( true);    // one word, only one word, please
      line = line.replace( rxp, ""); // remove last word
      copy = copy.remove( 0, line.length());
      line = line.simplifyWhiteSpace();
      line.prepend( " ");       // indent line by one char
      ret << line; // output of current line
   }
   return ret;
}
