/**************************************************************
 * 
 * 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 "sbcomp.hxx"
#include <stdio.h>
#include <string.h>
#include <ctype.h>

SV_IMPL_PTRARR(SbiStrings,String*)
SV_IMPL_PTRARR(SbiSymbols,SbiSymDef*)

// Alle Symbolnamen werden im Stringpool des Symbol-Pools abgelegt, damit
// alle Symbole im gleichen Case verarbeitet werden. Beim Speichern des
// Code-Images wird der globale Stringpool mit den entsprechenden Sympools
// gespeichert. Der lokale Stringpool nimmt alle Symbole auf, die nicht
// ins Image wandern (Labels, Konstantennamen etc).

/***************************************************************************
|*
|*	SbiStringPool
|*
***************************************************************************/

SbiStringPool::SbiStringPool( SbiParser* p )
{
	pParser = p;
}

SbiStringPool::~SbiStringPool()
{}

// Suchen

const String& SbiStringPool::Find( sal_uInt16 n ) const
{
	if( !n || n > aData.Count() )
		return aEmpty;
	else
		return *aData.GetObject( n-1 );
}

// Hinzufuegen eines Strings. Der String wird Case-Insensitiv
// verglichen.

short SbiStringPool::Add( const String& rVal, sal_Bool bNoCase )
{
	sal_uInt16 n = aData.Count();
	for( sal_uInt16 i = 0; i < n; i++ )
	{
		String* p = aData.GetObject( i );
		if( (  bNoCase && p->Equals( rVal ) )
		 || ( !bNoCase && p->EqualsIgnoreCaseAscii( rVal ) ) )
			return i+1;
	}
	const String* pNew = new String( rVal );
	aData.Insert( pNew, n++ );
	return (short) n;
}

short SbiStringPool::Add( double n, SbxDataType t )
{
	char buf[ 40 ];
	switch( t )
	{
        case SbxINTEGER: snprintf( buf, sizeof(buf), "%d", (short) n ); break;
        case SbxLONG:    snprintf( buf, sizeof(buf), "%ld", (long) n ); break;
        case SbxSINGLE:  snprintf( buf, sizeof(buf), "%.6g", (float) n ); break;
        case SbxDOUBLE:  snprintf( buf, sizeof(buf), "%.16g", n ); break;
		default: break;
	}
	return Add( String::CreateFromAscii( buf ) );
}

/***************************************************************************
|*
|*	SbiSymPool
|*
***************************************************************************/

SbiSymPool::SbiSymPool( SbiStringPool& r, SbiSymScope s ) : rStrings( r )
{
	pParser	 = r.GetParser();
	eScope   = s;
	pParent  = NULL;
	nCur	 =
	nProcId  = 0;
}

SbiSymPool::~SbiSymPool()
{}

// Inhalt loeschen

void SbiSymPool::Clear()
{
	aData.DeleteAndDestroy( 0, aData.Count() );
}

SbiSymDef* SbiSymPool::First()
{
	nCur = (sal_uInt16) -1;
	return Next();
}

SbiSymDef* SbiSymPool::Next()
{
	if( ++nCur >= aData.Count() )
		return NULL;
	else
		return aData.GetObject( nCur );
}

// Hinzufuegen eines Symbols

SbiSymDef* SbiSymPool::AddSym( const String& rName )
{
	SbiSymDef* p = new SbiSymDef( rName );
	p->nPos    = aData.Count();
	p->nId	   = rStrings.Add( rName );
	p->nProcId = nProcId;
	p->pIn	   = this;
	const SbiSymDef* q = p;
	aData.Insert( q, q->nPos );
	return p;
}

SbiProcDef* SbiSymPool::AddProc( const String& rName )
{
	SbiProcDef* p = new SbiProcDef( pParser, rName );
	p->nPos    = aData.Count();
	p->nId	   = rStrings.Add( rName );
	// Procs sind immer global
	p->nProcId = 0;
	p->pIn	   = this;
	const SbiSymDef* q = p;
	aData.Insert( q, q->nPos );
	return p;
}

// Hinzufuegen einer extern aufgebauten Symboldefinition

void SbiSymPool::Add( SbiSymDef* pDef )
{
	if( pDef && pDef->pIn != this )
	{
		if( pDef->pIn )
		{
#ifdef DBG_UTIL
			// schon in einem anderen Pool drin!
			pParser->Error( SbERR_INTERNAL_ERROR, "Dbl Pool" );
#endif
			return;
		}

		pDef->nPos = aData.Count();
		if( !pDef->nId )
		{
			// Bei statischen Variablen muss ein eindeutiger Name
			// im Stringpool erzeugt werden (Form ProcName:VarName)
			String aName( pDef->aName );
			if( pDef->IsStatic() )
			{
				aName = pParser->aGblStrings.Find( nProcId );
				aName += ':';
				aName += pDef->aName;
			}
			pDef->nId = rStrings.Add( aName );
		}
		// Procs sind immer global
		if( !pDef->GetProcDef() )
			pDef->nProcId = nProcId;
		pDef->pIn = this;
		const SbiSymDef* q = pDef;
		aData.Insert( q, q->nPos );
	}
}

