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

#define WORKAROUND_98119

#ifdef WORKAROUND_98119
#include "bufferedinputstream.hxx"
#endif

#include <string.h>
#ifndef _VOS_DIAGNOSE_HXX_
#include <vos/diagnose.hxx>
#endif
#include <osl/thread.h>
#include <rtl/memory.h>
#include <osl/file.hxx>
#include <cppuhelper/weak.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <comphelper/processfactory.hxx>
#include <rtl/uri.hxx>
#include <rtl/ustrbuf.hxx>
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#include "db.hxx"
#include <com/sun/star/io/XActiveDataSink.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
#include <com/sun/star/ucb/OpenMode.hpp>
#include <com/sun/star/ucb/XCommandProcessor.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/XContentIdentifier.hpp>
#include <com/sun/star/ucb/XContentProvider.hpp>
#include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>

#include "urlparameter.hxx"
#include "databases.hxx"

namespace chelp {

	inline bool ascii_isDigit( sal_Unicode ch )
	{
		return ((ch >= 0x0030) && (ch <= 0x0039));
	}
	
	inline bool ascii_isLetter( sal_Unicode ch )
	{
		return ( ( (ch >= 0x0041) && (ch <= 0x005A) ) || 
				 ( (ch >= 0x0061) && (ch <= 0x007A) ) );
	}
	
	inline bool isLetterOrDigit( sal_Unicode ch )
	{
		return ascii_isLetter( ch ) || ascii_isDigit( ch );
	}

}

using namespace cppu;
using namespace com::sun::star::io;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::ucb;
using namespace com::sun::star::beans;
using namespace com::sun::star::container;
using namespace berkeleydbproxy;
using namespace chelp;


URLParameter::URLParameter( const rtl::OUString& aURL,
							Databases* pDatabases )
	throw( com::sun::star::ucb::IllegalIdentifierException )
	: m_pDatabases( pDatabases ),
      m_aURL( aURL )
{
	init( false );
	parse();
}


bool URLParameter::isErrorDocument()
{
	bool bErrorDoc = false;

	if( isFile() )
	{
		Reference< XHierarchicalNameAccess > xNA =
			m_pDatabases->findJarFileForPath( get_jar(), get_language(), get_path() );
		bErrorDoc = !xNA.is();
	}

	return bErrorDoc;
}


rtl::OString URLParameter::getByName( const char* par )
{
	rtl::OUString val;
	
	if( strcmp( par,"Program" ) == 0 )
		val = get_program();
	else if( strcmp( par,"Database" ) == 0 )
		val = get_module();
	else if( strcmp( par,"DatabasePar" ) == 0 )
		val = get_dbpar();
	else if( strcmp( par,"Id" ) == 0 )
		val = get_id();
	else if( strcmp( par,"Path" ) == 0 )
		val = get_path();
	else if( strcmp( par,"Language" ) == 0 )
		val = get_language();
	else if( strcmp( par,"System" ) == 0 )
		val = get_system();
	else if( strcmp( par,"HelpPrefix" ) == 0 )
		val = get_prefix();
	
	return rtl::OString( val.getStr(),val.getLength(),RTL_TEXTENCODING_UTF8 );
}


rtl::OUString URLParameter::get_id()
{
	if( m_aId.compareToAscii("start") == 0 )
	{   // module is set
		StaticModuleInformation* inf =
			m_pDatabases->getStaticInformationForModule( get_module(),
														 get_language() );
		if( inf )
			m_aId = inf->get_id();
		
		m_bStart = true;
	}
	
	return m_aId;
}
		
rtl::OUString URLParameter::get_tag()
{
	if( isFile() )
		return get_the_tag();
	else
		return m_aTag;
}


rtl::OUString URLParameter::get_title()
{
	if( isFile() )
		return get_the_title();
	else if( m_aModule.compareToAscii("") != 0 )
	{
		StaticModuleInformation* inf =
			m_pDatabases->getStaticInformationForModule( get_module(),
														 get_language() );
		if( inf )
			m_aTitle = inf->get_title();
	}
	else   // This must be the root
		m_aTitle = rtl::OUString::createFromAscii("root");
	
	return m_aTitle;
}
		

rtl::OUString URLParameter::get_language()
{
	if( m_aLanguage.getLength() == 0 )
		return m_aDefaultLanguage;
	
	return m_aLanguage;
}


