//-*-C++-*-
/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2002 by Luis Carvalho
    email                : lpassos@mail.telepac.pt
**************************************************************************

**************************************************************************
*                                                                        *
*  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.                                   *
*                                                                        *
**************************************************************************/

#include "pmlibraryobject.h"

#include <qstring.h>
#include <qvaluelist.h>
#include <qdom.h>
#include <qbuffer.h>
#include <qimage.h>

#include <karchive.h>
#include <kurl.h>
#include <ktar.h>
#include <kfilterdev.h>

#include <stdlib.h>

#include "pmdebug.h"

PMLibraryObject::PMLibraryObject( )
{
   m_previewLoaded = false;
   m_objectsLoaded = false;
   m_preview = NULL;
   m_objects = NULL;
   m_data = NULL;
   m_name = QString::null;
   m_description = QString::null;
   m_keywords = QString::null;
}

PMLibraryObject::PMLibraryObject( KURL u )
{
   m_previewLoaded = false;
   m_objectsLoaded = false;
   m_preview = NULL;
   m_objects = NULL;
   m_data = new KTar( u.path( ), "application/x-gzip" );
   loadLibraryInfo( );
}

PMLibraryObject::~PMLibraryObject( )
{
   delete m_data;
   if( m_previewLoaded )
      delete m_preview;
   if( m_objectsLoaded )
      delete m_objects;
}

void PMLibraryObject::loadLibraryInfo( )
{
   if( m_data )
   {
      m_data->open( IO_ReadOnly );
      // Position in the root of the file
      const KArchiveDirectory* root = m_data->directory( );
      if( !root )
         return;

      // Find the object info
      const KArchiveEntry* entry = root->entry( "objectinfo.xml" );
      if( entry && entry->isFile( ) )
      {
         QBuffer buffer( ( ( KArchiveFile* )entry )->data( ) );
         buffer.open( IO_ReadOnly );

         QDomDocument doc( "OBJECTINFO" );
         doc.setContent( &buffer );

         QDomElement e = doc.documentElement( );
         m_name = e.attribute( "name", "empty" );

         QDomNode c = e.firstChild( );
         while( !c.isNull( ) )
         {
            if( c.isElement( ) )
            {
               QDomElement ce = c.toElement( );
               // Description information
               if( ce.tagName( ) == "description" )
               {
                  QDomText te = ce.firstChild( ).toText( );
                  m_description = te.nodeValue( );
               }
               // Keywords information
               else if( ce.tagName( ) == "keywords" )
               {
                  QDomText te = ce.firstChild( ).toText( );
                  m_keywords = te.nodeValue( );
               }
               // Extra files list
               else if( ce.tagName( ) == "file_entries" )
               {
                  QDomNode entry = ce.firstChild( );
                  while( !entry.isNull( ) )
                  {
                     QDomElement entryElement = entry.toElement( );
                     if( entryElement.tagName( ) == "file" )
                        m_extraFiles.append( entryElement.attribute( "name", "" ) );
                     entry = entry.nextSibling( );
                  }
               }
            }
            c = c.nextSibling( );
         }
      }
      m_data->close( );
   }
}

QImage* PMLibraryObject::preview( )
{
   if( !m_previewLoaded )
   {
      if( m_data )
      {
         m_data->open( IO_ReadOnly );
         // Position in the root of the file
         const KArchiveDirectory* root = m_data->directory( );
         if( !root )
            return NULL;

         // Find the object preview
         const KArchiveEntry* entry = root->entry( "preview.png" );
         if( entry && entry->isFile( ) )
         {
            QBuffer buffer( ( ( KArchiveFile* )entry )->data( ) );
            buffer.open( IO_ReadOnly );
            m_preview = new QImage( buffer.readAll( ) );
            m_previewLoaded = true;
         }

         m_data->close( );
      }
   }
   return m_preview;
}