// Suchen eines Eintrags ueber den Namen. Es wird auch im Parent gesucht.

SbiSymDef* SbiSymPool::Find( const String& rName ) const
{
	sal_uInt16 nCount = aData.Count();
	for( sal_uInt16 i = 0; i < nCount; i++ )
	{
		SbiSymDef* p = aData.GetObject( nCount - i - 1 );
		if( ( !p->nProcId || ( p->nProcId == nProcId ) )
		 && ( p->aName.EqualsIgnoreCaseAscii( rName ) ) )
			return p;
	}
	if( pParent )
		return pParent->Find( rName );
	else
		return NULL;
}

// Suchen ueber ID-Nummer

SbiSymDef* SbiSymPool::FindId( sal_uInt16 n ) const
{
	for( sal_uInt16 i = 0; i < aData.Count(); i++ )
	{
		SbiSymDef* p = aData.GetObject( i );
		if( p->nId == n && ( !p->nProcId || ( p->nProcId == nProcId ) ) )
			return p;
	}
	if( pParent )
		return pParent->FindId( n );
	else
		return NULL;
}

// Suchen ueber Position (ab 0)

SbiSymDef* SbiSymPool::Get( sal_uInt16 n ) const
{
	if( n >= aData.Count() )
		return NULL;
	else
		return aData.GetObject( n );
}

sal_uInt32 SbiSymPool::Define( const String& rName )
{
	SbiSymDef* p = Find( rName );
	if( p )
	{	if( p->IsDefined() )
			pParser->Error( SbERR_LABEL_DEFINED, rName );
	}
	else
		p = AddSym( rName );
	return p->Define();
}

sal_uInt32 SbiSymPool::Reference( const String& rName )
{
	SbiSymDef* p = Find( rName );
	if( !p )
		p = AddSym( rName );
	//Sicherheitshalber
	pParser->aGen.GenStmnt();
	return p->Reference();
}

// Alle offenen Referenzen anmaulen

void SbiSymPool::CheckRefs()
{
	for( sal_uInt16 i = 0; i < aData.Count(); i++ )
	{
		SbiSymDef* p = aData.GetObject( i );
		if( !p->IsDefined() )
			pParser->Error( SbERR_UNDEF_LABEL, p->GetName() );
	}
}

/***************************************************************************
|*
|*	Symbol-Definitionen
|*
***************************************************************************/

SbiSymDef::SbiSymDef( const String& rName ) : aName( rName )
{
	eType	 = SbxEMPTY;
	nDims	 = 0;
	nTypeId  = 0;
	nProcId  = 0;
	nId 	 = 0;
	nPos	 = 0;
	nLen	 = 0;
	nChain	 = 0;
	bAs		 =
	bNew	 =
	bStatic	 =
	bOpt	 =
	bParamArray =
	bWithEvents =
	bWithBrackets =
	bByVal	 =
	bChained =
    bGlobal  = sal_False;
	pIn		 =
	pPool	 = NULL;
	nDefaultId = 0;
	nFixedStringLength = -1;
}

SbiSymDef::~SbiSymDef()
{
	delete pPool;
}

SbiProcDef* SbiSymDef::GetProcDef()
{
	return NULL;
}

SbiConstDef* SbiSymDef::GetConstDef()
{
	return NULL;
}

// Wenn der Name benoetigt wird, den aktuellen Namen
// aus dem Stringpool nehmen

const String& SbiSymDef::GetName()
{
	if( pIn )
		aName = pIn->rStrings.Find( nId );
	return aName;
}

// Eintragen eines Datentyps

void SbiSymDef::SetType( SbxDataType t )
{
	if( t == SbxVARIANT && pIn )
	{
		sal_Unicode cu = aName.GetBuffer()[0];
		if( cu < 256 )
		{
			char ch = (char)aName.GetBuffer()[0];
			if( ch == '_' ) ch = 'Z';
			int ch2 = toupper( ch );
			unsigned char c = (unsigned char)ch2;
			if( c > 0 && c < 128 )
				t = pIn->pParser->eDefTypes[ ch2 - 'A' ];
		}
	}
	eType = t;
}

// Aufbau einer Backchain, falls noch nicht definiert
// Es wird der Wert zurueckgeliefert, der als Operand gespeichert
// werden soll.

sal_uInt32 SbiSymDef::Reference()
{
	if( !bChained )
	{
		sal_uInt32 n = nChain;
		nChain = pIn->pParser->aGen.GetOffset();
		return n;
	}
	else return nChain;
}

// Definition eines Symbols.
// Hier wird der Backchain aufgeloest, falls vorhanden

sal_uInt32 SbiSymDef::Define()
{
	sal_uInt32 n = pIn->pParser->aGen.GetPC();
	pIn->pParser->aGen.GenStmnt();
	if( nChain ) pIn->pParser->aGen.BackChain( nChain );
	nChain = n;
	bChained = sal_True;
	return nChain;
}

// Eine Symboldefinition kann einen eigenen Pool haben. Dies ist
// der Fall bei Objekten und Prozeduren (lokale Variable)

