/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: pm_cmn.cpp,v 1.21 2001/04/23 21:51:20 vlabella Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "pm_cmn.cpp"
    #pragma interface "pm_cmn.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "wx/pmf/pm_cmn.h"
#include "wx/pmf/pm.h"

#include "wx/list.h"
#include "wx/menuitem.h"

#include "wx/textdlg.h"
#include "wx/filedlg.h"
#include "wx/msgdlg.h"

#include "wx/protocol/ftp.h"
#include "wx/protocol/http.h"

#include "wx/debug.h"
#include "wx/log.h"
#include "wx/txtstrm.h"

#include "mimetypes.h"

#ifndef MAX_PATH
#define MAX_PATH 260
#endif

/***** Implementation for class wxPMMergableMenuChain *****/

wxPMMergableMenuChain::wxPMMergableMenuChain()

	: m_pCommands( NULL )
{}

wxPMMergableMenuChain::wxPMMergableMenuChain( const char* predecessorPath, 
											  wxCommandInfo* pCommands )

	: m_PredecessorPath( predecessorPath ),
	  m_pCommands( pCommands )
{}

/***** Implementation for class wxPMFileInfo *****/

MPFileLoaderListT wxPMFileInfo::msRegisteredLoaders; // TDB:: memory-leaks

IMPLEMENT_DYNAMIC_CLASS( wxPMFileInfo, wxObject )

wxMimeTypesManager* wxPMFileInfo::msMimeMgr = NULL;

wxPMFileInfo::wxPMFileInfo()

	: m_pLoader( NULL ),
	  m_pLoaderProto( NULL )
{
	RegisterLoaders();
}

wxPMFileInfo::~wxPMFileInfo()
{
/*
  MEMORY LEAK
  This is a static, so the question is when to really delete it :-(
    if ( msMimeMgr )
    {
        delete msMimeMgr;
        msMimeMgr = 0;
    }
*/
	if ( m_pLoader )
        delete m_pLoader;
}

wxPMFileInfo::wxPMFileInfo( const wxPMFileInfo& other )
	: m_pLoader( NULL ),
	  m_pLoaderProto( NULL )
{
	m_Path = other.m_Path;
	m_Type = other.m_Type;
	m_pLoaderProto = other.m_pLoaderProto;
}

const wxPMFileInfo& wxPMFileInfo::operator=( const wxPMFileInfo& other )
{
	m_Path = other.m_Path;
	m_Type = other.m_Type;
	m_pLoaderProto = other.m_pLoaderProto;

	return *this;
}

bool wxPMFileInfo::operator==( const wxPMFileInfo& other ) const
{
#ifdef __WINDOWS__
	return m_Path.CmpNoCase( other.m_Path ) == 0;
#else
	return m_Path == other.m_Path;
#endif

}

wxPMFileInfo::wxPMFileInfo( const wxString& path )

	: m_Path( path ),
	  m_pLoader( NULL ),
	  m_pLoaderProto( NULL )

{
	RegisterLoaders();

	DetectType(); // auto-detect it's type automatically
}

void wxPMFileInfo::SetPath( const wxString& path )
{
	m_Path = path;

	DetectType(); // auto-detect
}

const wxString& wxPMFileInfo::GetPath() const
{
	return m_Path;
}

const wxString& wxPMFileInfo::GetType() const
{
	return m_Type;
}

bool wxPMFileInfo::DetectType() const
{
	m_pLoaderProto = NULL; 

	// FIXME:: where msMimeMgr should be deleted?

	if ( msMimeMgr == NULL ) 
		
		msMimeMgr = new wxMimeTypesManager;

	if ( m_Path.Length() == 0 ) return FALSE;

	size_t i = m_Path.Length() - 1;

	// extract extention

	while( i > 0 && m_Path[i] != '.' ) --i;

	wxString ext = wxString( m_Path, i + 1, m_Path.Length() - i - 1 );
	ext = ext.Lower();

	// detect mime type

	wxFileType* pType = msMimeMgr->GetFileTypeFromExtension( ext );

	if ( pType ) pType->GetMimeType( &m_Type );

	if ( m_Type == "" ) // try fallback if not found in config or registry
	
		m_Type = wxPMFileInfo::FindFallBackForExtention( ext );

    if (m_Type != "")  // No need to check if the mime type was not found
    {
	    // detect location type
	    for( i = 0; i != msRegisteredLoaders.Count(); ++i )
        {
		    if ( msRegisteredLoaders[i]->CanAcceptPath( m_Path ) )
		    {
			    m_pLoaderProto = msRegisteredLoaders[i];
			    break;
		    }
        }
    }

	// detection succeeds only when both location and mime types are detected
	return m_Type != "" && m_pLoaderProto != NULL;
}

int wxPMFileInfo::GetLocationType()	const
{
	if ( !m_pLoader && !DetectType() )
        return UNKNOWN_LOCATION;

	return m_pLoader->GetLocationType();
}

wxString wxPMFileInfo::GetLocationTypeStr() const
{
	if ( !m_pLoader && !DetectType() )
        return "UNKNOWN_LOCATION";
					 
	return m_pLoader->GetLocationTypeStr();
}

void wxPMFileInfo::ChangeExtension( const wxString newExt )
{
	if ( m_Path.Length() == 0 ) return;

	size_t i = m_Path.Length();

	while( i > 0 && m_Path[i-1] != '.' ) --i;

	m_Path = wxString( m_Path, 0, i ) + newExt;
}