rtl::OUString URLParameter::get_program()
{
	if( ! m_aProgram.getLength() )
	{
		StaticModuleInformation* inf =
			m_pDatabases->getStaticInformationForModule( get_module(),
														 get_language() );
		if( inf )
			m_aProgram = inf->get_program();
	}
	return m_aProgram;
}
			

void URLParameter::init( bool bDefaultLanguageIsInitialized )
{
	(void)bDefaultLanguageIsInitialized;

	m_bBerkeleyRead = false;
	m_bStart = false;
    m_bUseDB = true;
	m_nHitCount = 100;                // The default maximum hitcount	
}


rtl::OUString URLParameter::get_the_tag()
{
    if(m_bUseDB) {
        if( ! m_bBerkeleyRead )
            readBerkeley();
		
        m_bBerkeleyRead = true;
		
        return m_aTag;
    }
    else
        return rtl::OUString();
}



rtl::OUString URLParameter::get_the_path()
{
    if(m_bUseDB) {
        if( ! m_bBerkeleyRead )
            readBerkeley();
        m_bBerkeleyRead = true;
        
        return m_aPath;
    }
    else
        return get_id();
}

	

rtl::OUString URLParameter::get_the_title()
{
    if(m_bUseDB) {
        if( ! m_bBerkeleyRead )
            readBerkeley();
        m_bBerkeleyRead = true;
        
        return m_aTitle;
    }
    else
        return rtl::OUString();
}


rtl::OUString URLParameter::get_the_jar()
{
    if(m_bUseDB) {
        if( ! m_bBerkeleyRead )
            readBerkeley();
        m_bBerkeleyRead = true;
        
        return m_aJar;
    }
    else
        return get_module() + rtl::OUString::createFromAscii(".jar");
}




void URLParameter::readBerkeley()
{
	static rtl::OUString aQuestionMark( rtl::OUString::createFromAscii( "?" ) );

	if( get_id().compareToAscii("") == 0 )
		return;

	rtl::OUString aModule = get_module();
	rtl::OUString aLanguage = get_language();

	DataBaseIterator aDbIt( *m_pDatabases, aModule, aLanguage, false );
	bool bSuccess = false;

	int nSize = 0;
	const sal_Char* pData = NULL;

	Dbt data;
	DBData aDBData;
	rtl::OUString aExtensionPath;
    rtl::OUString aExtensionRegistryPath;
	while( true )
	{
		Db* db = aDbIt.nextDb( &aExtensionPath, &aExtensionRegistryPath );
		if( !db )
			break;

		rtl::OString keyStr( m_aId.getStr(),m_aId.getLength(),RTL_TEXTENCODING_UTF8 );
		
		DBHelp* pDBHelp = db->getDBHelp();
		if( pDBHelp != NULL )
		{
			bSuccess = pDBHelp->getValueForKey( keyStr, aDBData );
			if( bSuccess )
			{
				nSize = aDBData.getSize();
				pData = aDBData.getData();
				break;
			}
		}
		else
		{
			Dbt key( static_cast< void* >( const_cast< sal_Char* >( keyStr.getStr() ) ),
					 keyStr.getLength() );
			int err = db->get( 0,&key,&data,0 );
			if( err == 0 )
			{
				bSuccess = true;
				nSize = data.get_size();
				pData = static_cast<sal_Char*>( data.get_data() );
				break;
			}
		}
	}

	if( bSuccess )
	{
		DbtToStringConverter converter( pData, nSize );
		m_aTitle = converter.getTitle();
		m_pDatabases->replaceName( m_aTitle );
		m_aPath  = converter.getFile();
		m_aJar   = converter.getDatabase();
		if( aExtensionPath.getLength() > 0 )
		{
			rtl::OUStringBuffer aExtendedJarStrBuf;
			aExtendedJarStrBuf.append( aQuestionMark );
			aExtendedJarStrBuf.append( aExtensionPath );
			aExtendedJarStrBuf.append( aQuestionMark );
			aExtendedJarStrBuf.append( m_aJar );
			m_aJar = aExtendedJarStrBuf.makeStringAndClear();
            m_aExtensionRegistryPath = aExtensionRegistryPath;
		}
		m_aTag   = converter.getHash();
	}
}



// Class encapsulating the transformation of the XInputStream to XHTML


