/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

#include <vector>
#include <map>
#include <algorithm>
#include <boost/shared_ptr.hpp>
#include <sot/storage.hxx>
#ifndef _SVTOOLS_HRC
#include <svtools/svtools.hrc>
#endif

#include <sal/main.h>
#include <vcl/event.hxx>
#include <vcl/svapp.hxx>
#include <vcl/wrkwin.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/fixed.hxx>
#include <vcl/edit.hxx>
#include <vcl/button.hxx>
#include <vcl/lstbox.hxx>
#include <svtools/filectrl.hxx>
#include <tools/urlobj.hxx>
#include <osl/file.hxx>
#include <vcl/unohelp2.hxx>
#include <svtools/svtreebx.hxx>
#include <svtools/svmedit.hxx>
#include <sfx2/filedlghelper.hxx>

#include <toolkit/unohlp.hxx>

#include <tools/stream.hxx>
#include <tools/resmgr.hxx>

#include <comphelper/processfactory.hxx>
#include <cppuhelper/servicefactory.hxx>
#include <cppuhelper/bootstrap.hxx>

#include <ucbhelper/contentbroker.hxx>
#include <ucbhelper/configurationkeys.hxx>

#include <com/sun/star/lang/XMultiServiceFactory.hpp>

#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/awt/XToolkit.hpp>
#include <com/sun/star/awt/WindowDescriptor.hpp>
#include <com/sun/star/awt/WindowAttribute.hpp>
#include <svx/msdffdef.hxx>

#include <unotools/localfilehelper.hxx>

#include "xmlconfig.hxx"

using ::rtl::OUString;

using namespace ::com::sun::star;

///////////////////////////////////////////////////////////////////////

enum CompareStatus           { CMP_NOTYET = 0, CMP_EQUAL = 1, CMP_NOTEQUAL = 2, CMP_NOTAVAILABLE = 3 };
static ColorData gColors[] = { COL_BLACK,      COL_GREEN,     COL_RED,          COL_CYAN };

class Atom
{
public:
	~Atom();

	/** imports this atom and its child atoms */
	static Atom* import( const DffRecordHeader& rRootRecordHeader, SvStream& rStCtrl );
	static Atom* import( UINT16 nRecType, SvStream& rStCtrl );

	inline const DffRecordHeader& getHeader() const;

	/** returns true if at least one atim with the given nRecType is found */
	inline bool hasChildAtom( sal_uInt16 nRecType ) const;

	/** returns true if at least one atim with the given nRecType and nRecInstnace is found */
	inline bool hasChildAtom( sal_uInt16 nRecType, sal_uInt16 nRecInstance ) const;

	/** returns the first child atom with nRecType or NULL */
	inline const Atom* findFirstChildAtom( sal_uInt16 nRecType ) const;

	/** returns the next child atom after pLast with nRecType or NULL */
	const Atom* findNextChildAtom( sal_uInt16 nRecType, const Atom* pLast ) const;

	/** returns the first child atom with nRecType and nRecInstance or NULL */
	inline const Atom* findFirstChildAtom( sal_uInt16 nRecType, sal_uInt16 nRecInstance ) const;

	/** returns the next child atom after pLast with nRecType and nRecInstance or NULL */
	const Atom* findNextChildAtom( sal_uInt16 nRecType, sal_uInt16 nRecInstance, const Atom* pLast ) const;

	/** returns the first child atom or NULL */
	inline const Atom* findFirstChildAtom() const;

	/** returns the next child atom after pLast or NULL */
	inline const Atom* findNextChildAtom( const Atom* pLast ) const;

	/** returns true if this atom is a container */
	inline bool isContainer() const;

	/** seeks to the contents of this atom */
	inline bool seekToContent() const;

	/** returns the record type */
	inline sal_uInt16 getType() const;

	/** returns the record instance */
	inline sal_uInt16 getInstance() const;

	/** returns the record length */
	inline sal_uInt32 getLength() const;

	SvStream& getStream() const { return mrStream; }

	bool operator==( const Atom& rAtom ) const;

	CompareStatus getCompareStatus() const { return meStatus; }

	void compare( Atom* pAtom );
	bool compareContent( Atom& rAtom );

	Atom* getCompareAtom() const { return mpCompareAtom; }
	void setCompareAtom( Atom* pAtom ) { mpCompareAtom = pAtom; }

private:
	Atom( const DffRecordHeader& rRecordHeader, SvStream& rStCtrl );

	// statics for compare
	static Atom* skipAtoms( Atom* pContainer, Atom* pAtom, Atom* pSkipTo );
	static Atom* findFirstEqualAtom( Atom* pCompare, Atom* pContainer, Atom* pSearch, int& nDistance );

	SvStream& mrStream;
	DffRecordHeader maRecordHeader;
	Atom* mpFirstChild;
	Atom* mpNextAtom;

	CompareStatus meStatus;
	Atom* mpCompareAtom;
};

bool Atom::operator==( const Atom& rAtom ) const
{
	return ( maRecordHeader.nRecType == rAtom.maRecordHeader.nRecType ) &&
			( maRecordHeader.nRecVer == rAtom.maRecordHeader.nRecVer ) &&	
		   ( maRecordHeader.nRecInstance == rAtom.maRecordHeader.nRecInstance );
}