wxString wxPMFileInfo::GetNameWithoutPath() const
{
	// TBD:: hanle URL and other location-types

	return ::wxFileNameFromPath( m_Path );
}

wxString wxPMFileInfo::GetPathOnly() const
{
	return ::wxPathOnly( m_Path );
}

wxString wxPMFileInfo::GetNameWithoutExtension() const
{
	wxString fname = ::wxFileNameFromPath( m_Path );

	size_t i = fname.Length();

	while( i > 0 && fname[i-1] != '.' ) --i;

	if ( i == 0 ) return fname;

	--i;

	return wxString( fname, 0, i );
}

wxPMFileLoader* wxPMFileInfo::GetFileLoader() const
{
	if ( !m_pLoader )
	{
		if ( !m_pLoaderProto )

			DetectType(); // detects both mime and location types

		if ( m_pLoaderProto ) 
		{
			m_pLoader = m_pLoaderProto->Clone();

			m_pLoader->SetFileInfo( (wxPMFileInfo*)(const wxPMFileInfo*)this );
		}
	}

	return m_pLoader;
}

void wxPMFileInfo::RegisterLoaders()
{
	// default registration is done by searching all class-info
	// objects which are derivatives of wxPMFileLoader, and automaticaly
	// instantiating the matched ones

	if ( wxPMFileInfo::msRegisteredLoaders.Count() == 0 )
	{
		// typecasting necessary for new changes in wx2.3
		wxClassInfo* pInfo = (wxClassInfo *)wxClassInfo::GetFirst();

		while( pInfo )
		{
			if ( pInfo->IsKindOf( CLASSINFO(wxPMFileLoader) ) && 
				 pInfo != CLASSINFO(wxPMFileLoader)
			   )
			{
				wxPMFileLoader* pLoader = (wxPMFileLoader*)pInfo->CreateObject();

				wxASSERT_MSG( pLoader != NULL, 
							  "All derivatives of wxPMFileLoader should be declared as dynamic classes!" );

				wxPMFileInfo::RegisterFileLoader( pLoader );
			}

			// typecasting necessary for new changes in wx2.3
			pInfo = (wxClassInfo *)pInfo->GetNext();
		}
	}
}

/*** static methods ***/

// TBD:: should be made statics of wxPMFileInfo class
static wxStringList _gTypes;
static wxStringList _gExtentions;

void wxPMFileInfo::RegisterTypeFallback( const wxString& mimeType, const wxString& ext )
{
	_gTypes.Add( mimeType );
	_gExtentions.Add( ext );
}

wxString wxPMFileInfo::FindFallBackForExtention( const wxString& ext )
{
    size_t i;
	for( i = 0; i != _gTypes.GetCount(); ++i )

		if ( _gExtentions[i] == ext )

			return _gTypes[i];

	return "";
}

void wxPMFileInfo::RegisterFileLoader( wxPMFileLoader* pLoader )
{
    size_t i;
	for( i = 0; i != msRegisteredLoaders.Count(); ++i )

		if ( pLoader->GetClassInfo() == msRegisteredLoaders[i]->GetClassInfo() )

			return; // already registered

	msRegisteredLoaders.Add( pLoader );
}

void wxPMFileInfo::RegisterDefaultTypeFallbacks()
{
	RegisterTypeFallback( "image/bitmap", "bmp" );
	RegisterTypeFallback( "image/png",    "png" );
	RegisterTypeFallback( "image/xpm",    "xpm" );
	RegisterTypeFallback( "image/gif",    "gif" );
	RegisterTypeFallback( "image/jpeg",   "jpg" );
	RegisterTypeFallback( "image/jpeg",   "jpeg" );
	RegisterTypeFallback( "image/icon",   "ico" );

	RegisterTypeFallback( "video/mpeg",   "mpeg" );
	RegisterTypeFallback( "video/mpeg",   "mpg" );
	RegisterTypeFallback( "video/avi",    "avi" );

	RegisterTypeFallback( wxwMIME_PLAIN_TEXT ,      "txt" );
	RegisterTypeFallback( wxwMIME_PLAIN_TEXT ,      "in" );  // "unoffical"
	RegisterTypeFallback( wxwMIME_PLAIN_TEXT ,      "mak" ); // -/-
	RegisterTypeFallback( wxwMIME_PLAIN_TEXT ,      "vc" );  // -/-

	RegisterTypeFallback( wxwMIME_HTML,             "html" );
	RegisterTypeFallback( wxwMIME_HTM,              "htm" );

	RegisterTypeFallback( wxwMIME_C_HEADER,         "h" );
	RegisterTypeFallback( wxwMIME_C_HEADER,         "hpp" );
	RegisterTypeFallback( wxwMIME_C_SOURCE,         "c" );
	RegisterTypeFallback( wxwMIME_CPP_SOURCE,       "cpp" );
	RegisterTypeFallback( wxwMIME_CPP_SOURCE,       "C" );
	RegisterTypeFallback( wxwMIME_PYTHON,           wxwEXT_PYTHON );
	RegisterTypeFallback( wxwMIME_JAVA,             wxwEXT_JAVA );
	RegisterTypeFallback( wxwMIME_WX_RESOURCE,      wxwEXT_WX_RESOURCE );
	RegisterTypeFallback( wxwMIME_WIDGET_LOOK,      wxwEXT_WIDGET_LOOK );

	RegisterTypeFallback( "application/x-java-class-file", "class" );
	RegisterTypeFallback( "application/x-windows-dll",     "dll" );
	
	RegisterTypeFallback( "application/x-windows-help", "hlp" );
	RegisterTypeFallback( "application/x-chm-help",     "chm" );
	RegisterTypeFallback( "application/x-zip",          "zip" );

	RegisterTypeFallback( "application/python-compiled", "pyc" );

	// see also wxPMFileBrowserPane::RegisterFileTypeIcons (of "pmgui.cpp")
}