class InputStreamTransformer
	: public OWeakObject,
	  public XInputStream,
	  public XSeekable
{
public:
	
	InputStreamTransformer( URLParameter* urlParam,
							Databases*    pDatatabases,
							bool isRoot = false );
	
	~InputStreamTransformer();
	
	virtual Any SAL_CALL queryInterface( const Type& rType ) throw( RuntimeException );
	virtual void SAL_CALL acquire( void ) throw();
	virtual void SAL_CALL release( void ) throw();

	virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead ) 
		throw( NotConnectedException,
			   BufferSizeExceededException,
			   IOException,
			   RuntimeException);
	
	virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead )
		throw( NotConnectedException,
			   BufferSizeExceededException,
			   IOException,
			   RuntimeException);
	
	virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) throw( NotConnectedException,
																	 BufferSizeExceededException,
																	 IOException,
																	 RuntimeException );
	
	virtual sal_Int32 SAL_CALL available( void ) throw( NotConnectedException,
														IOException,
														RuntimeException );
	
	virtual void SAL_CALL closeInput( void ) throw( NotConnectedException,
													IOException,
													RuntimeException );
	
	virtual void SAL_CALL seek( sal_Int64 location ) throw( IllegalArgumentException,
															IOException,
															RuntimeException );
	
	virtual sal_Int64 SAL_CALL getPosition( void ) throw( IOException,RuntimeException );
	
	virtual sal_Int64 SAL_CALL getLength( void ) throw( IOException,RuntimeException );
	
	void addToBuffer( const char* buffer,int len );

	sal_Int8* getData() const { return (sal_Int8*) buffer; }

    sal_Int32 getLen() const { return sal_Int32( len ); }
    
private:
	
	osl::Mutex m_aMutex;
	
	int len,pos;
	char *buffer;
};



void URLParameter::open( const Reference< XMultiServiceFactory >& rxSMgr,
						 const Command& aCommand,
						 sal_Int32 CommandId,
						 const Reference< XCommandEnvironment >& Environment,
						 const Reference< XOutputStream >& xDataSink )
{
	(void)rxSMgr;
	(void)aCommand;
	(void)CommandId;
	(void)Environment;

    if( ! xDataSink.is() )
        return;
    
	if( isPicture() )
	{
		Reference< XInputStream > xStream;
		Reference< XHierarchicalNameAccess > xNA =
			m_pDatabases->jarFile( rtl::OUString::createFromAscii( "picture.jar" ),
								   get_language() );
		
		rtl::OUString path = get_path();
		if( xNA.is() )
		{
			try
			{
				Any aEntry = xNA->getByHierarchicalName( path );
				Reference< XActiveDataSink > xSink;
				if( ( aEntry >>= xSink ) && xSink.is() )
					xStream = xSink->getInputStream();
			}
			catch ( NoSuchElementException & )
			{
			}
		}
        if( xStream.is() )
        {
            sal_Int32 ret;
            Sequence< sal_Int8 > aSeq( 4096 );
            while( true )
            {
                try
                {
                    ret = xStream->readBytes( aSeq,4096 );
                    xDataSink->writeBytes( aSeq );
                    if( ret < 4096 )
                        break;
                }
                catch( const Exception& )
                {
                    break;
                }
            }
        }
	}
	else
    {
		// a standard document or else an active help text, plug in the new input stream	
		InputStreamTransformer* p = new InputStreamTransformer( this,m_pDatabases,isRoot() );
        try
        {
            xDataSink->writeBytes( Sequence< sal_Int8 >( p->getData(),p->getLen() ) );
        }
        catch( const Exception& )
        {
        }
        delete p;
    }
    xDataSink->closeOutput();
}



void URLParameter::open( const Reference< XMultiServiceFactory >& rxSMgr,
						 const Command& aCommand,
						 sal_Int32 CommandId,
						 const Reference< XCommandEnvironment >& Environment,
						 const Reference< XActiveDataSink >& xDataSink )
{
	(void)rxSMgr;
	(void)aCommand;
	(void)CommandId;
	(void)Environment;

	if( isPicture() )
	{
		Reference< XInputStream > xStream;
		Reference< XHierarchicalNameAccess > xNA =
			m_pDatabases->jarFile( rtl::OUString::createFromAscii( "picture.jar" ),
								   get_language() );
		
		rtl::OUString path = get_path();
		if( xNA.is() )
		{
			try
			{
				Any aEntry = xNA->getByHierarchicalName( path );
				Reference< XActiveDataSink > xSink;
				if( ( aEntry >>= xSink ) && xSink.is() )
					xStream = xSink->getInputStream();
			}
			catch ( NoSuchElementException & )
			{
			}
		}
#ifdef WORKAROUND_98119
		xDataSink->setInputStream( turnToSeekable(xStream) );
#else
		xDataSink->setInputStream( xStream );
#endif
	}
	else
		// a standard document or else an active help text, plug in the new input stream	
		xDataSink->setInputStream( new InputStreamTransformer( this,m_pDatabases,isRoot() ) );
}