bool Atom::compareContent( Atom& rAtom )
{
	if( maRecordHeader.nRecLen == rAtom.maRecordHeader.nRecLen )
	{
		seekToContent();
		rAtom.seekToContent();

		SvStream& rStream1 = getStream();
		SvStream& rStream2 = rAtom.getStream();

		const int nBufferSize = 1024;
		boost::shared_ptr< char > buffer1( new char[nBufferSize] );
		boost::shared_ptr< char > buffer2( new char[nBufferSize] );

		sal_uInt32 nLength = maRecordHeader.nRecLen;
		sal_Size nRead = 0;
		while( nLength )
		{
			sal_Size nRead = (nBufferSize < nLength) ? nBufferSize : nLength;
			nRead = rStream1.Read( (void*)buffer1.get(), nRead );
			if( nRead == 0 )
				break;
			if( rStream2.Read( (void*)buffer2.get(), nRead ) != nRead )
				break;
			if( memcmp( (void*)buffer1.get(), (void*)buffer2.get(), nRead ) != 0 )
				break;

			nLength -= nRead;
		}

		return nLength == 0;
	}

	return false;
}

inline bool Atom::hasChildAtom( sal_uInt16 nRecType ) const
{
	return findFirstChildAtom( nRecType ) != NULL;
}

inline bool Atom::hasChildAtom( sal_uInt16 nRecType, sal_uInt16 nRecInstance ) const
{
	return findFirstChildAtom( nRecType, nRecInstance ) != NULL;
}

inline const Atom* Atom::findFirstChildAtom( sal_uInt16 nRecType ) const
{
	return findNextChildAtom( nRecType, NULL );
}

inline const DffRecordHeader& Atom::getHeader() const
{
	return maRecordHeader;
}

inline const Atom* Atom::findFirstChildAtom( sal_uInt16 nRecType, sal_uInt16 nRecInstance ) const
{
	return findNextChildAtom( nRecType, nRecInstance, NULL );
}

inline const Atom* Atom::findFirstChildAtom() const
{
	return mpFirstChild;
}

inline const Atom* Atom::findNextChildAtom( const Atom* pLast ) const
{
	return pLast ? pLast->mpNextAtom : pLast;
}

inline bool Atom::isContainer() const
{
	return (bool)maRecordHeader.IsContainer();
}

inline bool Atom::seekToContent() const
{
	maRecordHeader.SeekToContent( mrStream );
	return mrStream.GetError() == 0;
}

inline sal_uInt16 Atom::getType() const
{
	return maRecordHeader.nRecType;
}

inline sal_uInt16 Atom::getInstance() const
{
	return maRecordHeader.nRecInstance;
}

inline sal_uInt32 Atom::getLength() const
{
	return maRecordHeader.nRecLen;
}

Atom::Atom( const DffRecordHeader& rRecordHeader, SvStream& rStream )
: maRecordHeader( rRecordHeader ),
  mrStream( rStream ),
  mpFirstChild( 0 ),
  mpNextAtom( 0 ),
  meStatus( CMP_NOTYET ),
  mpCompareAtom( 0 )
{
	// check if we need to force this to a container
	if( maRecordHeader.nRecVer != DFF_PSFLAG_CONTAINER )
	{
		AtomConfig* pAtomConfig = dynamic_cast< AtomConfig* >( gAtomConfigMap[ maRecordHeader.nRecType ].get() );
		if( pAtomConfig && pAtomConfig->isContainer() )
		{
			maRecordHeader.nRecVer = DFF_PSFLAG_CONTAINER;
		}
	}

	if( isContainer() )
	{
		if( seekToContent() )
		{
			DffRecordHeader aChildHeader;

			Atom* pLastAtom = NULL;

			while( (mrStream.GetError() == 0 ) && ( mrStream.Tell() < maRecordHeader.GetRecEndFilePos() ) )
			{
				mrStream >> aChildHeader;

				if( mrStream.GetError() == 0 )
				{
					Atom* pAtom = new Atom( aChildHeader, mrStream );

					if( pLastAtom )
						pLastAtom->mpNextAtom = pAtom;
					if( mpFirstChild == NULL )
						mpFirstChild = pAtom;

					pLastAtom = pAtom;
				}
			}
		}
	}

	maRecordHeader.SeekToEndOfRecord( mrStream );
}

Atom::~Atom()
{
	Atom* pChild = mpFirstChild;
	while( pChild )
	{
		Atom* pNextChild = pChild->mpNextAtom;
		delete pChild;
		pChild = pNextChild;
	}
}

/** imports this atom and its child atoms */
Atom* Atom::import( const DffRecordHeader& rRootRecordHeader, SvStream& rStCtrl )
{
	Atom* pRootAtom = new Atom( rRootRecordHeader, rStCtrl );

	if( rStCtrl.GetError() == 0 )
	{
		return pRootAtom;
	}
	else
	{
		delete pRootAtom;
		return NULL;
	}
}

