/* -*- 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: RecipeMLExporter.cpp,v 1.22 2003/05/25 10:34:40 rjesse Exp $
 */

#include "RecipeMLExporter.h"
#include "RecipeModel.h"
#include "Ingredient.h"

#include <fstream>
#include <iostream>
#include <qfile.h>
#include <assert.h>

RecipeMLExporter::RecipeMLExporter( RecipeModel* model)
   : Exporter( model), m_doc( 0)
{
}

RecipeMLExporter::RecipeMLExporter( const Recipe& recipe)
   : Exporter( recipe), m_doc( 0)
{
}

RecipeMLExporter::~RecipeMLExporter()
{
   delete m_doc;
}

//! Fill up internal m_doc.
void
RecipeMLExporter::buildXMLTree() // private
   throw (Error)
{
   if (m_type == RECIPE)
      recipe2XML();
   else if (m_type == COOKBOOK)
      cookbook2XML();
   else
      throw Error( tr( "RecipeMLExporter: unknown type. I only know about recipes and cookbooks."));
}

QString
RecipeMLExporter::getExtensions() // public
{
   return QString( "RecipeML recipes (*.recipeml *.RecipeML)");
}

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

   try {
      buildXMLTree();
   }
   catch (Error& e) {
      throw e;
   }
   QFile file( m_fname);
   if (! file.open( IO_WriteOnly)) {
      QString s( tr( "Could not open file %1 for writing.")
                 .arg( m_fname));
      throw Error( s);
   }

   QTextStream ts( &file);
   ts.setEncoding( QTextStream::UnicodeUTF8);
//   QString docString = m_doc->toString();
//   ts << docString << flush;
   ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" << flush;
   int indent = 0;
   m_rootElement->save( ts, indent);
   file.close();
}

/*!
  Creates the header of the internal cookbook.

  The resulting document is still of generic type and only includes the header
  information. Go and complete it before confronting anyone with it.
*/
void
RecipeMLExporter::createCookbookHeader() // private
{
   QDomImplementation impl;
   QDomDocumentType doctype = impl.createDocumentType( 
      "recipml",
      "-//FormatData//DTD RecipeML 0.5//EN",
      "http://www.formatdata.com/recipeml/recipeml.dtd");

   // Create temporary document on stack.
   QDomDocument doc = impl.createDocument( "http://www.w3.org/2001/XMLSchema-instance", "RecipeML", doctype);
   // Copy the temporary over to the internal document representation.
   m_doc = new QDomDocument( doc);
}

void
RecipeMLExporter::createRecipes()
{
   std::cerr << "RecipeMLExporter::createRecipes() not yet implemented."
			 << std::endl;
}

void
RecipeMLExporter::createRecipeHeader()
{
   std::cerr << "RecipeMLExporter::createRecipeHeader() not yet implemented."
			 << std::endl;
}

void
RecipeMLExporter::createRecipeBody()
{
   std::cerr << "RecipeMLExporter::createRecipeBody() not yet implemented."
			 << std::endl;
}