wxString wxPMFileInfo::GetDirectoryFromPath( const wxString& path )
{
	// FOR NOW:: impl. only works for names of local-files

//	wxString name = ::wxFileNameFromPath( path );
//	return wxString( path, 0, path.Length() - name.Length() );

    return wxString( ::wxPathOnly(path) );
}

bool wxPMFileInfo::CreateDirsForPath( const wxString& path )
{
//	wxASSERT( ::wxIsAbsolutePath( path ) ); // DBG::

	wxString dir = wxPMFileInfo::GetDirectoryFromPath( path );

	if ( dir.Length() == 0 ) return TRUE;

	if ( !::wxDirExists( dir ) )

		return wxMkdir( dir );

	return TRUE;
}

/***** Implementation for class wxPMFileLoader *****/

IMPLEMENT_CLASS( wxPMFileLoader, wxObject )

wxPMFileLoader::wxPMFileLoader()
	: m_pFileInfo( NULL )
{}

void wxPMFileLoader::SetFileInfo( wxPMFileInfo* pInfo )
{
	m_pFileInfo = pInfo;
}

wxPMFileInfo* wxPMFileLoader::GetFileInfo()
{
	return m_pFileInfo;
}

wxPMFileLoader* wxPMFileLoader::Clone()
{
	wxFAIL_MSG( "wxPMFileLoader::Clone() should be overridden in the derived class!" );

	return NULL;
}

bool wxPMFileLoader::CanAcceptPath( const wxString& path )
{
	return FALSE;
}

bool wxPMFileLoader::CreateDirsForPath()
{
	return TRUE;
}

bool wxPMFileLoader::FileExists()
{
	return FALSE;
}

bool wxPMFileLoader::CanWrite()
{
	return FALSE;
}

wxInputStream* wxPMFileLoader::GetInputStream()
{
	return NULL;
}

wxOutputStream* wxPMFileLoader::GetOutputStream()
{
	return NULL;
}

bool wxPMFileLoader::IsOpen()
{
	return FALSE;
}

bool wxPMFileLoader::Close()
{
	return FALSE;
}

int wxPMFileLoader::GetLocationType()
{
	return wxPMFileInfo::UNKNOWN_LOCATION;
}

wxString wxPMFileLoader::GetLocationTypeStr()
{
	return "UNKNOWN_LOCATION";
}