/** imports this atom and its child atoms */
Atom* Atom::import( UINT16 nRecType, SvStream& rStCtrl )
{
	rStCtrl.Seek( STREAM_SEEK_TO_END );
	sal_Size nStreamLength = rStCtrl.Tell();
	rStCtrl.Seek( STREAM_SEEK_TO_BEGIN );

	DffRecordHeader aRootRecordHeader;
	aRootRecordHeader.nRecVer = DFF_PSFLAG_CONTAINER;
	aRootRecordHeader.nRecInstance = 0;
	aRootRecordHeader.nImpVerInst = 0;
	aRootRecordHeader.nRecType = nRecType;
	aRootRecordHeader.nRecLen = nStreamLength;
	aRootRecordHeader.nFilePos = 0;

	return import( aRootRecordHeader, rStCtrl );
}

/** returns the next child atom after pLast with nRecType or NULL */
const Atom* Atom::findNextChildAtom( sal_uInt16 nRecType, const Atom* pLast ) const
{
	Atom* pChild = pLast != NULL ? pLast->mpNextAtom : mpFirstChild;
	while( pChild && pChild->maRecordHeader.nRecType != nRecType )
	{
		pChild = pChild->mpNextAtom;
	}

	return pChild;
}

/** returns the next child atom after pLast with nRecType and nRecInstance or NULL */
const Atom* Atom::findNextChildAtom( sal_uInt16 nRecType, sal_uInt16 nRecInstance, const Atom* pLast ) const
{
	const Atom* pChild = pLast != NULL ? pLast->mpNextAtom : mpFirstChild;
	while( pChild && (pChild->maRecordHeader.nRecType != nRecType) && (pChild->maRecordHeader.nRecInstance != nRecInstance) )
	{
		pChild = findNextChildAtom( pChild );
	}

	return pChild;
}

Atom* Atom::findFirstEqualAtom( Atom* pCompare, Atom* pContainer, Atom* pSearch, int& nDistance )
{
	nDistance = 0;
	Atom* pRet = 0;

	while( pSearch )
	{
		if( *pSearch == *pCompare )
			return pSearch;

		pSearch = const_cast< Atom* >( pContainer->findNextChildAtom( pSearch ) );
		nDistance++;
	}

	return 0;
}

Atom* Atom::skipAtoms( Atom* pContainer, Atom* pAtom, Atom* pSkipTo )
{
	while( pAtom && (pAtom != pSkipTo) )
	{
		pAtom->meStatus = CMP_NOTAVAILABLE;
		pAtom = const_cast< Atom* >( pContainer->findNextChildAtom( pAtom ) );
	}

	return pAtom;
}

