/**************************************************************
 * 
 * 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_basic.hxx"


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

#include <basic/sbx.hxx>
#include <basic/sbxfac.hxx>
#include <basic/sbxbase.hxx>

// AppData-Struktur fuer SBX:

SV_IMPL_PTRARR(SbxParams,SbxParamInfo*);
SV_IMPL_PTRARR(SbxFacs,SbxFactory*);

TYPEINIT0(SbxBase)

// SBX-Daten anfordern oder ggf. anlegen
// wir legen den Bereich einfach an und verzichten auf die Freigabe!

SbxAppData* GetSbxData_Impl()
{
	SbxAppData** ppData = (SbxAppData**) ::GetAppData( SHL_SBX );
	SbxAppData* p = *ppData;
	if( !p )
		p = *ppData  = new SbxAppData;
	return p;
}

SbxAppData::~SbxAppData()
{
	if( pBasicFormater )
		delete pBasicFormater;
}


//////////////////////////////// SbxBase /////////////////////////////////

DBG_NAME(SbxBase);

SbxBase::SbxBase()
{
	DBG_CTOR( SbxBase, 0 );
	nFlags  = SBX_READWRITE;
}

SbxBase::SbxBase( const SbxBase& r )
    : SvRefBase( r )
{
	DBG_CTOR( SbxBase, 0 );
	nFlags  = r.nFlags;
}

SbxBase::~SbxBase()
{
	DBG_DTOR(SbxBase,0);
}

SbxBase& SbxBase::operator=( const SbxBase& r )
{
	DBG_CHKTHIS( SbxBase, 0 );
	nFlags = r.nFlags;
	return *this;
}

SbxDataType SbxBase::GetType() const
{
	DBG_CHKTHIS( SbxBase, 0 );
	return SbxEMPTY;
}

SbxClassType SbxBase::GetClass() const
{
	DBG_CHKTHIS( SbxBase, 0 );
	return SbxCLASS_DONTCARE;
}

void SbxBase::Clear()
{
	DBG_CHKTHIS( SbxBase, 0 );
}

sal_Bool SbxBase::IsFixed() const
{
	DBG_CHKTHIS( SbxBase, 0 );
	return IsSet( SBX_FIXED );
}

void SbxBase::SetModified( sal_Bool b )
{
	DBG_CHKTHIS( SbxBase, 0 );
	if( IsSet( SBX_NO_MODIFY ) )
		return;
	if( b )
		SetFlag( SBX_MODIFIED );
	else
		ResetFlag( SBX_MODIFIED );
}

SbxError SbxBase::GetError()
{
	return GetSbxData_Impl()->eSbxError;
}

void SbxBase::SetError( SbxError e )
{
	SbxAppData* p = GetSbxData_Impl();
	if( e && p->eSbxError == SbxERR_OK )
		p->eSbxError = e;
}

sal_Bool SbxBase::IsError()
{
	return sal_Bool( GetSbxData_Impl()->eSbxError != SbxERR_OK );
}

void SbxBase::ResetError()
{
	GetSbxData_Impl()->eSbxError = SbxERR_OK;
}

void SbxBase::AddFactory( SbxFactory* pFac )
{
	SbxAppData* p = GetSbxData_Impl();
	const SbxFactory* pTemp = pFac;

	// AB, 6.3.96: HandleLast-Flag beruecksichtigen
	sal_uInt16 nPos = p->aFacs.Count();		// Einfuege-Position
	if( !pFac->IsHandleLast() )			// Nur, wenn nicht selbst HandleLast
	{
		// Neue Factory vor Factories mit HandleLast einordnen
		while( nPos > 0 &&
				(static_cast<SbxFactory*>(p->aFacs.GetObject( nPos-1 )))->IsHandleLast() )
			nPos--;
	}
	p->aFacs.Insert( pTemp, nPos );
}

void SbxBase::RemoveFactory( SbxFactory* pFac )
{
	SbxAppData* p = GetSbxData_Impl();
	for( sal_uInt16 i = 0; i < p->aFacs.Count(); i++ )
	{
		if( p->aFacs.GetObject( i ) == pFac )
		{
			p->aFacs.Remove( i, 1 ); break;
		}
	}
}


SbxBase* SbxBase::Create( sal_uInt16 nSbxId, sal_uInt32 nCreator )
{
    // #91626: Hack to skip old Basic dialogs
    // Problem: There does not exist a factory any more,
    // so we have to create a dummy SbxVariable instead
	if( nSbxId == 0x65 )    // Dialog Id
        return new SbxVariable;

	XubString aEmptyStr;
	if( nCreator == SBXCR_SBX )
	  switch( nSbxId )
	{
		case SBXID_VALUE:		return new SbxValue;
		case SBXID_VARIABLE:	return new SbxVariable;
		case SBXID_ARRAY:  		return new SbxArray;
		case SBXID_DIMARRAY:	return new SbxDimArray;
		case SBXID_OBJECT:		return new SbxObject( aEmptyStr );
		case SBXID_COLLECTION:	return new SbxCollection( aEmptyStr );
		case SBXID_FIXCOLLECTION:
								return new SbxStdCollection( aEmptyStr, aEmptyStr );
		case SBXID_METHOD:		return new SbxMethod( aEmptyStr, SbxEMPTY );
		case SBXID_PROPERTY:	return new SbxProperty( aEmptyStr, SbxEMPTY );
	}
	// Unbekanter Typ: ber die Factories gehen!
	SbxAppData* p = GetSbxData_Impl();
	SbxBase* pNew = NULL;
	for( sal_uInt16 i = 0; i < p->aFacs.Count(); i++ )
	{
		SbxFactory* pFac = p->aFacs.GetObject( i );
		pNew = pFac->Create( nSbxId, nCreator );
		if( pNew )
			break;
	}
#ifdef DBG_UTIL
	if( !pNew )
	{
		ByteString aMsg( "SBX: Keine Factory fuer SBX-ID " );
		aMsg += ByteString::CreateFromInt32(nSbxId);
		DbgError( aMsg.GetBuffer() );
	}
#endif
	return pNew;
}

SbxObject* SbxBase::CreateObject( const XubString& rClass )
{
	SbxAppData* p = GetSbxData_Impl();
	SbxObject* pNew = NULL;
	for( sal_uInt16 i = 0; i < p->aFacs.Count(); i++ )
	{
		pNew = p->aFacs.GetObject( i )->CreateObject( rClass );
		if( pNew )
			break;
	}
#ifdef DBG_UTIL
	if( !pNew )
	{
		ByteString aMsg( "SBX: Keine Factory fuer Objektklasse " );
		ByteString aClassStr( (const UniString&)rClass, RTL_TEXTENCODING_ASCII_US );
		aMsg += aClassStr;
		DbgError( (const char*)aMsg.GetBuffer() );
	}
#endif
	return pNew;
}

static sal_Bool bStaticEnableBroadcasting = sal_True;

// Sbx-Loesung als Ersatz fuer SfxBroadcaster::Enable()
void SbxBase::StaticEnableBroadcasting( sal_Bool bEnable )
{
	bStaticEnableBroadcasting = bEnable;
}

sal_Bool SbxBase::StaticIsEnabledBroadcasting( void )
{
	return bStaticEnableBroadcasting;
}


SbxBase* SbxBase::Load( SvStream& rStrm )
{
	sal_uInt16 nSbxId, nFlags, nVer;
	sal_uInt32 nCreator, nSize;
	rStrm >> nCreator >> nSbxId >> nFlags >> nVer;

	// Eine Dummheit meinerseits korrigieren:
	if( nFlags & SBX_RESERVED )
		nFlags = ( nFlags & ~SBX_RESERVED ) | SBX_GBLSEARCH;

	sal_uIntPtr nOldPos = rStrm.Tell();
	rStrm >> nSize;
	SbxBase* p = Create( nSbxId, nCreator );
	if( p )
	{
		p->nFlags = nFlags;
		if( p->LoadData( rStrm, nVer ) )
		{
			sal_uIntPtr nNewPos = rStrm.Tell();
			nOldPos += nSize;
			DBG_ASSERT( nOldPos >= nNewPos, "SBX: Zu viele Daten eingelesen" );
			if( nOldPos != nNewPos )
				rStrm.Seek( nOldPos );
			if( !p->LoadCompleted() )
			{
				// Loeschen des Objekts
				SbxBaseRef aRef( p );
				p = NULL;
			}
		}
		else
		{
			rStrm.SetError( SVSTREAM_FILEFORMAT_ERROR );
			// Loeschen des Objekts
			SbxBaseRef aRef( p );
			p = NULL;
		}
	}
	else
		rStrm.SetError( SVSTREAM_FILEFORMAT_ERROR );
	return p;
}

// Sbx-Objekt im Stream ueberspringen
void SbxBase::Skip( SvStream& rStrm )
{
	sal_uInt16 nSbxId, nFlags, nVer;
	sal_uInt32 nCreator, nSize;
	rStrm >> nCreator >> nSbxId >> nFlags >> nVer;

	sal_uIntPtr nStartPos = rStrm.Tell();
	rStrm >> nSize;

	rStrm.Seek( nStartPos + nSize );
}

sal_Bool SbxBase::Store( SvStream& rStrm )
{
	DBG_CHKTHIS( SbxBase, 0 );
	if( !( nFlags & SBX_DONTSTORE ) )
	{
		rStrm << (sal_uInt32) GetCreator()
			  << (sal_uInt16) GetSbxId()
			  << (sal_uInt16) GetFlags()
			  << (sal_uInt16) GetVersion();
		sal_uIntPtr nOldPos = rStrm.Tell();
		rStrm << (sal_uInt32) 0L;
		sal_Bool bRes = StoreData( rStrm );
		sal_uIntPtr nNewPos = rStrm.Tell();
		rStrm.Seek( nOldPos );
		rStrm << (sal_uInt32) ( nNewPos - nOldPos );
		rStrm.Seek( nNewPos );
		if( rStrm.GetError() != SVSTREAM_OK )
			bRes = sal_False;
		if( bRes )
			bRes = StoreCompleted();
		return bRes;
	}
	else
		return sal_True;
}

sal_Bool SbxBase::LoadData( SvStream&, sal_uInt16 )
{
	DBG_CHKTHIS( SbxBase, 0 );
	return sal_False;
}

sal_Bool SbxBase::StoreData( SvStream& ) const
{
	DBG_CHKTHIS( SbxBase, 0 );
	return sal_False;
}

sal_Bool SbxBase::LoadPrivateData( SvStream&, sal_uInt16 )
{
	DBG_CHKTHIS( SbxBase, 0 );
	return sal_True;
}

sal_Bool SbxBase::StorePrivateData( SvStream& ) const
{
	DBG_CHKTHIS( SbxBase, 0 );
	return sal_True;
}

sal_Bool SbxBase::LoadCompleted()
{
	DBG_CHKTHIS( SbxBase, 0 );
	return sal_True;
}

sal_Bool SbxBase::StoreCompleted()
{
	DBG_CHKTHIS( SbxBase, 0 );
	return sal_True;
}

//////////////////////////////// SbxFactory ////////////////////////////////

SbxBase* SbxFactory::Create( sal_uInt16, sal_uInt32 )
{
	return NULL;
}

SbxObject* SbxFactory::CreateObject( const XubString& )
{
	return NULL;
}

///////////////////////////////// SbxInfo //////////////////////////////////

SbxInfo::~SbxInfo()
{}

void SbxInfo::AddParam
		( const XubString& rName, SbxDataType eType, sal_uInt16 nFlags )
{
	const SbxParamInfo* p = new SbxParamInfo( rName, eType, nFlags );
	aParams.Insert( p, aParams.Count() );
}

void SbxInfo::AddParam( const SbxParamInfo& r )
{
	const SbxParamInfo* p = new SbxParamInfo
		( r.aName, r.eType, r.nFlags, r.aTypeRef );
	aParams.Insert( p, aParams.Count() );
}

const SbxParamInfo* SbxInfo::GetParam( sal_uInt16 n ) const
{
	if( n < 1 || n > aParams.Count() )
		return NULL;
	else
		return aParams.GetObject( n-1 );
}

sal_Bool SbxInfo::LoadData( SvStream& rStrm, sal_uInt16 nVer )
{
	aParams.Remove( 0, aParams.Count() );
	sal_uInt16 nParam;
	rStrm.ReadByteString( aComment, RTL_TEXTENCODING_ASCII_US );
	rStrm.ReadByteString( aHelpFile, RTL_TEXTENCODING_ASCII_US );
	rStrm >> nHelpId >> nParam;
	while( nParam-- )
	{
		XubString aName;
		sal_uInt16 nType, nFlags;
		sal_uInt32 nUserData = 0;
		rStrm.ReadByteString( aName, RTL_TEXTENCODING_ASCII_US );
		rStrm >> nType >> nFlags;
		if( nVer > 1 )
			rStrm >> nUserData;
		AddParam( aName, (SbxDataType) nType, nFlags );
		SbxParamInfo* p = aParams.GetObject( aParams.Count() - 1 );
		p->nUserData = nUserData;
	}
	return sal_True;
}

sal_Bool SbxInfo::StoreData( SvStream& rStrm ) const
{
	rStrm.WriteByteString( aComment, RTL_TEXTENCODING_ASCII_US );
	rStrm.WriteByteString( aHelpFile, RTL_TEXTENCODING_ASCII_US );
	rStrm << nHelpId << aParams.Count();
	for( sal_uInt16 i = 0; i < aParams.Count(); i++ )
	{
		SbxParamInfo* p = aParams.GetObject( i );
		rStrm.WriteByteString( p->aName, RTL_TEXTENCODING_ASCII_US );
		rStrm << (sal_uInt16) p->eType
			  << (sal_uInt16) p->nFlags
			  << (sal_uInt32) p->nUserData;
	}
	return sal_True;
}

