/* This file is part of the KDE project
   Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
   Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <tqpainter.h>
#include <tqfile.h>

#include <kdebug.h>
#include <kurl.h>
#include <kfilterdev.h>
#include <tdeio/netaccess.h>

#include "KoPictureKey.h"
#include "KoPictureBase.h"
#include "KoPictureImage.h"
#include "KoPictureEps.h"
#include "KoPictureClipart.h"
#include "KoPictureWmf.h"
#include "KoPictureShared.h"
#include <kmdcodec.h>


KoPictureShared::KoPictureShared(void) : m_base(NULL)
{
}

void KoPictureShared::assignPictureId( uint _id)
{
    m_pictureId = _id;
}

TQString KoPictureShared::uniquePictureId() const
{
    return "Pictures"+ TQString::number(m_pictureId);
}

KoPictureShared::~KoPictureShared(void)
{
    delete m_base;
}

KoPictureShared::KoPictureShared(const KoPictureShared &other)
    : TQShared() // Some compilers want it explicitly!
{
    // We need to use newCopy, because we want a real copy, not just a copy of the part of KoPictureBase
    if (other.m_base)
        m_base=other.m_base->newCopy();
    else
        m_base=NULL;
}

KoPictureShared& KoPictureShared::operator=( const KoPictureShared &other )
{
    clear();
    kdDebug(30003) << "KoPictureShared::= before" << endl;
    if (other.m_base)
        m_base=other.m_base->newCopy();
    kdDebug(30003) << "KoPictureShared::= after" << endl;
    return *this;
}

KoPictureType::Type KoPictureShared::getType(void) const
{
    if (m_base)
        return m_base->getType();
    return KoPictureType::TypeUnknown;
}

bool KoPictureShared::isNull(void) const
{
    if (m_base)
        return m_base->isNull();
    return true;
}

void KoPictureShared::draw(TQPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode)
{
    if (m_base)
        m_base->draw(painter, x, y, width, height, sx, sy, sw, sh, fastMode);
    else
    {
        // Draw a red box (easier DEBUG)
        kdWarning(30003) << "Drawing red rectangle! (KoPictureShared::draw)" << endl;
        painter.save();
        painter.setBrush(TQColor(255,0,0));
        painter.drawRect(x,y,width,height);
        painter.restore();
    }
}

bool KoPictureShared::loadWmf(TQIODevice* io)
{
    kdDebug(30003) << "KoPictureShared::loadWmf" << endl;
    if (!io)
    {
        kdError(30003) << "No TQIODevice!" << endl;
        return false;
    }

    clear();

    // The extension .wmf was used (KOffice 1.1.x) for TQPicture files
    // For an extern file or in the storage, .wmf can mean a real Windows Meta File.

    TQByteArray array ( io->readAll() );

    if ((array[0]=='Q') && (array[1]=='P') &&(array[2]=='I') && (array[3]=='C'))
    {
        m_base=new KoPictureClipart();
        setExtension("qpic");
    }
    else
    {
        m_base=new KoPictureWmf();
        setExtension("wmf");
    }
    return m_base->loadData(array, m_extension);
}

bool KoPictureShared::loadTmp(TQIODevice* io)
// We have a temp file, probably from a downloaded file
//   We must check the file type
{
    kdDebug(30003) << "KoPictureShared::loadTmp" << endl;
    if (!io)
    {
        kdError(30003) << "No TQIODevice!" << endl;
        return false;
    }

    // The extension .wmf was used (KOffice 1.1.x) for TQPicture files
    // For an extern file or in the storage, .wmf can mean a real Windows Meta File.

    TQByteArray array ( io->readAll() );
    return identifyAndLoad( array );
}

bool KoPictureShared::identifyAndLoad( TQByteArray array )
{
    if ( array.size() < 5 )
    {
        kdError(30003) << "Picture is less than 5 bytes long!" << endl;
        return false;
    }

    TQString strExtension;
    bool flag=false;

    // Try to find the file type by comparing magic on the first few bytes!
    // ### TODO: could not TQImageIO::imageFormat do it too? (At least most of them?)
    if ((array[0]==char(0x89)) && (array[1]=='P') &&(array[2]=='N') && (array[3]=='G'))
    {
        strExtension="png";
    }
    else if ((array[0]==char(0xff)) && (array[1]==char(0xd8)) &&(array[2]==char(0xff)) && (array[3]==char(0xe0)))
    {
        strExtension="jpeg";
    }
    else if ((array[0]=='B') && (array[1]=='M'))
    {
        strExtension="bmp";
    }
    else if ((array[0]==char(0xd7)) && (array[1]==char(0xcd)) &&(array[2]==char(0xc6)) && (array[3]==char(0x9a)))
    {
        strExtension="wmf";
    }
    else if ((array[0]=='<') && (array[1]=='?') && ( array[2]=='x' ) && (array[3]=='m') && ( array[4]=='l' ) )
    {
        strExtension="svg";
    }
    else if ((array[0]=='Q') && (array[1]=='P') &&(array[2]=='I') && (array[3]=='C'))
    {
        strExtension="qpic";
    }
    else if ((array[0]=='%') && (array[1]=='!') &&(array[2]=='P') && (array[3]=='S'))
    {
        strExtension="eps";
    }
    else if ((array[0]==char(0xc5)) && (array[1]==char(0xd0)) && (array[2]==char(0xd3)) && (array[3]==char(0xc6)))
    {
        // So called "MS-DOS EPS file"
        strExtension="eps";
    }
    else if ((array[0]=='G') && (array[1]=='I') && (array[2]=='F') && (array[3]=='8'))
    {
        // GIF (87a or 89a)
        strExtension="gif";
    }
    else if ( ( array[0] == char( 0037 ) ) && ( array[1] == char( 0213 ) ) )
    {
        // Gzip
        TQBuffer buffer(array);
        buffer.open(IO_ReadOnly);

        const bool flag = loadCompressed( &buffer, "application/x-gzip", "tmp" );
        buffer.close();
        return flag;
    }
    else if ( ( array[0] == 'B' ) && ( array[1] == 'Z' ) && ( array[2] == 'h') )
    {
        // BZip2
        TQBuffer buffer(array);
        buffer.open(IO_ReadOnly);
        const bool flag = loadCompressed( &buffer, "application/x-bzip2", "tmp" );
        buffer.close();
        return flag;
    }
    else
    {
        kdDebug(30003) << "Cannot identify the type of temp file!"
            << " Trying to convert to PNG! (in KoPictureShared::loadTmp" << endl;

        // Do not trust TQBuffer and do not work directly on the TQByteArray array
        // DF: It would be faster to work on array here, and to create a completely
        // different TQBuffer for the writing code!
        TQBuffer buf( array.copy() );
        if (!buf.open(IO_ReadOnly))
        {
            kdError(30003) << "Could not open read buffer!" << endl;
            return false;
        }

        TQImageIO imageIO(&buf,NULL);

        if (!imageIO.read())
        {
            kdError(30003) << "Could not read image!" << endl;
            return false;
        }

        buf.close();

        if ( !buf.open( IO_WriteOnly | IO_Truncate ) )
        {
            kdError(30003) << "Could not open write buffer!" << endl;
            return false;
        }

        imageIO.setIODevice(&buf);
        imageIO.setFormat("PNG");

        if (!imageIO.write())
        {
            kdError(30003) << "Could not write converted image!" << endl;
            return false;
        }
        buf.close();

        array = buf.buffer();

        strExtension="png";
    }

    kdDebug(30003) << "Temp file considered to be " << strExtension << endl;

    clearAndSetMode(strExtension);
    if (m_base)
        flag = m_base->loadData(array,strExtension);
    setExtension(strExtension);

    return flag;
}



bool KoPictureShared::loadXpm(TQIODevice* io)
{
    kdDebug(30003) << "KoPictureShared::loadXpm" << endl;
    if (!io)
    {
        kdError(30003) << "No TQIODevice!" << endl;
        return false;
    }

    clear();

    // Old KPresenter XPM files have char(1) instead of some "
    // Therefore we need to treat XPM separately

    TQByteArray array=io->readAll();

    // As XPM files are normally only ASCII files, we can replace it without problems

    int pos=0;

    while ((pos=array.find(char(1),pos))!=-1)
    {
        array[pos]='"';
    }

    // Now that the XPM file is corrected, we need to load it.

    m_base=new KoPictureImage();

    TQBuffer buffer(array);
    bool check = m_base->load(&buffer,"xpm");
    setExtension("xpm");
    return check;
}

bool KoPictureShared::save(TQIODevice* io) const
{
    if (!io)
        return false;
    if (m_base)
        return m_base->save(io);
    return false;
}

bool KoPictureShared::saveAsBase64( KoXmlWriter& writer ) const
{
    if ( m_base )
        m_base->saveAsBase64( writer );
    return false;
}

void KoPictureShared::clear(void)
{
    // Clear does not reset the key m_key!
    delete m_base;
    m_base=NULL;
}

void KoPictureShared::clearAndSetMode(const TQString& newMode)
{
    delete m_base;
    m_base=NULL;

    const TQString mode=newMode.lower();

    if ((mode=="svg") || (mode=="qpic"))
    {
        m_base=new KoPictureClipart();
    }
    else if (mode=="wmf")
    {
        m_base=new KoPictureWmf();
    }
    else if ( (mode=="eps") || (mode=="epsi") || (mode=="epsf") )
    {
        m_base=new KoPictureEps();
    }
    else
    {   // TODO: test if TQImageIO really knows the file format
        m_base=new KoPictureImage();
    }
}

TQString KoPictureShared::getExtension(void) const
{
    return m_extension;
}

void KoPictureShared::setExtension(const TQString& extension)
{
    m_extension = extension;
}

TQString KoPictureShared::getMimeType(void) const
{
   if (m_base)
        return m_base->getMimeType(m_extension);
    return TQString(NULL_MIME_TYPE);
}


bool KoPictureShared::loadFromBase64( const TQCString& str )
{
    clear();
    TQByteArray data;
    KCodecs::base64Decode( str, data );
    return identifyAndLoad( data );
}

bool KoPictureShared::load(TQIODevice* io, const TQString& extension)
{
    kdDebug(30003) << "KoPictureShared::load(TQIODevice*, const TQString&) " << extension << endl;
    bool flag=false;
    TQString ext(extension.lower());
    if (ext=="wmf")
        flag=loadWmf(io);
    else if (ext=="tmp") // ### TODO: also remote scripts need this, don't they?
        flag=loadTmp(io);
    else if ( ext == "bz2" )
    {
        flag = loadCompressed( io, "application/x-bzip2", "tmp" );
    }
    else if ( ext == "gz" )
    {
        flag = loadCompressed( io, "application/x-gzip", "tmp" );
    }
    else if ( ext == "svgz" )
    {
        flag = loadCompressed( io, "application/x-gzip", "svg" );
    }
    else
    {
        clearAndSetMode(ext);
        if (m_base)
            flag = m_base->load(io, ext);
        setExtension(ext);
    }
    if (!flag)
    {
        kdError(30003) << "File was not loaded! (KoPictureShared::load)" << endl;
    }
    return flag;
}

bool KoPictureShared::loadFromFile(const TQString& fileName)
{
    kdDebug(30003) << "KoPictureShared::loadFromFile " << fileName << endl;
    if ( fileName.isEmpty() )
    {
        kdError(30003) << "Cannot load file with empty name!" << endl;
        return false;
    }
    TQFile file(fileName);
    if (!file.open(IO_ReadOnly))
        return false;

    bool flag = false;
    const int pos=fileName.findRev('.');
    if (pos==-1)
    {
        kdDebug(30003) << "File with no extension!" << endl;
        // As we have no extension, consider it like a temporary file
        flag = loadTmp( &file );
    }
    else
    {
        const TQString extension( fileName.mid( pos+1 ) );
        // ### TODO: check if the extension if gz or bz2 and find the previous extension
        flag = load( &file, extension );
    }
    file.close();
    return flag;
}

TQSize KoPictureShared::getOriginalSize(void) const
{
    if (m_base)
        return m_base->getOriginalSize();
    return TQSize(0,0);
}

TQPixmap KoPictureShared::generatePixmap(const TQSize& size, bool smoothScale)
{
    if (m_base)
        return m_base->generatePixmap(size, smoothScale);
    return TQPixmap();
}

TQDragObject* KoPictureShared::dragObject( TQWidget *dragSource, const char *name )
{
    if (m_base)
        return m_base->dragObject( dragSource, name );
    return 0L;
}

TQImage KoPictureShared::generateImage(const TQSize& size)
{
    if (m_base)
        return m_base->generateImage( size );
    return TQImage();
}

bool KoPictureShared::hasAlphaBuffer() const
{
   if (m_base)
       return m_base->hasAlphaBuffer();
   return false;
}

void KoPictureShared::setAlphaBuffer(bool enable)
{
    if (m_base)
        m_base->setAlphaBuffer(enable);
}

TQImage KoPictureShared::createAlphaMask(int conversion_flags) const
{
    if (m_base)
        return m_base->createAlphaMask(conversion_flags);
    return TQImage();
}

void KoPictureShared::clearCache(void)
{
    if (m_base)
        m_base->clearCache();
}

bool KoPictureShared::loadCompressed( TQIODevice* io, const TQString& mimeType, const TQString& extension )
{
    // ### TODO: check that we do not have an endless recursion
    TQIODevice* in = KFilterDev::device( io, mimeType, false);

    if ( !in )
    {
        kdError(30003) << "Cannot create device for uncompressing! Aborting!" << endl;
        return false;
    }


    if ( !in->open( IO_ReadOnly ) )
    {
        kdError(30003) << "Cannot open file for uncompressing! Aborting!" << endl;
        delete in;
        return false;
    }

    const bool flag = load( in, extension );

    in->close();
    delete in;

    return flag;
}