/***** Implementation for class wxPMLocalFileLoader *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMLocalFileLoader, wxPMFileLoader )

wxPMLocalFileLoader::wxPMLocalFileLoader()

	: m_pInputStream( NULL ),
	  m_pOutputStream( NULL )
{}

wxPMLocalFileLoader::~wxPMLocalFileLoader()
{
	Close();
}

wxPMFileLoader* wxPMLocalFileLoader::Clone()
{
	return new wxPMLocalFileLoader();
}

bool wxPMLocalFileLoader::CanAcceptPath( const wxString& path )
{
	return path.Find( "//" ) == -1; // FOR NOW:: primitive
}

bool wxPMLocalFileLoader::CreateDirsForPath()
{
	return wxPMFileInfo::CreateDirsForPath( GetFileInfo()->GetPath() );
}

bool wxPMLocalFileLoader::FileExists()
{
	return ::wxFileExists( GetFileInfo()->GetPath() );
}

bool wxPMLocalFileLoader::CanWrite()
{
	return TRUE; // FIXME:: should we attemt to open it here?
}

wxInputStream* wxPMLocalFileLoader::GetInputStream()
{
	if ( !m_pInputStream )

		m_pInputStream = new wxFileInputStream( GetFileInfo()->GetPath() );

	return m_pInputStream;
}

wxOutputStream* wxPMLocalFileLoader::GetOutputStream()
{
	if ( !m_pOutputStream )

		m_pOutputStream = new wxFileOutputStream( GetFileInfo()->GetPath() );

	return m_pOutputStream;
}

bool wxPMLocalFileLoader::IsOpen()
{
	return m_pOutputStream || m_pInputStream;
}

bool wxPMLocalFileLoader::Close()
{
	if ( m_pInputStream ) 
	{
		delete m_pInputStream;
		m_pInputStream = NULL;
	}

	if ( m_pOutputStream)
	{
		delete m_pOutputStream;
		m_pOutputStream = NULL;
	}

	return TRUE;
}

int wxPMLocalFileLoader::GetLocationType()
{
	if ( ::wxIsAbsolutePath( GetFileInfo()->GetPath() ) )

		return wxPMFileInfo::ABSOLUTE_LOCAL_FILE;
	else
		return wxPMFileInfo::RELATIVE_LOCAL_FILE;
}

wxString wxPMLocalFileLoader::GetLocationTypeStr()
{
	if ( GetLocationType() == wxPMFileInfo::ABSOLUTE_LOCAL_FILE )

		return "ABSOLUTE_LOCAL_FILE";
	else
		return "RELATIVE_LOCAL_FILE";
}

/***** Implementation for class wxPMHTTPFileLoader *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMHTTPFileLoader, wxPMFileLoader )

wxPMHTTPFileLoader::wxPMHTTPFileLoader()

	: m_pInputStream( NULL )
{}

wxPMHTTPFileLoader::~wxPMHTTPFileLoader()
{
	Close();
}

wxPMFileLoader* wxPMHTTPFileLoader::Clone()
{
	return new wxPMHTTPFileLoader();
}

bool wxPMHTTPFileLoader::CanAcceptPath( const wxString& path )
{
	return path.Find( "HTTP://" ) != -1 ||
		   path.Find( "HTTP://" ) != -1;
}

bool wxPMHTTPFileLoader::FileExists()
{
	return GetInputStream() != NULL; // FIXME:: correct?
}

bool wxPMHTTPFileLoader::CanWrite()
{
	return FALSE; // no write-access for HTTP-protocol
}

wxInputStream* wxPMHTTPFileLoader::GetInputStream()
{
	if ( !m_pInputStream )
	{
		wxURL url( GetFileInfo()->GetPath() );

		m_pInputStream = url.GetInputStream();
	}

	return m_pInputStream;
}

wxOutputStream* wxPMHTTPFileLoader::GetOutputStream()
{
	return NULL; // no write-access for HTTP-protocol
}

bool wxPMHTTPFileLoader::IsOpen()
{
	return m_pInputStream != NULL;
}

bool wxPMHTTPFileLoader::Close()
{
	if ( m_pInputStream )
	{
		delete m_pInputStream;
		m_pInputStream = NULL;
		return TRUE;
	}

	return FALSE;
}

int wxPMHTTPFileLoader::GetLocationType()
{
	return wxPMFileInfo::URL_LOCATION;
}

wxString wxPMHTTPFileLoader::GetLocationTypeStr()
{
	return "HTTP_LOCATION";
}

/***** Implementation for class wxPMFTPFileLoader *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMFTPFileLoader, wxPMFileLoader )

wxPMFTPFileLoader::wxPMFTPFileLoader()

	: m_pInputStream( NULL ),
	  m_pOutputStream( NULL ),
	  m_User( "ftp" ),
	  m_Password( "joe@aol.com" )
{}

wxPMFTPFileLoader::~wxPMFTPFileLoader()
{
	Close();
}

wxPMFileLoader* wxPMFTPFileLoader::Clone()
{
	return new wxPMFTPFileLoader();
}

bool wxPMFTPFileLoader::CanAcceptPath( const wxString& path )
{
	return path.Find( "FTP://" ) != -1 ||
		   path.Find( "FTP://" ) != -1;
}

bool wxPMFTPFileLoader::FileExists()
{
	return TRUE; // FIXME:: what to do??
}

bool wxPMFTPFileLoader::CanWrite()
{
	return TRUE; // FIXME:: what to do??
}

wxInputStream* wxPMFTPFileLoader::GetInputStream()
{
	if ( !m_pInputStream )
	{
		wxURL url( GetFileInfo()->GetPath() );

		m_pInputStream = url.GetInputStream();
	}

	return m_pInputStream;
}

wxOutputStream* wxPMFTPFileLoader::GetOutputStream()
{
	if ( !m_pOutputStream )
	{
		wxURL url( GetFileInfo()->GetPath() );

		wxASSERT( url.GetProtocol().IsKindOf( CLASSINFO(wxFTP) ) ); // DBG::

		wxFTP* pFTP = (wxFTP*)&url.GetProtocol();

		pFTP->SetUser( m_User );
		pFTP->SetPassword( m_Password );

		m_pOutputStream = pFTP->GetOutputStream( GetFileInfo()->GetPath() );
	}

	return m_pOutputStream;
}

bool wxPMFTPFileLoader::IsOpen()
{
	return m_pInputStream || m_pOutputStream;
}

bool wxPMFTPFileLoader::Close()
{
	if ( m_pInputStream ) 
	{
		delete m_pInputStream;
		m_pInputStream = NULL;
	}

	if ( m_pOutputStream)
	{
		delete m_pOutputStream;
		m_pOutputStream = NULL;
	}

	return TRUE;
}

int wxPMFTPFileLoader::GetLocationType()
{
	return wxPMFileInfo::URL_LOCATION;
}

wxString wxPMFTPFileLoader::GetLocationTypeStr()
{
	return "FTP_LOCATION";
}

void wxPMFTPFileLoader::SetUser( const wxString& user )
{
	m_User = user;
}

void wxPMFTPFileLoader::SetPassword( const wxString& password )
{
	m_Password = password;
}

/***** Implementation for class wxPMLayoutPreference *****/

wxPMSerializer::wxPMSerializer( wxConfigBase* pImplementingConfig, bool isLoading )

	: m_pImp( pImplementingConfig ),
	  m_IsLoading( isLoading )
{}

wxPMSerializer::~wxPMSerializer()
{
	if ( m_pImp ) delete m_pImp;
}

bool wxPMSerializer::IsLoading()
{
	return m_IsLoading;
}

