/* AbiSource
 * 
 * Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
 * Copyright (C) 2004 Robert Staudinger <robsta@stereolyzer.net>
 * Copyright (C) 2005 Daniel d'Andrada T. de Carvalho
 * <daniel.carvalho@indt.org.br>
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */



// Class definition include
#include "ie_imp_OpenDocument.h"

// Internal includes
#include "OpenDocument_MetaStream_Listener.h"
#include "OpenDocument_SettingsStream_Listener.h"
#include "OpenDocument_StylesStream_Listener.h"
#include "OpenDocument_ContentStream_Listener.h"

// External includes
#include <glib-object.h>
#include <gsf/gsf-input-stdio.h>
#include <gsf/gsf-infile.h>
#include <gsf/gsf-infile-zip.h>





/**
 * Constructor
 */
IE_Imp_OpenDocument::IE_Imp_OpenDocument (PD_Document * pDocument)
  : IE_Imp (pDocument), m_pGsfInfile (0), m_pSSListener(0)
{
}





/*
 * Destructor
 */
IE_Imp_OpenDocument::~IE_Imp_OpenDocument ()
{
    if (m_pGsfInfile) {
        g_object_unref (G_OBJECT(m_pGsfInfile));
    }
    
    m_styleBucket.purgeData();
} 
 
 




/**
 * 
 */
void IE_Imp_OpenDocument::defineSimpleStyle (const UT_UTF8String & name,
                            const XML_Char **props)
{
    if (!name.size() || !props) {
        return;
    }

    OO_Style* pStyle = new OO_Style (props, NULL);
    
    m_styleBucket.insert (name.utf8_str(), pStyle);
}






/**
 * 
 */
const XML_Char* IE_Imp_OpenDocument::mapStyle (const XML_Char* pName) const
{
    OO_Style* pStyle = m_styleBucket.pick((const char *)pName);
    
    if (NULL == pStyle) {
        return "";
    }
    
    return pStyle->getAbiStyle (); 
}





/**
 * 
 */
const OO_Style* IE_Imp_OpenDocument::mapStyleObj (const XML_Char* pName) const
{
    if (!pName)
        return NULL;
        
    return m_styleBucket.pick((const char *)pName);
}







/**
 * Import the given file
 */
UT_Error IE_Imp_OpenDocument::importFile(const char * szFilename)
{
    GsfInput* oo_src = GSF_INPUT (gsf_input_stdio_new (szFilename, NULL));
      
    if (oo_src == NULL) {
        return UT_ERROR;
    }

    m_pGsfInfile = GSF_INFILE (gsf_infile_zip_new (oo_src, NULL));
    g_object_unref (G_OBJECT (oo_src));
    
    if (m_pGsfInfile == NULL) {
        return UT_ERROR;
    }
      
    UT_Error err = UT_OK;
    
    if ( UT_OK != (err = _handleMimetype ())) {
        return err;
    }
    
    if ( UT_OK != (err = _handleMetaStream ())) {
        return err;
    }
    
    if ( UT_OK != (err = _handleStylesStream ())) {
        return err;
    }
    
    if ( UT_OK != (err = _handleContentStream ())) {
        return err;
    }
        
    return UT_OK;
}






 
/**
 * 
 */
PD_Document* IE_Imp_OpenDocument::getDocument () const
{
    return getDoc ();
}












/**
 * Handle the mimetype file
 */
UT_Error IE_Imp_OpenDocument::_handleMimetype ()
{
    guint8 const *data = NULL;
    size_t len = 0;

    GsfInput* pInput = gsf_infile_child_by_name(m_pGsfInfile, "mimetype");

    if (!pInput) {
        UT_DEBUGMSG(("Error: didn't get a mimetype. Assuming that it's a"
        " application/vnd.oasis.opendocument.text document\n"));
        UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
        return UT_OK;
    }

    UT_UTF8String mimetype;
    
    if (gsf_input_size (pInput) > 0) {
        mimetype.append(
            (const char *)gsf_input_read(pInput, gsf_input_size (pInput), NULL),
            gsf_input_size (pInput));
    }

    if (UT_strcmp("application/vnd.oasis.opendocument.text", mimetype.utf8_str()) != 0) {
        UT_DEBUGMSG(("*** Unknown mimetype '%s'\n", mimetype.utf8_str()));
        return UT_IE_BOGUSDOCUMENT;
    }

    g_object_unref (G_OBJECT (pInput));
    return UT_OK;
}






/**
 * Handle the meta-stream
 */
UT_Error IE_Imp_OpenDocument::_handleMetaStream ()
{
    OpenDocument_MetaStream_Listener listener(this);
    return _handleStream (m_pGsfInfile, "meta.xml", listener);
}








/**
 * Handle the setting-stream
 */
UT_Error IE_Imp_OpenDocument::_handleSettingsStream ()
{
    OpenDocument_SettingsStream_Listener listener(this);
    return _handleStream (m_pGsfInfile, "settings.xml", listener);
}






/**
 * Handle the styles-stream
 */
UT_Error IE_Imp_OpenDocument::_handleStylesStream ()
{
    m_pSSListener = new OpenDocument_StylesStream_Listener(this);
    return _handleStream (m_pGsfInfile, "styles.xml", *m_pSSListener);
}







/**
 * Handle the content-stream
 */
UT_Error IE_Imp_OpenDocument::_handleContentStream ()
{
    OpenDocument_ContentStream_Listener listener (this, m_pSSListener);
    return _handleStream (m_pGsfInfile, "content.xml", listener);
}









/**
 * Handle the stream @stream using the listener @listener.
 * Tries to abstract away how we're actually going to handle
 * how we read the stream, so that the underlying implementation
 * can easily adapt or change
 */
UT_Error IE_Imp_OpenDocument::_handleStream ( GsfInfile* pGsfInfile,
                   const char * pStream,
                   OpenDocument_Stream_Listener & listener )
{
  UT_XML reader;
  reader.setListener ( &listener );
  return _parseStream (pGsfInfile, pStream, reader);
}












/**
 * Static utility method to read a file/stream embedded inside of the
 * zipfile into an xml parser
 */
UT_Error IE_Imp_OpenDocument::_parseStream ( GsfInfile* pGsfInfile, 
                  const char* pStream,
                  UT_XML & parser )
{
    guint8 const *data = NULL;
    size_t len = 0;

    GsfInput* pInput = gsf_infile_child_by_name(pGsfInfile, pStream);

    if (!pInput) {
        return UT_ERROR;
    }

    if (gsf_input_size (pInput) > 0) {
        while ((len = gsf_input_remaining (pInput)) > 0) {
            // FIXME: we want to pass the stream in chunks, but libXML2 finds this disagreeable.
            // we probably need to pass some magic to our XML parser? 
            // len = UT_MIN (len, BUF_SZ);
            if (NULL == (data = gsf_input_read (pInput, len, NULL))) {
                g_object_unref (G_OBJECT (pInput));
                return UT_ERROR;
            }
            parser.parse ((const char *)data, len);
        }
    }
  
    g_object_unref (G_OBJECT (pInput));
    return UT_OK;
}