// #include <stdio.h>

void URLParameter::parse() throw( com::sun::star::ucb::IllegalIdentifierException )
{
    // fprintf(stdout,"url send to xmlhelp: %s\n",(rtl::OUStringToOString(m_aURL,RTL_TEXTENCODING_UTF8).getStr()));
	m_aExpr = m_aURL;
	
	sal_Int32 lstIdx = m_aExpr.lastIndexOf( sal_Unicode( '#' ) );
	if( lstIdx != -1 )
		m_aExpr = m_aExpr.copy( 0,lstIdx );
	
	if( ! scheme() ||
        ! name( module() ) ||
        ! query() ||
        ! m_aLanguage.getLength() ||
        ! m_aSystem.getLength() )
		throw com::sun::star::ucb::IllegalIdentifierException();
}
		

bool URLParameter::scheme()
{
	// Correct extension help links as sometimes the
	// module is missing resulting in a misformed URL
	if( m_aExpr.compareToAscii( "vnd.sun.star.help:///", 21 ) == 0 )
	{
		sal_Int32 nLen = m_aExpr.getLength();
		rtl::OUString aLastStr = m_aExpr.copy( nLen - 6 );
		if( aLastStr.compareToAscii( "DbPAR=" ) == 0 )
		{
			rtl::OUString aNewExpr = m_aExpr.copy( 0, 20 );
			rtl::OUString aSharedStr = rtl::OUString::createFromAscii( "shared" );
			aNewExpr += aSharedStr;
			aNewExpr += m_aExpr.copy( 20 );
			aNewExpr += aSharedStr;
			m_aExpr = aNewExpr;
		}
	}

	for( sal_Int32 nPrefixLen = 20 ; nPrefixLen >= 18 ; --nPrefixLen )
	{
		if( m_aExpr.compareToAscii( "vnd.sun.star.help://", nPrefixLen ) == 0 )
		{
			m_aExpr = m_aExpr.copy( nPrefixLen );
			return true;
		}
	}
	return false;
}

		
bool URLParameter::module()
{
	sal_Int32 idx = 0,length = m_aExpr.getLength();
	
	while( idx < length && isLetterOrDigit( (m_aExpr.getStr())[idx] ) ) 
		++idx;
			
	if( idx != 0 )
	{
		m_aModule = m_aExpr.copy( 0,idx );
		m_aExpr = m_aExpr.copy( idx );
		return true;
	}
	else
		return false;
}
	
	
	
bool URLParameter::name( bool modulePresent )
{
	// if modulepresent, a name may be present, but must not

	sal_Int32 length = m_aExpr.getLength();
    
	if( length != 0 && (m_aExpr.getStr())[0] == sal_Unicode( '/' ) )
	{
		sal_Int32 idx = 1;
		while( idx < length && (m_aExpr.getStr())[idx] != '?' )
//                ( isLetterOrDigit( (m_aExpr.getStr())[idx] ) 
//                  || (m_aExpr.getStr())[idx] == '/'
//                  || (m_aExpr.getStr())[idx] == '.' ))
			++idx;
        
		if( idx != 1 && ! modulePresent )
			return false;
		else
		{
			m_aId = m_aExpr.copy( 1,idx-1 );
			m_aExpr = m_aExpr.copy( idx );
		}
	}
    
//    fprintf(stdout,"id %s\n",(rtl::OUStringToOString(m_aId,RTL_TEXTENCODING_UTF8).getStr()));
	return true;
}
		
	
bool URLParameter::query()
{
	rtl::OUString query_;
	
	if( ! m_aExpr.getLength() )
		return true;
	else if( (m_aExpr.getStr())[0] == sal_Unicode( '?' ) )
		query_ = m_aExpr.copy( 1 ).trim();
	else
		return false;
		
			
	bool ret = true;
	sal_Int32 delimIdx,equalIdx;
	rtl::OUString parameter,value;
		
	while( query_.getLength() != 0 )
	{
		delimIdx = query_.indexOf( sal_Unicode( '&' ) );
		equalIdx = query_.indexOf( sal_Unicode( '=' ) );
		parameter = query_.copy( 0,equalIdx ).trim();
		if( delimIdx == -1 )
		{
			value = query_.copy( equalIdx + 1 ).trim();
			query_ = rtl::OUString();
		}
		else
		{
			value = query_.copy( equalIdx+1,delimIdx - equalIdx - 1 ).trim();
			query_ = query_.copy( delimIdx+1 ).trim();
		}
		
		if( parameter.compareToAscii( "Language" ) == 0 )
			m_aLanguage = value;
		else if( parameter.compareToAscii( "Device" ) == 0 )
			m_aDevice = value;
		else if( parameter.compareToAscii( "Program" ) == 0 )
			m_aProgram = value;
		else if( parameter.compareToAscii( "Eid" ) == 0 )
			m_aEid = value;
		else if( parameter.compareToAscii( "UseDB" ) == 0 )
            m_bUseDB = ! ( value.compareToAscii("no") == 0 );
        else if( parameter.compareToAscii( "DbPAR" ) == 0 )
            m_aDbPar = value;
		else if( parameter.compareToAscii( "Query" ) == 0 )
		{	
			if( ! m_aQuery.getLength() )
				m_aQuery = value;
			else
				m_aQuery += ( rtl::OUString::createFromAscii( " " ) + value );
		}
		else if( parameter.compareToAscii( "Scope" ) == 0 )
			m_aScope = value;
		else if( parameter.compareToAscii( "System" ) == 0 )
			m_aSystem = value;
		else if( parameter.compareToAscii( "HelpPrefix" ) == 0 )
			m_aPrefix = rtl::Uri::decode(
                value,
                rtl_UriDecodeWithCharset,
                RTL_TEXTENCODING_UTF8 );
		else if( parameter.compareToAscii( "HitCount" ) == 0 )
			m_nHitCount = value.toInt32();
		else if( parameter.compareToAscii( "Active" ) == 0 )
			m_aActive = value;
		else
			ret = false;
	}
			
	return ret;
}