void Atom::compare( Atom* pAtom )
{
	if( pAtom )
	{
		if( meStatus == CMP_NOTYET )
		{
			mpCompareAtom = pAtom;
			pAtom->mpCompareAtom = this;

			mpCompareAtom = pAtom;
			pAtom->mpCompareAtom = this;

			meStatus = pAtom->meStatus = ( *this == *pAtom ) ? CMP_EQUAL : CMP_NOTEQUAL;
		}

		if(meStatus == CMP_EQUAL)
		{
			if( isContainer() )
			{
				/** returns the first child atom or NULL */
				Atom* pChildAtom1 = const_cast< Atom* >( findFirstChildAtom() );

				if( pChildAtom1 && (pChildAtom1->meStatus == CMP_NOTYET) )
				{
					Atom* pChildAtom2 = const_cast< Atom* >( pAtom->findFirstChildAtom() );
					while( pChildAtom1 && pChildAtom2 )
					{
						if( !(*pChildAtom1 == *pChildAtom2) )
						{
							int nDistance1;
							int nDistance2;

							Atom* pFind1 = findFirstEqualAtom( pChildAtom1, pAtom, const_cast< Atom* >( pAtom->findNextChildAtom( pChildAtom2 )), nDistance1 );
							Atom* pFind2 = findFirstEqualAtom( pChildAtom2, this, const_cast< Atom* >(findNextChildAtom( pChildAtom1 )), nDistance2 );

							if( pFind1 && (!pFind2 || (nDistance1 < nDistance2) ) )
							{
								pChildAtom2 = skipAtoms( pAtom, pChildAtom2, pFind1 );
							}
							else if( pFind2 )
							{
								pChildAtom1 = skipAtoms( this, pChildAtom1, pFind2 );
							}
							else
							{
								pChildAtom1 = skipAtoms( this, pChildAtom1, 0 );
								pChildAtom2 = skipAtoms( pAtom, pChildAtom2, 0 );
							}
						}

						if( pChildAtom1 && pChildAtom2 )
						{
							pChildAtom1->mpCompareAtom = pChildAtom2;
							pChildAtom2->mpCompareAtom = pChildAtom1;

							pChildAtom1->meStatus = pChildAtom2->meStatus =
								(pChildAtom1->isContainer() || pChildAtom1->compareContent( *pChildAtom2 )) ?
									CMP_EQUAL : CMP_NOTEQUAL;

							pChildAtom1 = const_cast< Atom* >( findNextChildAtom( pChildAtom1 ) );
							pChildAtom2 = const_cast< Atom* >( pAtom->findNextChildAtom( pChildAtom2 ) );
						}
					}
				}
			}
			else
			{
				if( !compareContent( *pAtom ) )
				{
					meStatus = pAtom->meStatus = CMP_NOTEQUAL;
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////

class AtomBoxString : public SvLBoxString
{
public:
	AtomBoxString( SvLBoxEntry* pEntry, const String& rStr )
		: SvLBoxString( pEntry, 0, rStr )
	{ }

	~AtomBoxString() { }

	void Paint( const Point& rPos, SvLBox& rOutDev, USHORT nViewDataEntryFlags, SvLBoxEntry* pEntry )
	{
		Color aOldTextColor = rOutDev.GetTextColor();

		if( pEntry && pEntry->GetUserData() )
		{
			Atom* pAtom = static_cast<Atom*>( pEntry->GetUserData() );
			rOutDev.SetTextColor( Color( gColors[ pAtom->getCompareStatus() ] ) );
		}

		SvLBoxString::Paint( rPos, rOutDev, nViewDataEntryFlags, pEntry );

		rOutDev.SetTextColor( aOldTextColor );

/*
		Color aOldFillColor = rOutDev.GetFillColor();

		SvTreeListBox* pTreeBox = static_cast< SvTreeListBox* >( &rOutDev );
		long nX = pTreeBox->GetSizePixel().Width();

		ScrollBar* pVScroll = pTreeBox->GetVScroll();
		if ( pVScroll->IsVisible() )
		{
			nX -= pVScroll->GetSizePixel().Width();
		}

		SvViewDataItem* pItem = rOutDev.GetViewDataItem( pEntry, this );
		nX -= pItem->aSize.Height();

		long nSize = pItem->aSize.Height() / 2;
		long nHalfSize = nSize / 2;
		long nY = rPos.Y() + nHalfSize;

		if ( aOldFillColor == COL_WHITE )
		{
			rOutDev.SetFillColor( Color( COL_BLACK ) );
		}
		else
		{
			rOutDev.SetFillColor( Color( COL_WHITE ) );
		}

		long n = 0;
		while ( n <= nHalfSize )
		{
			rOutDev.DrawRect( Rectangle( nX+n, nY+n, nX+n, nY+nSize-n ) );
			n++;
		}

		rOutDev.SetFillColor( aOldFillColor );
*/
	}

private:
	Image* mpImage;
};


//////////////////////////////////////////////////////////////////////

class AtomContainerTreeListBox : public SvTreeListBox
{
public:
	AtomContainerTreeListBox( Window* pParent );
	~AtomContainerTreeListBox();

	void SetRootAtom( const Atom* pAtom );


	void            SetCollapsingHdl(const Link& rNewHdl){maCollapsingHdl=rNewHdl;}
	const Link&     GetCollapsingHdl() const { return maCollapsingHdl; }

	void            SetExpandingHdl(const Link& rNewHdl){maExpandingHdl=rNewHdl;}
	const Link&     GetExpandingHdl() const { return maExpandingHdl; }

	virtual BOOL    Expand( SvLBoxEntry* pParent );
	virtual BOOL    Collapse( SvLBoxEntry* pParent );

	SvLBoxEntry*	findAtom( Atom* pAtom );

	virtual void InitEntry(SvLBoxEntry*,const XubString&,const Image&,const Image&);
	virtual void SetTabs();

private:
	void InsertAtom( const Atom* pAtom, SvLBoxEntry* pParent = 0 );
	const Atom* mpRootAtom;
	ResMgr*	mpResMgr;
	Image maImgFolder;
	Image maImgAtom;
	Image maImgExpanded;
	Image maImgCollapsed;
	bool mbRecursiveGuard;
	Link maCollapsingHdl;
	Link maExpandingHdl;
};

typedef std::pair< AtomContainerTreeListBox*, SvLBoxEntry* > AtomContainerEntryPair;

AtomContainerTreeListBox::AtomContainerTreeListBox( Window* pParent )
: SvTreeListBox( pParent, WB_HASBUTTONS|WB_HASLINES|WB_HASBUTTONSATROOT|WB_3DLOOK|WB_BORDER ),
	mpRootAtom( 0 ), mbRecursiveGuard( false )
{
	mpResMgr = ResMgr::CreateResMgr( "svt" );
	maImgCollapsed = Image( ResId( RID_IMG_TREENODE_COLLAPSED, mpResMgr ) );
	maImgExpanded = Image( ResId( RID_IMG_TREENODE_EXPANDED, mpResMgr ) );

//	SetDefaultExpandedEntryBmp( aExpanded );
//	SetDefaultCollapsedEntryBmp(aCollapsed );

	maImgFolder = Image( ResId( IMG_SVT_FOLDER, mpResMgr ) );
	maImgAtom = Image( ResId( IMG_SVT_DOCTEMPLATE_DOCINFO_SMALL, mpResMgr ) );
}

AtomContainerTreeListBox::~AtomContainerTreeListBox()
{
}

void AtomContainerTreeListBox::SetTabs()
{
	if( IsEditingActive() )
		EndEditing( TRUE );

	ClearTabList();

	short nIndent = 0; GetIndent();
	long nNodeWidthPixel = maImgCollapsed.GetSizePixel().Width();
	long nContextWidthDIV2 = nNodeWidthPixel >> 1;

	long nStartPos = 2 + ( nIndent + nContextWidthDIV2 );
	AddTab( nStartPos, SV_LBOXTAB_DYNAMIC | SV_LBOXTAB_ADJUST_CENTER );
	nStartPos += nNodeWidthPixel + 5;
	AddTab( nStartPos, SV_LBOXTAB_DYNAMIC | SV_LBOXTAB_ADJUST_CENTER | SV_LBOXTAB_SHOW_SELECTION );
	nStartPos += nContextWidthDIV2 + 5;
	AddTab( nStartPos, SV_LBOXTAB_DYNAMIC|SV_LBOXTAB_ADJUST_LEFT | SV_LBOXTAB_SHOW_SELECTION );
}

void AtomContainerTreeListBox::InitEntry(SvLBoxEntry* pEntry,const XubString& aStr,const Image& aCollEntryBmp,const Image& aExpEntryBmp)
{
	pEntry->AddItem( new SvLBoxContextBmp( pEntry,0, aCollEntryBmp,aExpEntryBmp, SVLISTENTRYFLAG_EXPANDED ) );
	pEntry->AddItem( new SvLBoxContextBmp( pEntry,0, maImgAtom, maImgAtom, SVLISTENTRYFLAG_EXPANDED ) );
	pEntry->AddItem( new AtomBoxString( pEntry, aStr ) );
}

SvLBoxEntry* AtomContainerTreeListBox::findAtom( Atom* pAtom )
{
	SvLBoxEntry* pEntry = First();
	while( pEntry )
	{
		if( pEntry->GetUserData() == pAtom )
			return pEntry;

		pEntry = Next( pEntry );
	}

	return 0;
}

BOOL AtomContainerTreeListBox::Expand( SvLBoxEntry* pParent )
{
	BOOL bRet = FALSE;
	if( !mbRecursiveGuard )
	{
		mbRecursiveGuard = true;
		AtomContainerEntryPair aPair( this, pParent );
		maExpandingHdl.Call( &aPair);

		bRet = SvTreeListBox::Expand( pParent );
		mbRecursiveGuard = false;
	}
	return bRet;
}

BOOL AtomContainerTreeListBox::Collapse( SvLBoxEntry* pParent )
{
	BOOL bRet = FALSE;
	if( !mbRecursiveGuard )
	{
		mbRecursiveGuard = true;
		AtomContainerEntryPair aPair( this, pParent );
		maCollapsingHdl.Call( &aPair);

		bRet = SvTreeListBox::Collapse( pParent );
		mbRecursiveGuard = false;
	}
	return bRet;
}

void AtomContainerTreeListBox::SetRootAtom( const Atom* pAtom )
{
	mpRootAtom = pAtom;
	InsertAtom( mpRootAtom );
}

void AtomContainerTreeListBox::InsertAtom( const Atom* pAtom, SvLBoxEntry* pParent /* = 0 */ )
{
	if( pAtom )
	{
		const DffRecordHeader& rHeader = pAtom->getHeader();

		char buffer[1024];

		rtl::OUString aText;
		AtomConfig* pAtomConfig = dynamic_cast< AtomConfig*>( gAtomConfigMap[rHeader.nRecType].get() );

		if( pAtomConfig )
            aText = pAtomConfig->getName();

		if( !aText.getLength() )
		{
			sprintf( buffer, "unknown_0x%04x", rHeader.nRecType );
			aText += rtl::OUString::createFromAscii( buffer );
		}

		sprintf( buffer, " (I: %lu L: %lu)", (UINT32)rHeader.nRecVer, (UINT32)rHeader.nRecLen );
		aText += String( rtl::OUString::createFromAscii( buffer ) );

		SvLBoxEntry* pEntry = 0;
		if( pAtom->isContainer() && pAtom->findFirstChildAtom() )
		{
			pEntry = InsertEntry( aText, maImgExpanded, maImgCollapsed, pParent );
	
			/** returns the first child atom or NULL */
			const Atom* pChildAtom = pAtom->findFirstChildAtom();

			while( pChildAtom )
			{
				InsertAtom( pChildAtom, pEntry );
				pChildAtom = pAtom->findNextChildAtom( pChildAtom );
			}
		}
		else
		{
			pEntry = InsertEntry( aText, pParent );
		}

		if( pEntry )
		{
			pEntry->SetUserData( (void*)pAtom );

			if( pAtom->isContainer() )
			{
				SvLBoxContextBmp* pBoxBmp = dynamic_cast< SvLBoxContextBmp* >( pEntry->GetItem( pEntry->ItemCount() - 2 ) );
				if( pBoxBmp )
				{
					pBoxBmp->SetBitmap1( pEntry, maImgFolder );
					pBoxBmp->SetBitmap2( pEntry, maImgFolder );
				}
			}

/*
			pEntry->ReplaceItem(
				new AtomBoxString( pEntry, aText, pImage ),
				pEntry->ItemCount() - 1 );
*/
		}
	}
}

///////////////////////////////////////////////////////////////////////

extern void load_config( const OUString& rPath );

class PPTDocument
{
public:
	PPTDocument( const rtl::OUString& rFilePath );
	~PPTDocument();

	Atom* getRootAtom() const;

private:
	void Load( const rtl::OUString& rFilePath );

	Atom* mpAtom;
	SvStream* mpDocStream;
	SotStorageRef maStorage;
};

typedef boost::shared_ptr< PPTDocument > PPTDocumentPtr;

PPTDocument::PPTDocument(const rtl::OUString& rFilePath)
: mpAtom(0), mpDocStream(0)
{
	Load( rFilePath );
}

PPTDocument::~PPTDocument()
{
	delete mpAtom;
	delete mpDocStream;
}

void PPTDocument::Load( const rtl::OUString& rFilePath )
{
    maStorage = new SotStorage( rFilePath, STREAM_STD_READ );
    if( !maStorage->GetError() )
	{
        mpDocStream = maStorage->OpenSotStream( String( RTL_CONSTASCII_USTRINGPARAM("PowerPoint Document") ), STREAM_STD_READ );
		if( mpDocStream )
		{
			DffRecordHeader aRecordHeader;
			*mpDocStream >> aRecordHeader;

			mpAtom = Atom::import( 65530, *mpDocStream );
		}
	}
}

Atom* PPTDocument::getRootAtom() const
{
	return mpAtom;
}

///////////////////////////////////////////////////////////////////////

class MSViewerWorkWindow : public WorkWindow
{
public:
	MSViewerWorkWindow();
	~MSViewerWorkWindow();

	PPTDocumentPtr Load();
	void onView();
	void onCompare();
	void onClose();

	void View( const PPTDocumentPtr& pDocument, int nPane );
	void Compare( const PPTDocumentPtr& pDocument1, const PPTDocumentPtr& pDocument2 );

	virtual void Resize();

private:
	void Sync( AtomContainerEntryPair* pPair, int nAction );

	AtomContainerTreeListBox*	mpListBox[2];
	MultiLineEdit*				mpEdit[2];
	PPTDocumentPtr				mpDocument[2];
	MenuBar*					mpMenuBar;
	PopupMenu*					mpFileMenu;
	bool mbSelectHdlGuard;
	DECL_LINK( implSelectHdl, AtomContainerTreeListBox* );
	DECL_LINK( implExpandingHdl, AtomContainerEntryPair* );
	DECL_LINK( implCollapsingHdl, AtomContainerEntryPair* );
	DECL_LINK( implMenuHdl, Menu* );
};

// -----------------------------------------------------------------------

void MSViewerWorkWindow::onView()
{
	PPTDocumentPtr pDocument( Load() );
	if( pDocument.get() )
	{
		onClose();
		View( pDocument, 0 );
	}
}

void MSViewerWorkWindow::onClose()
{
}

void MSViewerWorkWindow::onCompare()
{
	PPTDocumentPtr pDocument1( Load() );
	if( pDocument1.get() )
	{
		PPTDocumentPtr pDocument2( Load() );
		if( pDocument2.get() )
		{
			onClose();
			Compare( pDocument1, pDocument2 );
		}
	}
}

void MSViewerWorkWindow::Compare( const PPTDocumentPtr& pDocument1, const PPTDocumentPtr& pDocument2 )
{
	if( pDocument1.get() && pDocument2.get() )
	{
		Atom* pAtom1 = pDocument1->getRootAtom();
		Atom* pAtom2 = pDocument2->getRootAtom();
		pAtom1->setCompareAtom( pAtom2 );
		pAtom2->setCompareAtom( pAtom1 );
	}

	View( pDocument1, 0 );
	View( pDocument2, 1 );
}

void MSViewerWorkWindow::View( const PPTDocumentPtr& pDocument, int nPane )
{
	if( ((nPane != 0) && (nPane != 1)) || (pDocument.get() == 0) )
		return;

	mpDocument[nPane] = pDocument;

	mpListBox[nPane]->SetRootAtom( pDocument->getRootAtom() );
	mpListBox[nPane]->Expand( mpListBox[nPane]->GetEntry(0) );
	mpListBox[nPane]->Show();
	mpEdit[nPane]->Show();
	Resize();
}


PPTDocumentPtr MSViewerWorkWindow::Load()
{
	::sfx2::FileDialogHelper aDlg( ::sfx2::FILEOPEN_SIMPLE, 0 );
	String aStrFilterType( RTL_CONSTASCII_USTRINGPARAM( "*.ppt" ) );
	aDlg.AddFilter( aStrFilterType, aStrFilterType );
//	INetURLObject aFile( SvtPathOptions().GetPalettePath() );
//	aDlg.SetDisplayDirectory( aFile.GetMainURL( INetURLObject::NO_DECODE ) );

	PPTDocumentPtr pDocument;
	if ( aDlg.Execute() == ERRCODE_NONE )
	{
		pDocument.reset( new PPTDocument( aDlg.GetPath() ) );	
	}

	return pDocument;
}

// -----------------------------------------------------------------------

MSViewerWorkWindow::MSViewerWorkWindow() :
	WorkWindow( 0, WB_APP | WB_STDWORK | WB_3DLOOK ),mbSelectHdlGuard(false)
{  
    Size aOutputSize( 400, 600 );
	SetOutputSizePixel( aOutputSize );
	SetText( String( RTL_CONSTASCII_USTRINGPARAM( "MSViewer" ) ) );

    Size aOutSize( GetOutputSizePixel() );

	Font aFont( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), GetFont().GetSize() );

	mpMenuBar = new MenuBar();
	mpMenuBar->InsertItem( 1, String( RTL_CONSTASCII_USTRINGPARAM("~File" ) ) );
	mpFileMenu = new PopupMenu();
	mpFileMenu->InsertItem( 2, String( RTL_CONSTASCII_USTRINGPARAM("~View" ) ) );
	mpFileMenu->InsertItem( 3, String( RTL_CONSTASCII_USTRINGPARAM("~Compare" ) ) );
	mpFileMenu->InsertSeparator();
	mpFileMenu->InsertItem( 4, String( RTL_CONSTASCII_USTRINGPARAM("~Quit" ) ) );
	mpFileMenu->SetSelectHdl( LINK( this, MSViewerWorkWindow, implMenuHdl ) );

	mpMenuBar->SetPopupMenu( 1, mpFileMenu );
	SetMenuBar( mpMenuBar );
	int nPane;
	for( nPane = 0; nPane < 2; nPane++ )
	{
		mpListBox[nPane] = new AtomContainerTreeListBox( this );
		mpListBox[nPane]->SetSelectHdl( LINK( this, MSViewerWorkWindow, implSelectHdl ) );
		mpListBox[nPane]->SetExpandingHdl( LINK( this, MSViewerWorkWindow, implExpandingHdl ) );
		mpListBox[nPane]->SetCollapsingHdl( LINK( this, MSViewerWorkWindow, implCollapsingHdl ) );
		
		mpEdit[nPane] = new MultiLineEdit(this, WB_3DLOOK | WB_BORDER | WB_LEFT | WB_TOP | WB_READONLY | WB_HSCROLL | WB_VSCROLL );
		mpEdit[nPane]->SetReadOnly( TRUE );
		mpEdit[nPane]->SetReadOnly( TRUE );
		mpEdit[nPane]->SetControlFont( aFont );
	}
}

// -----------------------------------------------------------------------

static String GetAtomText( const Atom* pAtom )
{
	String aText;
	if( pAtom )
	{
		const DffRecordHeader& rHeader = pAtom->getHeader();
		char buffer[512];
		sprintf( buffer, "Version = %lu\n\rInstance = %lu\n\rVersionInstance = %lu\n\rLength = %lu\n\r",
		(UINT32)rHeader.nRecVer,
		(UINT32)rHeader.nRecInstance,
		(UINT32)rHeader.nImpVerInst,
		(UINT32)rHeader.nRecLen );
		aText = rtl::OUString::createFromAscii( buffer );
		if( pAtom->isContainer() )
		{

		}
		else
		{
			pAtom->seekToContent();
			AtomConfig* pAtomConfig = dynamic_cast< AtomConfig* >( gAtomConfigMap[pAtom->getType()].get() );
			if( pAtomConfig )
			{
				sal_Size nLength = pAtom->getLength();
				aText += String( pAtomConfig->format( pAtom->getStream(), nLength ) );
			}
			else
			{
				sal_Size nLength = pAtom->getLength();
				aText += String( ElementConfig::dump_hex( pAtom->getStream(), nLength ) );
			}
		}
	}

	return aText;
}

IMPL_LINK(MSViewerWorkWindow,implSelectHdl, AtomContainerTreeListBox*, pListBox )
{
	int nPane = (pListBox == mpListBox[1]) ? 1 : 0;
	SvLBoxEntry* pEntry = mpListBox[nPane]->FirstSelected();
	if( pEntry && pEntry->GetUserData() )
	{
		Atom* pAtom = static_cast<Atom*>( pEntry->GetUserData() );
		mpEdit[nPane]->SetText( GetAtomText( pAtom ) );

		if(!mbSelectHdlGuard)
		{
			mbSelectHdlGuard = true;
			// select other
			AtomContainerEntryPair aPair( pListBox, pEntry );
			Sync( &aPair, 2 );
			mbSelectHdlGuard = false;
		}
	}
	return 0;
}

void MSViewerWorkWindow::Sync( AtomContainerEntryPair* pPair, int nAction )
{
	if( mpDocument[0].get() && mpDocument[1].get() && pPair->first && pPair->second )
	{
		AtomContainerTreeListBox* pDestinationListBox = (pPair->first == mpListBox[0]) ? mpListBox[1] : mpListBox[0];

		Atom* pAtom = static_cast<Atom*>(pPair->second->GetUserData());
		if( pAtom && pAtom->getCompareAtom() )
		{
			SvLBoxEntry* pEntry = pDestinationListBox->findAtom( pAtom->getCompareAtom() );
			
			if(pEntry )
			{
				if( nAction == 0 )
				{
					pDestinationListBox->Expand( pEntry );
				}
				else if( nAction == 1 )
				{
					pDestinationListBox->Collapse( pEntry );
				}
				else
				{
					pDestinationListBox->Select( pEntry );
				}
			}
		}
	}
}

IMPL_LINK(MSViewerWorkWindow, implExpandingHdl, AtomContainerEntryPair*, pPair )
{
	SvLBoxEntry* pEntry = pPair->second;
	if( pEntry && pEntry->GetUserData() )
	{
		Atom* pAtom = static_cast<Atom*>( pEntry->GetUserData() );
		pAtom->compare( pAtom->getCompareAtom() );
	}

	Sync( pPair, 0 );

	return 0;
}

IMPL_LINK(MSViewerWorkWindow, implCollapsingHdl, AtomContainerEntryPair*, pPair )
{
	Sync( pPair, 1 );

	return 0;
}

IMPL_LINK( MSViewerWorkWindow, implMenuHdl, Menu*, pMenu )
{
	if( pMenu )
	{
		USHORT nId = pMenu->GetCurItemId();
		switch( nId )
		{
		case 2: onView(); break;
		case 3: onCompare(); break;
		case 4: Application::Quit(); break;
		}
	}
	return 0;
}

// -----------------------------------------------------------------------

MSViewerWorkWindow::~MSViewerWorkWindow()
{
	int nPane;
	for( nPane = 0; nPane < 2; nPane++ )
	{
		delete mpListBox[nPane];
		delete mpEdit[nPane];
	}

	delete mpFileMenu;
	delete mpMenuBar;
}

// -----------------------------------------------------------------------

void MSViewerWorkWindow::Resize()
{
	int nPaneCount = ((mpDocument[0].get() != 0) ? 1 : 0) + ((mpDocument[1].get() != 0) ? 1 : 0);

    Size aOutputSize( GetOutputSizePixel() );
	int nHeight = aOutputSize.Height() >> 1;
	if( nPaneCount )
	{
		int nWidth = aOutputSize.Width();
		if( nPaneCount == 2 )
			nWidth >>= 1;

		int nPosX = 0;

		int nPane;
		for( nPane = 0; nPane < 2; nPane++ )
		{
			mpListBox[nPane]->SetPosSizePixel( nPosX,0, nWidth, nHeight );
			mpEdit[nPane]->SetPosSizePixel( nPosX, nHeight, nWidth, aOutputSize.Height() - nHeight );
			nPosX += nWidth;
		}
	}
}

// -----------------------------------------------------------------------

// -----------------------------------------------------------------------

    SAL_IMPLEMENT_MAIN()
{
	if( argc > 3 )
		return 0;

    uno::Reference< lang::XMultiServiceFactory > xMSF;
	try
	{
        uno::Reference< uno::XComponentContext > xCtx( cppu::defaultBootstrap_InitialComponentContext() );
        if ( !xCtx.is() )
        {
            DBG_ERROR( "Error creating initial component context!" );
            return -1;
        }

        xMSF = uno::Reference< lang::XMultiServiceFactory >(xCtx->getServiceManager(), uno::UNO_QUERY );

        if ( !xMSF.is() )
        {
            DBG_ERROR( "No service manager!" );
            return -1;
        }

        // Init UCB
        uno::Sequence< uno::Any > aArgs( 2 );
        aArgs[ 0 ] <<= rtl::OUString::createFromAscii( UCB_CONFIGURATION_KEY1_LOCAL );
	    aArgs[ 1 ] <<= rtl::OUString::createFromAscii( UCB_CONFIGURATION_KEY2_OFFICE );
	    sal_Bool bSuccess = ::ucb::ContentBroker::initialize( xMSF, aArgs );
	    if ( !bSuccess )
	    {
		    DBG_ERROR( "Error creating UCB!" );
		    return -1;
	    }

	}
    catch ( uno::Exception const & )
	{
        DBG_ERROR( "Exception during creation of initial component context!" );
		return -1;
	}
	comphelper::setProcessServiceFactory( xMSF );
	
    InitVCL( xMSF );

	String aConfigURL;
	if( ::utl::LocalFileHelper::ConvertPhysicalNameToURL( Application::GetAppFileName(), aConfigURL ) )
	{
		INetURLObject aURL( aConfigURL );

		aURL.removeSegment();
		aURL.removeFinalSlash();
		aURL.Append( String(  RTL_CONSTASCII_USTRINGPARAM( "msview.xml" )  ) );

		load_config( aURL.GetMainURL( INetURLObject::NO_DECODE ) );
	}

	{
		MSViewerWorkWindow aMainWindow;

		if( argc >= 2 )
		{
			const rtl::OUString aFile1( rtl::OUString::createFromAscii(argv[1]) );
			PPTDocumentPtr pDocument1( new PPTDocument(  aFile1 ) );

			if( argc == 3 )
			{
				const rtl::OUString aFile2( rtl::OUString::createFromAscii(argv[2]) );

				PPTDocumentPtr pDocument2;
				pDocument2.reset( new PPTDocument( aFile2 ) );
				aMainWindow.Compare( pDocument1, pDocument2 );
			}
			else
			{
				aMainWindow.View( pDocument1, 0 );
			}
		}

		aMainWindow.Show();

		Application::Execute();
	}

    DeInitVCL();

    return 0;
}