void wxPMSerializer::SetPath( const wxString& strPath )
{
	m_pImp->SetPath( strPath );
}

const wxString& wxPMSerializer::GetPath() const
{
	return m_pImp->GetPath();
}

bool wxPMSerializer::GetFirstGroup( wxString& str, long& lIndex ) const
{
	return m_pImp->GetFirstGroup( str, lIndex );
}

bool wxPMSerializer::GetNextGroup( wxString& str, long& lIndex ) const
{
	return m_pImp->GetNextGroup( str, lIndex );
}

bool wxPMSerializer::GetFirstEntry( wxString& str, long& lIndex ) const
{
	return m_pImp->GetFirstEntry( str, lIndex );
}

bool wxPMSerializer::GetNextEntry( wxString& str, long& lIndex ) const
{
	return m_pImp->GetNextEntry( str, lIndex );
}

size_t wxPMSerializer::GetNumberOfEntries( bool bRecursive ) const
{
	return m_pImp->GetNumberOfEntries( bRecursive );
}

size_t wxPMSerializer::GetNumberOfGroups( bool bRecursive ) const
{
	return m_pImp->GetNumberOfGroups( bRecursive );
}

bool wxPMSerializer::HasGroup( const wxString& strName ) const
{
	return m_pImp->HasGroup( strName );
}

bool wxPMSerializer::HasEntry( const wxString& strName ) const
{
	return m_pImp->HasEntry( strName );
}

bool wxPMSerializer::Read( const wxString& key, wxString* pStr ) const
{
	return m_pImp->Read( key, pStr );
}

bool wxPMSerializer::Read( const wxString& key, wxString* pStr, const wxString& defVal ) const
{
	return m_pImp->Read( key, pStr, defVal );
}

wxString wxPMSerializer::Read( const wxString& key, const wxString& defVal ) const
{
	return m_pImp->Read( key, defVal );
}

bool wxPMSerializer::Read( const wxString& key, long* pl ) const
{
	return m_pImp->Read( key, pl );
}

bool wxPMSerializer::Read( const wxString& key, long* pl, long defVal ) const
{
	return m_pImp->Read( key, pl, defVal );
}

long wxPMSerializer::Read( const wxString& strKey, long defVal ) const
{
	return m_pImp->Read( strKey, defVal );
}

bool wxPMSerializer::Read( const wxString& key, int* pi ) const
{
	return m_pImp->Read( key, pi );
}

bool wxPMSerializer::Read( const wxString& key, int* pi, int defVal ) const
{
	return m_pImp->Read( key, pi, defVal );
}

bool wxPMSerializer::Read( const wxString& key, double* val ) const
{
	return m_pImp->Read( key, val );
}

bool wxPMSerializer::Read( const wxString& key, double* val, double defVal ) const
{
	return m_pImp->Read( key, val, defVal );
}

bool wxPMSerializer::Read( const wxString& key, bool* val ) const
{
	return m_pImp->Read( key, val );
}

bool wxPMSerializer::Read( const wxString& key, bool* val, bool defVal ) const
{
	return m_pImp->Read( key, val, defVal );
}

bool wxPMSerializer::Write( const wxString& key, const wxString& value )
{
	return m_pImp->Write( key, value );
}

bool wxPMSerializer::Write( const wxString& key, long value )
{
	return m_pImp->Write( key, value );
}

bool wxPMSerializer::Write( const wxString& key, double value )
{
	return m_pImp->Write( key, value );
}

bool wxPMSerializer::Write( const wxString& key, bool value )
{
	return m_pImp->Write( key, value );
}

bool wxPMSerializer::Flush( bool bCurrentOnly )
{
	return m_pImp->Flush( bCurrentOnly );
}

bool wxPMSerializer::RenameEntry( const wxString& oldName, const wxString& newName )
{
	return m_pImp->RenameEntry( oldName, newName );
}

bool wxPMSerializer::RenameGroup( const wxString& oldName, const wxString& newName )
{
	return m_pImp->RenameGroup( oldName, newName );
}

bool wxPMSerializer::DeleteEntry( const wxString& key, bool bDeleteGroupIfEmpty )
{
	return m_pImp->DeleteEntry( key, bDeleteGroupIfEmpty );
}

bool wxPMSerializer::DeleteGroup( const wxString& key )
{
	return m_pImp->DeleteGroup( key );
}

bool wxPMSerializer::DeleteAll()
{
	return m_pImp->DeleteAll();
}

bool wxPMSerializer::Exchange( const wxString& key, long& l )
{
	if ( m_IsLoading ) return m_pImp->Read( key, &l );
				 else return m_pImp->Write( key, l );
}

bool wxPMSerializer::Exchange( const wxString& key, bool& b )
{
	bool ok = FALSE;
	long l = (long)b;

	if ( m_IsLoading ) 
	{
		ok = m_pImp->Read( key, &l );

		if ( ok ) 
			b = l != 0;
		else
			b = 0;
	}
	else 
		ok = m_pImp->Write( key, b ? 1L : 0L  );

	return ok;
}

bool wxPMSerializer::Exchange( const wxString& key, int& i )
{
	if ( m_IsLoading ) return m_pImp->Read( key, &i );
				 else return m_pImp->Write( key, (long)i );
}