struct UserData {
	
	UserData( InputStreamTransformer* pTransformer,
			  URLParameter*           pInitial,
			  Databases*              pDatabases )
		: m_pTransformer( pTransformer ),
          m_pDatabases( pDatabases ),
		  m_pInitial( pInitial )
	{
	}
	
	InputStreamTransformer*             m_pTransformer;
	Databases*                          m_pDatabases;
	URLParameter*                       m_pInitial;
};

UserData *ugblData = 0;

extern "C" {

static int
fileMatch(const char * URI) {
	if ((URI != NULL) && !strncmp(URI, "file:/", 6))
        return 1;
    return 0;
}

static int
zipMatch(const char * URI) {
	if ((URI != NULL) && !strncmp(URI, "vnd.sun.star.zip:/", 18))
        return 1;
    return 0;
}

static int
helpMatch(const char * URI) {
	if ((URI != NULL) && !strncmp(URI, "vnd.sun.star.help:/", 19))
        return 1;
    return 0;
}

static void *
fileOpen(const char *URI) {
	osl::File *pRet = new osl::File(rtl::OUString(URI, strlen(URI), RTL_TEXTENCODING_UTF8));
	pRet->open(OpenFlag_Read);
	return pRet;
}

static void *
zipOpen(const char * /*URI*/) {
	rtl::OUString language,jar,path;

	if( ugblData->m_pInitial->get_eid().getLength() )
		return (void*)(new Reference< XHierarchicalNameAccess >);
	else
	{
		jar = ugblData->m_pInitial->get_jar();
		language = ugblData->m_pInitial->get_language();
		path = ugblData->m_pInitial->get_path();
	}

	Reference< XHierarchicalNameAccess > xNA =
		ugblData->m_pDatabases->findJarFileForPath( jar, language, path );

    Reference< XInputStream > xInputStream;

	if( xNA.is() )
	{
		try
		{
			Any aEntry = xNA->getByHierarchicalName( path );
			Reference< XActiveDataSink > xSink;
			if( ( aEntry >>= xSink ) && xSink.is() )
				xInputStream = xSink->getInputStream();
		}
		catch ( NoSuchElementException & )
		{
		}
	}
	
	if( xInputStream.is() )
	{
		return new Reference<XInputStream>(xInputStream);
	}
	return 0;
}

static void *
helpOpen(const char * URI) {
	rtl::OUString language,jar,path;

	URLParameter urlpar( rtl::OUString::createFromAscii( URI ),
						 ugblData->m_pDatabases );
		
	jar = urlpar.get_jar();
	language = urlpar.get_language();
	path = urlpar.get_path();

	Reference< XHierarchicalNameAccess > xNA =
		ugblData->m_pDatabases->findJarFileForPath( jar, language, path );

    Reference< XInputStream > xInputStream;

	if( xNA.is() )
	{
		try
		{
			Any aEntry = xNA->getByHierarchicalName( path );
			Reference< XActiveDataSink > xSink;
			if( ( aEntry >>= xSink ) && xSink.is() )
				xInputStream = xSink->getInputStream();
		}
		catch ( NoSuchElementException & )
		{
		}
	}
	
	if( xInputStream.is() )
		return new Reference<XInputStream>(xInputStream);
	return 0;
}

static int
helpRead(void * context, char * buffer, int len) {
	Reference< XInputStream > *pRef = (Reference< XInputStream >*)context;

	Sequence< sal_Int8 > aSeq;
	len = (*pRef)->readBytes( aSeq,len);
	memcpy(buffer, aSeq.getConstArray(), len);

	return len;
}

static int
zipRead(void * context, char * buffer, int len) {
	if( ugblData->m_pInitial->get_eid().getLength() )
	{
		ugblData->m_pDatabases->popupDocument( ugblData->m_pInitial,&buffer,&len);
		return len;
	}
	else
		return helpRead(context, buffer, len);
}

static int
fileRead(void * context, char * buffer, int len) {
	int nRead = 0;
	osl::File *pFile = (osl::File*)context;
	if (pFile)
	{
		sal_uInt64 uRead = 0;
		if (osl::FileBase::E_None == pFile->read(buffer, len, uRead))
			nRead = static_cast<int>(uRead);
	}
	return nRead;
}

static int
uriClose(void * context) {
	Reference< XInputStream > *pRef = (Reference< XInputStream >*)context;
	delete pRef;
    return 0;
}

static int
fileClose(void * context) {
	osl::File *pFile = (osl::File*)context;
	if (pFile)
	{
		pFile->close();
		delete pFile;
	}
	return 0;
}

} // extern "C"