/*!
  Convert the complete cookbook into an XML tree.
  
  The result is stored in the m_doc member variable.

  \todo The directions text is currently exported as a single step. That
    might possibly be splitted up.
  \todo What about the menu element?
*/
void
RecipeMLExporter::cookbook2XML() // private
{
   assert( !m_cookbook->isEmpty());
   RecipeModel::RecipeList recipeList = m_cookbook->getRecipeList();

   createCookbookHeader();
     QDomElement recipeml = m_doc->createElement( "recipeml");
     recipeml.setAttribute( "version", "0.5");
     recipeml.setAttribute( "name", m_cookbook->getName());
     m_doc->appendChild( recipeml);
   RecipeModel::RecipeList::const_iterator it;
   for (it = recipeList.begin(); it != recipeList.end(); it++) {
      Recipe current( *it);
      /*
       * "recipe" contains: 
       *   head, description*, equipment?, ingredients, directions, 
       *   nutrition?, diet-exchanges?
       *
       *   "meta" is stored here as child of "recipe" even though the RecipeML
       *   DTD declares it as child of "recipeml". But one file per recipe
       *   is absolutely not desireable.
       */
      QDomElement recipe = m_doc->createElement( "recipe");
      /* "meta" contains: creator, date */
      QDomElement meta = m_doc->createElement( "meta");
      meta.setAttribute( "DC.Creator", current.typist());
      meta.setAttribute( "DC.Date", current.created().toString());
      recipe.appendChild( meta);
      /*
       * "head" contains: 
       *   title, subtitle?, version?, source?, categories?, preptime*, 
       *   yield?
       */
      QDomElement head = m_doc->createElement( "head");
      QDomElement title = createTextElement( "title", current.title());
      head.appendChild( title);

      QDomElement source = createTextElement( "source", current.source());
      head.appendChild( source);

      QString yieldString;
      yieldString.setNum( current.yieldQty());
      QDomElement yield = createTextElement( "yield", yieldString);
      head.appendChild( yield);

      QDomElement preptime = createTextElement( "preptime", current.overallPreparationTime().toString());
      head.appendChild( preptime);

      QDomElement categories = m_doc->createElement( "categories");
      Recipe::Categories curCategories( current.categories());
      Recipe::Categories::iterator catIt;
      for (catIt  = curCategories.begin();
           catIt != curCategories.end(); 
           catIt++)
      {
         QDomElement cat = createTextElement( "cat", *catIt);
         categories.appendChild( cat);
      }
      head.appendChild( categories);
      recipe.appendChild( head);
      /* Ingredients. */
      QDomElement ingredients = m_doc->createElement( "ingredients");
      Recipe::Ingredients curIngredients( current.ingredients());
      Recipe::Ingredients::const_iterator ingIt;
      for (ingIt  = curIngredients.begin();
           ingIt != curIngredients.end();
           ingIt++)
      {
         // We ignore the ingredient categories for now. (ingIt.key())
         Recipe::IngredientList ingL = ingIt.data();
         Recipe::IngredientList::const_iterator ingLIt;
         for (ingLIt = ingL.begin(); ingLIt != ingL.end(); ingLIt++) {
            QDomElement ing = m_doc->createElement( "ing");
            QDomElement amt = m_doc->createElement( "amt");
            QDomElement qty = createTextElement( "qty", QString::number((*ingLIt).amountQty()));
            amt.appendChild( qty);
            QDomElement range = createTextElement( "range", QString::number((*ingLIt).amountRange()));
            amt.appendChild( range);
            QDomElement unit = createTextElement( "unit", (*ingLIt).measurement().mmAbbrev());
            amt.appendChild( unit);
            ing.appendChild( amt);
            QDomElement item = createTextElement( "item", (*ingLIt).name());
            ing.appendChild( item);
            ingredients.appendChild( ing);
         } // ingredient list
      } // ingredients map
      recipe.appendChild( ingredients);
      
      /* Directions. */
      QDomElement directions = m_doc->createElement( "directions");
      QDomElement step = createTextElement( "step", current.directions());
      directions.appendChild( step);
      recipe.appendChild( directions);
      
      recipeml.appendChild( recipe);
   }

   // save the root (recipeml) element for later use.
   m_rootElement = new QDomElement( recipeml);
} // cookbook2XML()

/*! Convert the recipe into an XML tree.

  The result is stored in the m_doc member variable.
*/
void
RecipeMLExporter::recipe2XML()  // private
{
   /* 
      All we do for now is simply creating a temporary cookbook and
      converting that.
   */
   RecipeModel* rm = new RecipeModel( m_recipe.title(), true);
   *rm += m_recipe;
   setCookbook( rm);
   cookbook2XML();
}

/*!
  This is just a helper function for creating a DomElement node that contains
  some text.

  \param nodeName - name of the node containing the text
  \param text - Text to be put into the element
  \returns the QDomElement containing the text.
*/
QDomElement
RecipeMLExporter::createTextElement( const QString& nodeName, const QString& text)
{
   QDomElement elem = m_doc->createElement( nodeName);
   QDomText textN = m_doc->createTextNode( text);
   elem.appendChild( textN);
   return elem;
}