SbiSymPool& SbiSymDef::GetPool()
{
	if( !pPool )
		pPool = new SbiSymPool( pIn->pParser->aGblStrings, SbLOCAL );	// wird gedumpt
	return *pPool;
}

SbiSymScope SbiSymDef::GetScope() const
{
	return pIn ? pIn->GetScope() : SbLOCAL;
}

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

// Die Prozedur-Definition hat drei Pools:
// 1) aParams: wird durch die Definition gefuellt. Enthaelt die Namen
//	  der Parameter, wie sie innerhalb des Rumpfes verwendet werden.
//	  Das erste Element ist der Returnwert.
// 2) pPool: saemtliche lokale Variable
// 3) aLabels: Labels

SbiProcDef::SbiProcDef( SbiParser* pParser, const String& rName,
					    sal_Bool bProcDecl )
		 : SbiSymDef( rName )
		 , aParams( pParser->aGblStrings, SbPARAM )  // wird gedumpt
		 , aLabels( pParser->aLclStrings, SbLOCAL )	 // wird nicht gedumpt
		 , mbProcDecl( bProcDecl )
{
	aParams.SetParent( &pParser->aPublics );
	pPool = new SbiSymPool( pParser->aGblStrings, SbLOCAL ); // Locals
	pPool->SetParent( &aParams );
	nLine1	=
	nLine2	= 0;
	mePropMode = PROPERTY_MODE_NONE;
	bPublic = sal_True;
	bCdecl	= sal_False;
	bStatic = sal_False;
	// Fuer Returnwerte ist das erste Element der Parameterliste
	// immer mit dem Namen und dem Typ der Proc definiert
	aParams.AddSym( aName );
}

SbiProcDef::~SbiProcDef()
{}

SbiProcDef* SbiProcDef::GetProcDef()
{
	return this;
}

void SbiProcDef::SetType( SbxDataType t )
{
	SbiSymDef::SetType( t );
	aParams.Get( 0 )->SetType( eType );
}

// Match mit einer Forward-Deklaration
// Falls der Match OK ist, wird pOld durch this im Pool ersetzt
// pOld wird immer geloescht!

void SbiProcDef::Match( SbiProcDef* pOld )
{
	SbiSymDef* po, *pn=NULL;
	// Parameter 0 ist der Funktionsname
	sal_uInt16 i;
	for( i = 1; i < aParams.GetSize(); i++ )
	{
		po = pOld->aParams.Get( i );
		pn = aParams.Get( i );
		// Kein Typabgleich; das wird beim Laufen erledigt
		// aber ist sie evtl. mit zu wenigen Parametern aufgerufen
		// worden?
		if( !po && !pn->IsOptional() && !pn->IsParamArray() )
			break;
		po = pOld->aParams.Next();
	}
	// Wurden zu viele Parameter angegeben?
	if( pn && i < aParams.GetSize() && pOld->pIn )
	{
		// Die ganze Zeile markieren
		pOld->pIn->GetParser()->SetCol1( 0 );
		pOld->pIn->GetParser()->Error( SbERR_BAD_DECLARATION, aName );
	}
	if( !pIn && pOld->pIn )
	{
		// Alten Eintrag durch neuen ersetzen
		SbiSymDef** pData = (SbiSymDef**) pOld->pIn->aData.GetData();
		pData[ pOld->nPos ] = this;
		nPos = pOld->nPos;
		nId  = pOld->nId;
		pIn  = pOld->pIn;
	}
	delete pOld;
}

void SbiProcDef::setPropertyMode( PropertyMode ePropMode )
{ 
	mePropMode = ePropMode;
	if( mePropMode != PROPERTY_MODE_NONE )
	{
		// Prop name = original scanned procedure name
		maPropName = aName;

		// CompleteProcName includes "Property xxx " 
		// to avoid conflicts with other symbols
		String aCompleteProcName;
		aCompleteProcName.AppendAscii( "Property " );
		switch( mePropMode )
		{
			case PROPERTY_MODE_GET:		aCompleteProcName.AppendAscii( "Get " ); break;
			case PROPERTY_MODE_LET:		aCompleteProcName.AppendAscii( "Let " ); break;
			case PROPERTY_MODE_SET:		aCompleteProcName.AppendAscii( "Set " ); break;
			case PROPERTY_MODE_NONE:	
				DBG_ERROR( "Illegal PropertyMode PROPERTY_MODE_NONE" );
				break;
		}
		aCompleteProcName += aName;
		aName = aCompleteProcName;
	}
}


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

SbiConstDef::SbiConstDef( const String& rName )
		   : SbiSymDef( rName )
{
	nVal = 0; eType = SbxINTEGER;
}

void SbiConstDef::Set( double n, SbxDataType t )
{
	aVal.Erase(); nVal = n; eType = t;
}

void SbiConstDef::Set( const String& n )
{
	aVal = n; nVal = 0; eType = SbxSTRING;
}

SbiConstDef::~SbiConstDef()
{}

SbiConstDef* SbiConstDef::GetConstDef()
{
	return this;
}