/*
// For debugging only
extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
{
	(void)userData;
	(void)error;

	// Reset error handler
	xmlSetStructuredErrorFunc( NULL, NULL ); 
}
*/

InputStreamTransformer::InputStreamTransformer( URLParameter* urlParam,
												Databases*    pDatabases,
												bool isRoot )
	: len( 0 ),
	  pos( 0 ),
	  buffer( new char[1] ) // Initializing with one element to avoid gcc compiler warning
{
	if( isRoot )
	{
		delete[] buffer;
		pDatabases->cascadingStylesheet( urlParam->get_language(),
										 &buffer,
										 &len );
	}
	else if( urlParam->isActive() )
	{
		delete[] buffer;
		pDatabases->setActiveText( urlParam->get_module(),
								   urlParam->get_language(),
								   urlParam->get_id(),
								   &buffer,
								   &len );
	}
	else
	{
		UserData userData( this,urlParam,pDatabases );
	
		// Uses the implementation detail, that rtl::OString::getStr returns a zero terminated character-array
        
		const char* parameter[47];
		rtl::OString parString[46];
		int last = 0;
        
		parString[last++] = "Program";
		rtl::OString aPureProgramm( urlParam->getByName( "Program" ) );
		parString[last++] = rtl::OString('\'') + aPureProgramm + rtl::OString('\'');
		parString[last++] = "Database";
		parString[last++] = rtl::OString('\'') + urlParam->getByName( "DatabasePar" ) + rtl::OString('\'');
		parString[last++] = "Id";
		parString[last++] = rtl::OString('\'') + urlParam->getByName( "Id" ) + rtl::OString('\'');
		parString[last++] = "Path";
		rtl::OString aPath( urlParam->getByName( "Path" ) );
		parString[last++] = rtl::OString('\'') + aPath + rtl::OString('\'');

		rtl::OString aPureLanguage = urlParam->getByName( "Language" );
		parString[last++] = "Language";
		parString[last++] = rtl::OString('\'') + aPureLanguage + rtl::OString('\'');
		parString[last++] = "System";
		parString[last++] = rtl::OString('\'') + urlParam->getByName( "System" ) + rtl::OString('\'');
		parString[last++] = "productname";
		parString[last++] = rtl::OString('\'') + rtl::OString(
            pDatabases->getProductName().getStr(),
            pDatabases->getProductName().getLength(),
            RTL_TEXTENCODING_UTF8 ) + rtl::OString('\'');
		parString[last++] = "productversion";
		parString[last++] = rtl::OString('\'') + 
            rtl::OString(  pDatabases->getProductVersion().getStr(),
                          pDatabases->getProductVersion().getLength(),
                          RTL_TEXTENCODING_UTF8 ) + rtl::OString('\'');

        parString[last++] = "imgrepos";
        parString[last++] = rtl::OString('\'') + pDatabases->getImagesZipFileURL() + rtl::OString('\'');
        parString[last++] = "hp";
        parString[last++] = rtl::OString('\'') + urlParam->getByName( "HelpPrefix" ) + rtl::OString('\'');
        
        if( parString[last-1].getLength() )
        {
            parString[last++] = "sm";
            parString[last++] = "'vnd.sun.star.help%3A%2F%2F'";
            parString[last++] = "qm";
            parString[last++] = "'%3F'";
            parString[last++] = "es";
            parString[last++] = "'%3D'";
            parString[last++] = "am";
            parString[last++] = "'%26'";
            parString[last++] = "cl";
            parString[last++] = "'%3A'";
            parString[last++] = "sl";
            parString[last++] = "'%2F'";
            parString[last++] = "hm";
            parString[last++] = "'%23'";
            parString[last++] = "cs";
            parString[last++] = "'css'";

            parString[last++] = "vendorname";
            parString[last++] = rtl::OString("''");
            parString[last++] = "vendorversion";
            parString[last++] = rtl::OString("''");
            parString[last++] = "vendorshort";
            parString[last++] = rtl::OString("''");
        }
        
		// Do we need to add extension path?
		::rtl::OUString aExtensionPath;
		rtl::OUString aJar = urlParam->get_jar();

		bool bAddExtensionPath = false;
        rtl::OUString aExtensionRegistryPath;
        sal_Int32 nQuestionMark1 = aJar.indexOf( sal_Unicode('?') );
        sal_Int32 nQuestionMark2 = aJar.lastIndexOf( sal_Unicode('?') );
		if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 )
		{
			aExtensionPath = aJar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 );
            aExtensionRegistryPath = urlParam->get_ExtensionRegistryPath();
			bAddExtensionPath = true;
		}
		else
		{
			// Path not yet specified, search directly
			Reference< XHierarchicalNameAccess > xNA = pDatabases->findJarFileForPath
				( aJar, urlParam->get_language(), urlParam->get_path(), &aExtensionPath, &aExtensionRegistryPath );
			if( xNA.is() && aExtensionPath.getLength() )
				bAddExtensionPath = true;
		}

		if( bAddExtensionPath )
		{
			Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();
			Reference< XPropertySet > xProps( xFactory, UNO_QUERY );
			OSL_ASSERT( xProps.is() );
			Reference< XComponentContext > xContext;
			if (xProps.is())
			{
				xProps->getPropertyValue(
					::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DefaultContext") ) ) >>= xContext;
			}
			if( !xContext.is() )
			{
				throw RuntimeException(
					::rtl::OUString::createFromAscii( "InputStreamTransformer::InputStreamTransformer(), no XComponentContext" ),
					Reference< XInterface >() );
			}

			rtl::OUString aOUExpandedExtensionPath = Databases::expandURL( aExtensionRegistryPath, xContext );
			rtl::OString aExpandedExtensionPath = rtl::OUStringToOString( aOUExpandedExtensionPath, osl_getThreadTextEncoding() );

			parString[last++] = "ExtensionPath";
			parString[last++] = rtl::OString('\'') + aExpandedExtensionPath + rtl::OString('\'');

			// ExtensionId
			rtl::OString aPureExtensionId;
			sal_Int32 iSlash = aPath.indexOf( '/' );
			if( iSlash != -1 )
				aPureExtensionId = aPath.copy( 0, iSlash );

			parString[last++] = "ExtensionId";
			parString[last++] = rtl::OString('\'') + aPureExtensionId + rtl::OString('\'');
		}

		for( int i = 0; i < last; ++i )
			parameter[i] = parString[i].getStr();
		parameter[last] = 0;
		
		rtl::OUString xslURL = pDatabases->getInstallPathAsURL();
		
		rtl::OString xslURLascii( 
			xslURL.getStr(),
			xslURL.getLength(),
			RTL_TEXTENCODING_UTF8);
		xslURLascii += "main_transform.xsl";

        ugblData = &userData;

        xmlInitParser();
        xmlRegisterInputCallbacks(zipMatch, zipOpen, zipRead, uriClose);
        xmlRegisterInputCallbacks(helpMatch, helpOpen, helpRead, uriClose);
		xmlRegisterInputCallbacks(fileMatch, fileOpen, fileRead, fileClose);
		//xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction ); 

        xsltStylesheetPtr cur = 
            xsltParseStylesheetFile((const xmlChar *)xslURLascii.getStr());

        xmlDocPtr doc = xmlParseFile("vnd.sun.star.zip:/");

        xmlDocPtr res = xsltApplyStylesheet(cur, doc, parameter);
        if (res)
		{
			xmlChar *doc_txt_ptr=0;
			int doc_txt_len;
			xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, res, cur);
			addToBuffer((const char*)doc_txt_ptr, doc_txt_len);
			xmlFree(doc_txt_ptr);
        }
        xmlPopInputCallbacks();	//filePatch
        xmlPopInputCallbacks();	//helpPatch
        xmlPopInputCallbacks();	//zipMatch
        xmlFreeDoc(res);
        xmlFreeDoc(doc);
        xsltFreeStylesheet(cur);
	}
}