bool wxPMSerializer::Exchange( const wxString& key, double& d )
{
	if ( m_IsLoading ) return m_pImp->Read( key, &d );
				 else return m_pImp->Write( key, d );
}

bool wxPMSerializer::Exchange( const wxString& key, wxString& s )
{
	if ( m_IsLoading ) return m_pImp->Read( key, &s );
				 else return m_pImp->Write( key, s );
}

bool wxPMSerializer::Exchange( const wxString& key, wxPoint& pos )
{
	if ( m_IsLoading ) 
	
		return m_pImp->Read( key + "_x", &pos.x ) &&
			   m_pImp->Read( key + "_y", &pos.y );
	else
		return m_pImp->Write( key + "_x", (long)pos.x ) &&
			   m_pImp->Write( key + "_y", (long)pos.y );
}

bool wxPMSerializer::Exchange( const wxString& key, wxSize& size )
{
	if ( m_IsLoading ) 
	
		return m_pImp->Read( key + "_width",  &size.x ) &&
			   m_pImp->Read( key + "_height", &size.y );
	else
		return m_pImp->Write( key + "_width",  (long)size.x ) &&
			   m_pImp->Write( key + "_height", (long)size.y );
}

bool wxPMSerializer::Exchange( const wxString& key, wxStringList& lst )
{
	if ( m_IsLoading )
	{
		long cnt;
		if ( !Exchange( key + "_count", cnt ) ) return FALSE;
		lst.Clear();

		wxString s;
        size_t i;
		for( i = 0; i != (size_t)cnt; ++i )
		{
			if ( !Read( IndexedKey(key,i), &s ) ) return FALSE;
			lst.Add( s );
		}
	}
	else
	{
		long cnt = lst.GetCount();
		if ( !Exchange( key + "_count", cnt ) )
			return FALSE;

        size_t i;
        wxString sList;
        for( i = 0; i != (size_t)cnt; ++i )
        {
            sList = lst[i] ;
            if ( !Write( (const wxString&)IndexedKey(key,i), sList ) )
                return FALSE;
        }

	}

	return TRUE;
}

wxString wxPMSerializer::IndexedKey( const wxString& key, size_t index )
{
	// dont use sprintf(..) to avoid #include <stdio.h>

	char buf[10];
	sprintf( buf, "%d", index );

	return key + "_" + wxString(buf);
}

/***** Implementation for class wxPMService *****/

wxPMService::wxPMService()

	: m_IsStarted( FALSE )
{}

wxPMService::~wxPMService()
{}

bool wxPMService::Start()
{
	return TRUE;
}

bool wxPMService::Stop()
{
	return TRUE;
}

bool wxPMService::IsStarted()
{
	return m_IsStarted;
}

void wxPMService::SetIsStarted( bool isStarted )
{
	m_IsStarted = isStarted;
}

bool wxPMService::SerializeState( wxPMSerializer& ser )
{
	return TRUE;
}

wxPMMergableMenuChain* wxPMService::GetMergableMenuChain()
{
	return NULL; // no menus by default
}

wxString wxPMService::GetDisplayName()
{
	// default imp.
	return wxString("<") + GetWxObject()->GetClassInfo()->GetClassName() + ">";
}

wxString wxPMService::GetDescription()
{
	return "";
}

void* wxPMService::GetInterface( const char* infName, const char* version )
{
	return NULL;
}

bool wxPMService::SupportInterface( const char* infName, const char* version )
{
	return GetInterface( infName ) != NULL;
}

bool wxPMService::AssociateData( wxPMService& toService,
								 wxObject* pData )
{
	return wxPMBootSvc().AssociateData( *this, toService, pData );
}

wxObject* wxPMService::GetAssociatedData( wxPMService& forService )
{
	return wxPMBootSvc().GetAssociatedData( *this, forService );
}

/***** Implementation for class wxPMSimpleService *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMSimpleService, wxEvtHandler )

wxPMSimpleService::wxPMSimpleService()
{
	// this hack is absolutely a must for each service. Using the 
	// ref-data member of wxObject, PMF can safely cast any wxObject 
	// back to wxPMService (see API-spec for more comments)

	SetRefData( (wxObjectRefData*)(wxPMService*)this );
}

wxPMSimpleService::~wxPMSimpleService()
{
	SetRefData( NULL ); // part of the above hack..
}

bool wxPMSimpleService::Start()
{
	return TRUE; // nothing by default
}

bool wxPMSimpleService::Stop() 
{
	return TRUE; // nothing by default
}

wxPMMergableMenuChain* wxPMSimpleService::GetMergableMenuChain()
{
	return NULL; // no menu-entries by default
}

wxObject* wxPMSimpleService::GetWxObject()
{
	return (wxObject*)this;
}

void* wxPMSimpleService::GetInterface( const char* infName, const char* version )
{
	return NULL; // no interfaces implemented by default
}

/***** Implementation for class wxPMBitmapManager *****/

wxPMBitmapManager::wxPMBitmapManager()

	: m_DefaultExt( "bmp" ),
	  m_RelPath( "" ),
      m_WrongBitmap( 16,16 ),
      m_BitmapHash( wxKEY_STRING )
{
	wxSetPMBitmapManager( this );
}

wxPMBitmapManager::~wxPMBitmapManager()
{
	ClearCache();
}

void wxPMBitmapManager::SetDefaultExtension( const wxString& ext )
{
	m_DefaultExt = ext;
}

