/* -*- 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: CMLExporter.cpp,v 1.2 2003/06/19 17:54:55 rjesse Exp $
 */

#include "CMLExporter.h"
#include "RecipeModel.h"
#include "resources.h"
#include <qfile.h>
#include <qstringlist.h>

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

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

CMLExporter::~CMLExporter()
{
   if (m_doc)
      delete m_doc;
}

QString
CMLExporter::getExtensions()
{
   return QString( "CookML recipes (*.cml)");
}

//! Fill up internal m_doc.
void
CMLExporter::buildXMLTree()
   throw (Error)
{
   if (m_type == RECIPE)
      recipe2Xml();
   else if (m_type == COOKBOOK)
      cookbook2Xml();
   else
      throw Error( tr( "CookML exporter: unknown type. I only know about recipes and cookbooks."));
}

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

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

   QTextStream ts( &file);
   ts.setEncoding( QTextStream::UnicodeUTF8);
   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 very generic and only includes the header information.
  Please complete the document before actually using it anywhere.
*/
void
CMLExporter::createCookbookHeader()
{
   QDomImplementation impl;
   QDomDocumentType doctype = impl.createDocumentType( "cookml", "SYSTEM", "cookml.dtd");

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

/*!
  Does nothing.
  \sa cookbook2Xml(), recipe2Xml()
*/
void
CMLExporter::createRecipes()
{
}

/*!
  Does nothing.
  \sa cookbook2Xml(), recipe2Xml()
*/
void
CMLExporter::createRecipeHeader()
{
}

/*!
  Does nothing.
  \sa cookbook2Xml(), recipe2Xml()
*/
void
CMLExporter::createRecipeBody()
{
}

/*!
  Convert the complete cookbook into an XML tree. The result is stored in the
  m_doc member variable.
*/
void
CMLExporter::cookbook2Xml()
{
   if (m_cookbook->isEmpty())
      return;

   RecipeModel::RecipeList recipeList = m_cookbook->getRecipeList();
   createCookbookHeader();
   QDomElement cookml = m_doc->createElement( "cookml");
   cookml.setAttribute( "version", "1.0.11");
   cookml.setAttribute( "name", m_cookbook->getName());
   cookml.setAttribute( "prog", "Mango");
   cookml.setAttribute( "progver", MANGO_VERSION_STRING);
   m_doc->appendChild( cookml);

   RecipeModel::RecipeList::const_iterator it;
   for (it = recipeList.begin(); it != recipeList.end(); it++) {
      Recipe current( *it);
      
      QDomElement recipe = m_doc->createElement( "recipe");
      recipe.setAttribute( "lang", current.lang());

      // recipe : head
      QDomElement head = m_doc->createElement( "head");
      head.setAttribute( "title", current.title());
      head.setAttribute( "rid", current.fingerprint());
      head.setAttribute( "servingqty", current.yieldQty());
      head.setAttribute( "servingtype", current.yieldUnit());
      // recipe : head : cat
      Recipe::Categories curCategories( current.categories());
      Recipe::Categories::const_iterator catIt;
      for (catIt  = curCategories.begin(); catIt != curCategories.end(); catIt++) {
         QDomElement cat = createTextElement( "cat", *catIt);
         head.appendChild( cat);
      }
      // recipe : head : hint
      Recipe::Keywords keywords( current.keywords());
      Recipe::Keywords::const_iterator kwIt;
      for (kwIt = keywords.begin(); kwIt != keywords.end(); kwIt++) {
         QDomElement hint = createTextElement( "hint", *kwIt);
         head.appendChild( hint);
      }      
      recipe.appendChild( head);

      // recipe : part
      Recipe::Ingredients ingredients( current.ingredients());
      Recipe::Ingredients::const_iterator ingIt;
      for (ingIt = ingredients.begin(); ingIt != ingredients.end(); ingIt++) {
         QDomElement part = m_doc->createElement( "part");
         QString partName( ingIt.key());
         if (! partName.isEmpty())
            part.setAttribute( "title", partName);
         // recipe : part : ingredient
         Recipe::IngredientList inglist( ingIt.data());
         Recipe::IngredientList::const_iterator ingListIt;
         for (ingListIt = inglist.begin(); ingListIt != inglist.end(); ingListIt++) {
            QDomElement ing = m_doc->createElement( "ingredient");
            ing.setAttribute( "qty", (*ingListIt).amountQty());
            ing.setAttribute( "unit", (*ingListIt).measurement().description());
            ing.setAttribute( "item", (*ingListIt).name());
            QString s( (*ingListIt).bls());
            if (! s.isEmpty())
               ing.setAttribute( "bls", s);
            s = (*ingListIt).annotation();
            if (! s.isEmpty()) {
               QDomElement inote = createTextElement( "inote", s);
               ing.appendChild( inote);
            }
            part.appendChild( ing);
         }
         recipe.appendChild( part);
      }

      // recipe : picbin
      QValueList< RecipeImageData > pictures = current.pictures();
      QValueList< RecipeImageData >::const_iterator picIt;
      for (picIt = pictures.begin(); picIt != pictures.end(); picIt++) {
         QDomElement picbin = m_doc->createElement( "picbin");
         QString s( (*picIt).label());
         QDomElement desc = createTextElement( "desc", s);
         picbin.appendChild( desc);
         QString b64( (*picIt).pictureAsBase64());
      }

      // recipe : preparation
      QString directionsStr( current.directions());
      if (! directionsStr.isEmpty()) {
         QDomElement preparation = m_doc->createElement( "preparation");
         // We don't export "preparation : step" but always "preparation : text".
         QDomElement text = createTextElement( "text", directionsStr);
         preparation.appendChild( text);
         recipe.appendChild( preparation);
      }
   }
}

/*!
  Convert the recipe into an XML tree representing a CookML document.
  The result is stored in the m_doc member variable.
*/
void
CMLExporter::recipe2Xml()
{
   RecipeModel* rm = new RecipeModel( m_recipe.title(), true);
   *rm += m_recipe;
   setCookbook( rm);
   cookbook2Xml();
}   

/*!
  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
CMLExporter::createTextElement( const QString& nodeName, const QString& text)
{
   QDomElement elem = m_doc->createElement( nodeName);
   QDomText textnode = m_doc->createTextNode( text);
   elem.appendChild( textnode);
   return elem;
}