InputStreamTransformer::~InputStreamTransformer()
{
	delete[] buffer;
}


Any SAL_CALL InputStreamTransformer::queryInterface( const Type& rType ) throw( RuntimeException )
{
	Any aRet = ::cppu::queryInterface( rType,
									   SAL_STATIC_CAST( XInputStream*,this ),
									   SAL_STATIC_CAST( XSeekable*,this ) );
	
	return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
}



void SAL_CALL InputStreamTransformer::acquire( void ) throw()
{
	OWeakObject::acquire();
}



void SAL_CALL InputStreamTransformer::release( void ) throw()
{
	OWeakObject::release();
}



sal_Int32 SAL_CALL InputStreamTransformer::readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead )
	throw( NotConnectedException,
		   BufferSizeExceededException,
		   IOException,
		   RuntimeException)
{
	osl::MutexGuard aGuard( m_aMutex );

	int curr,available_ = len-pos;
	if( nBytesToRead <= available_ )
		curr = nBytesToRead;
	else
		curr = available_;
	
	if( 0 <= curr && aData.getLength() < curr )
		aData.realloc( curr ); 
	
	for( int k = 0; k < curr; ++k )
		aData[k] = buffer[pos++];
	
	return curr > 0 ? curr : 0;
}


sal_Int32 SAL_CALL InputStreamTransformer::readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead )
	throw( NotConnectedException,
		   BufferSizeExceededException,
		   IOException,
		   RuntimeException)
{
	return readBytes( aData,nMaxBytesToRead );
}