void wxPMBitmapManager::SetRelativePath( const wxString& relPath )
{
	m_RelPath = relPath;

	if ( m_RelPath != "" ) 
	{
		if ( m_RelPath[ m_RelPath.Length() - 1 ] != wxFILE_SEP_PATH )
		
			m_RelPath += wxFILE_SEP_PATH;
	}
}

const wxString& wxPMBitmapManager::GetRelativePath()
{
	return m_RelPath;
}

const wxString& wxPMBitmapManager::GetDefaultExtension()
{
	return m_DefaultExt;
}

wxBitmap& wxPMBitmapManager::FindBitmapByFileName( const wxString& name )
{
	wxString tmp = m_RelPath;

	m_RelPath = "";

	wxBitmap& found = FindBitmap( name );

	m_RelPath = tmp;

	return found;
}

wxBitmap wxPMBitmapManager::CloneBitmapCopy( const wxBitmap& src )
{
	wxBitmap dest;
	dest.Create( src.GetWidth(), src.GetHeight() );

	wxMemoryDC dc;
	dc.SelectObject( dest );
	dc.DrawBitmap( src, 0, 0 );

	dc.SelectObject( wxNullBitmap );

	return dest;
}

wxBitmap wxPMBitmapManager::FindAndCopyBitmap( const wxString& name )
{
	return CloneBitmapCopy( FindBitmap( name ) );
}

wxBitmap& wxPMBitmapManager::FindBitmap( const wxString& name )
{
	wxString fname = name;

	if ( fname.Find( "." ) == -1 ) fname += "." + m_DefaultExt;

	fname = m_RelPath + fname;

	// is cached?
	wxBitmap* pBmp = (wxBitmap*)m_BitmapHash.Get( fname );

	if ( pBmp == NULL )
	{
		// try loading from file

		if ( wxFileExists( fname ) )
		{
			wxBitmap* pNewBmp = new wxBitmap();

			if ( !pNewBmp->LoadFile( fname, DetectBitmapType( fname ) ) )
			{
				delete pNewBmp;

				m_BitmapHash.Put( fname, &m_WrongBitmap );
				return m_WrongBitmap;
			}


			m_Bitmaps.Add( pNewBmp );
			m_BitmapHash.Put( fname, pNewBmp ); // put to cache

			return *pNewBmp;
		}
		else
		{
			// m_WrongBitmap is displayed for all "failed" loads
			m_BitmapHash.Put( fname, &m_WrongBitmap );

			// NOTE:: for debug purpose only, comment this out otherwise

			//wxFAIL_MSG( "DBG:: Cannot find bimtap: " + fname ); // DBG::

			return m_WrongBitmap;
		}
	}
	else 
		return *pBmp;
}

bool wxPMBitmapManager::BitmapExist( const wxString& name )
{
	return &FindBitmap( name ) == &m_WrongBitmap;
}

int wxPMBitmapManager::DetectBitmapType( const wxString& fileName )
{
	wxString s = fileName.Upper();
	
	if ( s.Find( ".BMP" )  != -1 ) return wxBITMAP_TYPE_BMP;
	if ( s.Find( ".PNG" )  != -1 ) return wxBITMAP_TYPE_PNG;
	if ( s.Find( ".GIF" )  != -1 ) return wxBITMAP_TYPE_GIF;
	if ( s.Find( ".JPG" )  != -1 ) return wxBITMAP_TYPE_JPEG;
	if ( s.Find( ".JPEG" ) != -1 ) return wxBITMAP_TYPE_JPEG;
	if ( s.Find( ".XBM" )  != -1 ) return wxBITMAP_TYPE_XBM;
	if ( s.Find( ".XPM" )  != -1 ) return wxBITMAP_TYPE_XPM;

	return -1;
}

wxBitmap& wxPMBitmapManager::GetWrongBitmap()
{
	return m_WrongBitmap;
}

void wxPMBitmapManager::ClearCache()
{
    size_t i;
	for( i = 0; i != m_Bitmaps.Count(); ++i )

		if ( m_Bitmaps[i] != &m_WrongBitmap )

			delete m_Bitmaps[i];
}

/* global access-point to the global instance of wxPMBitmapManager */

static wxPMBitmapManager* _gBitmapManagerObj = NULL;

void wxSetPMBitmapManager( wxPMBitmapManager* pMgr )
{
	_gBitmapManagerObj = pMgr;
}

wxPMBitmapManager& wxGetPMBitmapManager()
{
	return *_gBitmapManagerObj;
}

wxPMBitmapManager& wxPMBmpMgr()
{
	return *_gBitmapManagerObj;
}

/* global utility functions of PMF */

bool wxPMReadStreamToString( wxInputStream& stm, wxString& toString )
{
	// FIXME:: should avoid using StreamSize(), because not
	//		   all streams support it 

	char buf[1024*32];

	toString = "";

	while( stm.LastError() != wxStream_EOF )
	{
		stm.Read( buf, sizeof(buf) );

		if ( stm.LastError() == wxStream_READ_ERR )

			return FALSE;

		// FIXME:: what about possible '\0's in the stream?

		toString += wxString( buf, 0, stm.LastRead() );
	}

	return TRUE;
}