QByteArray* PMLibraryObject::objects( )
{
   if( !m_objectsLoaded )
   {
      if( m_data )
      {
         m_data->open( IO_ReadOnly );
         // Position in the root of the file
         const KArchiveDirectory* root = m_data->directory( );
         if( !root )
            return NULL;

         // Find the object info
         const KArchiveEntry* entry = root->entry( "objectdata.kpm" );
         if( entry && entry->isFile( ) )
         {
            // Transfer the file contents to a QByteArray.
            // TODO Maybe there is a smarter way of doing this.
            char buf[256];
            int nbytes;
            QIODevice* aux_in = ( ( KArchiveFile* )entry )->device( );

            m_objects = new QByteArray( );
            QBuffer aux_out( *m_objects );

            aux_in->open( IO_ReadOnly );
            aux_out.open( IO_WriteOnly );
            while( !aux_in->atEnd( ) )
            {
               nbytes = aux_in->readBlock( buf, 256 );
               aux_out.writeBlock( buf, nbytes );
            }
            delete aux_in;
            if( m_objects->size( ) != 0 )
               m_objectsLoaded = true;
            else
            {
               delete m_objects;
               m_objects = NULL;
            }
         }
      }
   }
   return m_objects;
}

void PMLibraryObject::setName( const QString& str )
{
   m_name = str;
}

void PMLibraryObject::setDescription( const QString& str )
{
   m_description = str;
}

void PMLibraryObject::setKeywords( const QString& str )
{
   m_keywords = str;
}

void PMLibraryObject::setPreview( const QImage& img )
{
   if( m_previewLoaded )
      delete m_preview;

   m_preview = new QImage( img );
   m_previewLoaded = true;
}

void PMLibraryObject::setObjects( const QByteArray& obj )
{
   if( m_objectsLoaded )
      delete m_objects;

   m_objects = new QByteArray( obj );
   m_objects->detach( );
   m_objectsLoaded = true;
}

void PMLibraryObject::save( const QString& fileName )
{
   // create the library file
   m_data = new KTar( fileName, "application/x-gzip" );
   m_data->open( IO_WriteOnly );
   // save object info
   saveLibraryInfo( );
   // save preview
   savePreview( );
   // save library objects
   saveObjects( );
   m_data->close( );
}

void PMLibraryObject::saveLibraryInfo( )
{
   // Pre-condition for the execution of this method.
   if( !m_data || !m_data->isOpened( ) )
   {
      kdError( PMArea ) << "Trying to save to an unopened data file." << endl;
      exit( 1 );
   }

   QBuffer buf;

   // Document type
   QDomDocument doc( "OBJECTINFO" );
   
   // The root element for the document has one attribute: name 
   QDomElement root = doc.createElement( "object" );
   doc.appendChild( root );
   root.setAttribute( "name", m_name );

   // Inside the root element we add the description node ...
   QDomElement description = doc.createElement( "description" );
   root.appendChild( description );
   description.appendChild( doc.createTextNode( m_description ) );

   // ... and the keywords node ...
   QDomElement keywords = doc.createElement( "keywords" );
   root.appendChild( keywords );
   keywords.appendChild( doc.createTextNode( m_keywords ) );

   // ... and finally the extra files information
   QDomElement file_list = doc.createElement( "file_list" );
   root.appendChild( file_list );
   QStringList::Iterator it = m_extraFiles.begin( );
   for( ; it != m_extraFiles.end( ); ++it )
   {
      QDomElement entry = doc.createElement( "file" );
      entry.setAttribute( "name", *it );
      file_list.appendChild( entry );
   }

   // Write the document to the buffer
   QByteArray array;
   QTextStream str( array, IO_WriteOnly );
   str.setEncoding( QTextStream::UnicodeUTF8 );
   str << doc.toString( );
   m_data->writeFile( "objectinfo.xml", "user", "group", array.size( ), array.data( ) );
}

void PMLibraryObject::savePreview( )
{
   // Pre-condition for the execution of this method.
   if( !m_data || !m_data->isOpened( ) )
   {
      kdError( PMArea ) << "Trying to save to an unopened data file." << endl;
      exit( 1 );
   }

   QByteArray array;
   QBuffer iods( array );
   QImageIO img_io( &iods, "PNG" );

   if( m_previewLoaded )
   {
      img_io.setImage( *m_preview );
      iods.open( IO_WriteOnly );
      img_io.write( );
      m_data->writeFile( "preview.png", "user", "group", array.size( ), array.data( ) );
   }
}

void PMLibraryObject::saveObjects( )
{
   // Pre-condition for the execution of this method.
   if( !m_data || !m_data->isOpened( ) )
   {
      kdError( PMArea ) << "Trying to save to an unopened data file." << endl;
      exit( 1 );
   }

   if( m_objectsLoaded )
      m_data->writeFile( "objectdata.kpm", "user", "group", m_objects->size( ), m_objects->data( ) );
}