void SAL_CALL InputStreamTransformer::skipBytes( sal_Int32 nBytesToSkip ) throw( NotConnectedException,
																				 BufferSizeExceededException,
																				 IOException,
																				 RuntimeException )
{
	osl::MutexGuard aGuard( m_aMutex );
	while( nBytesToSkip-- ) ++pos;
}



sal_Int32 SAL_CALL InputStreamTransformer::available( void ) throw( NotConnectedException,
																	IOException,
																	RuntimeException )
{
	osl::MutexGuard aGuard( m_aMutex );
	return len-pos > 0 ? len - pos : 0 ;
}



void SAL_CALL InputStreamTransformer::closeInput( void ) throw( NotConnectedException,
																IOException,
																RuntimeException )
{
}



void SAL_CALL InputStreamTransformer::seek( sal_Int64 location ) throw( IllegalArgumentException,
																		IOException,
																		RuntimeException )
{
	osl::MutexGuard aGuard( m_aMutex );
	if( location < 0 )
		throw IllegalArgumentException();
	else
		pos = sal::static_int_cast<sal_Int32>( location );
	
	if( pos > len )
		pos = len;
}



sal_Int64 SAL_CALL InputStreamTransformer::getPosition( void ) throw( IOException,
																	  RuntimeException )
{
	osl::MutexGuard aGuard( m_aMutex );
	return sal_Int64( pos );
}



sal_Int64 SAL_CALL InputStreamTransformer::getLength( void ) throw( IOException,RuntimeException )
{
	osl::MutexGuard aGuard( m_aMutex );
	
	return len;
}


void InputStreamTransformer::addToBuffer( const char* buffer_,int len_ )
{
	osl::MutexGuard aGuard( m_aMutex );
	
	char* tmp = buffer;
	buffer = new char[ len+len_ ];
	rtl_copyMemory( (void*)(buffer),(void*)(tmp),sal_uInt32( len ) );
	rtl_copyMemory( (void*)(buffer+len),(void*)(buffer_),sal_uInt32( len_ ) );
	delete[] tmp;
	len += len_;
}