bool wxPMReadTextStreamToString( wxInputStream& stm, wxString& toString )
{
	wxTextInputStream tstm( stm );

	// FIXME:: should avoid using StreamSize(), because not
	//		   all streams support it 

	toString = "";

	while( stm.LastError() != wxStream_EOF )
	{
		// FIXME:: what about possible '\0's in the stream?

		toString += tstm.ReadLine();

		if ( stm.LastError() != wxStream_EOF )

			toString += '\n';
	}

	return TRUE;
}

// path-manipulation global helpers (FOR NOW:: primitve imps.)

wxString wxPMConcatPaths( const wxString& basePath, const wxString relPath )
{
	if ( relPath.Length() == 0 )
		return basePath;

	if ( basePath.Length() == 0 )
		return relPath;

	if ( relPath[0] == '.' &&
		 ( relPath.Length() == 1 || 
		   ( relPath.Length() == 2 &&
		     relPath[1] == wxFILE_SEP_PATH ) ) )

		return basePath;

	wxString* p1 = (wxString*)(const wxString*)&basePath;
	wxString* p2 = (wxString*)(const wxString*)&relPath;
	wxString b,r;

	if ( basePath[ basePath.Length()-1 ] != wxFILE_SEP_PATH )
	{
		b = basePath + wxFILE_SEP_PATH;
		p1 = &b;
	}

	if ( relPath[0] == '.' )
	{
		r = relPath.Mid( 1 );

		if ( relPath.Length() > 1 &&
			 relPath[1] == wxFILE_SEP_PATH )
		
			 r = relPath.Mid(2);

		p2 = &r;
	}

	return *p1 + *p2;
}


// If either path below has a drive letter (Windows), then both must have a drive letter
wxString wxPMGetRelativePath( const wxString& baseDirPath, const wxString& absFilePath )
{
	bool isAbsolute = wxIsAbsolutePath(absFilePath);
	if (!isAbsolute)
	{
		wxASSERT_MSG(isAbsolute,"Non-absolute path passed in second parameters of wxPMGetRelativePath()");
		return wxEmptyString;
	}

	wxString path,fileName,extension;

	wxString fileStr, baseStr;

	fileStr = absFilePath;
	baseStr = baseDirPath;

	// Force paths to always be stored with unix seperators
	// as those are cross platform supported
	fileStr.Replace("\\\\","/",TRUE);
	fileStr.Replace("\\","/",TRUE);
	baseStr.Replace("\\\\","/",TRUE);
	baseStr.Replace("\\","/",TRUE);

	wxSplitPath(fileStr, &path, &fileName, &extension);

	// We do this, because there is discussion currently on wx-dev as
	// to whether wxSplitPath() should leave the final seperator or
	// not, so this ensures it will still work, regardless of the 
	// final outcome
	if (!wxEndsWithPathSeparator(path))
		path += "/";
	if (!wxEndsWithPathSeparator(baseStr))
		baseStr += "/";

	// Check if the file is in the exact same dir as the 
	// workspace is created in
	if (!path.IsSameAs(baseStr,FALSE))
	{
		bool same = TRUE;
		int i = -1;
		int lastSep = -1;

		// Find the index of the last character in the string that is identical
		// between the two paths
		while (i+1 < (int)path.length() && (wxTolower(baseStr.at(i+1)) == wxTolower(path.at(i+1))))
		{
			i++;
			if (baseStr.at(i) == '/')
				lastSep = i;
		}

		if (i > -1)
		{
			// at least one char of the path matched
			size_t j = wxMin(i,lastSep);

			fileStr.erase(0,j+1);
			path.erase(0,j+1);

			int f = fileStr.Freq('/');
			fileStr = "./";
			while (f > 0)
			{
				fileStr += "../";
				f--;
			}
			fileStr += path;
			fileStr += fileName;
			if (extension.length())
			{
				fileStr += ".";
				fileStr += extension;
			}
		}
		else
		{
			// Nothing in the paths match up, so just return the absolute
			// path, as there is no other choice
			fileStr = absFilePath;
			// Force paths to always be stored with unix seperators
			// as those are cross platform supported
			fileStr.Replace("\\\\","/",TRUE);
			fileStr.Replace("\\","/",TRUE);
		}
	}
	else
	{
		// File is in the same dir as the workspace, so just remove
		// the workspace's directory path from the files path, and 
		// replace it with a "./" for Unix (which also works for MSW)
		fileStr = absFilePath;

		fileStr.replace(0,path.length(),"./");
	}

	return fileStr;
}

wxString wxPMGetAbsolutePath( const wxString& baseDirPath, const wxString& relFilePath )
{
	wxASSERT(baseDirPath);

	if (relFilePath.at(0) != '.' ||
		relFilePath.at(1) != '/')
	{
//		int avoidCompilerWarning = 0;
//		wxASSERT_MSG(avoidCompilerWarning,"Not a relative path passed in second parameter of wxPMGetAbsolutePath()");
		return relFilePath;
	}

	wxString tmpPathStr, tStr;

	tStr = relFilePath;
	tStr.erase(0,2);
	tmpPathStr = baseDirPath;
	if (!wxEndsWithPathSeparator(baseDirPath))
		tmpPathStr += wxFILE_SEP_PATH;
	tmpPathStr += tStr;

	wxChar p[MAX_PATH+1];

	strcpy(p,tmpPathStr.c_str());

	tmpPathStr = wxRealPath(p);

	return tmpPathStr;
}
