/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/




#include <cstdarg>
#include <vector>
#include <rtl/ustring.hxx>
#include <rtl/instance.hxx>

#include "vos/process.hxx"
#include "vos/diagnose.hxx"
#include <osl/file.hxx>

#define MAX_RESOURCES	100
#define MAX_ARGS		100
#define MAX_ENVIROMENTS 100

using namespace vos;

/////////////////////////////////////////////////////////////////////////////
/// Argument

OArgumentList::OArgumentList() :
	n_Args(0),
	m_aVec(0)
{
	// empty 
}

OArgumentList::OArgumentList( sal_uInt32 nArgs, const ::rtl::OUString* aArgument1, ... ) :
	n_Args( nArgs )
{
	m_aVec = new rtl_uString* [nArgs];
	std::va_list pArgs;
	sal_uInt32 i = 0;
	const rtl::OUString* aArgument;
	
	va_start ( pArgs, aArgument1 );
	aArgument = aArgument1;

    while( true ) {
		m_aVec[i] = aArgument->pData;
		rtl_uString_acquire( m_aVec[i++] );
		if( i < n_Args )
			aArgument = va_arg( pArgs,rtl::OUString* );
		else
			break;
	}
	va_end( pArgs );
}


OArgumentList::OArgumentList( const rtl::OUString aArgumentList[], sal_uInt32 nArgs ) :
	n_Args( nArgs )
{
	m_aVec = new rtl_uString* [n_Args];
	for( sal_uInt32 i = 0; i < n_Args; ++ i )  {
		m_aVec[i] = aArgumentList[i].pData;
		rtl_uString_acquire( m_aVec[i] );
	}
}

OArgumentList::OArgumentList( const OArgumentList& rOther ) : n_Args( rOther.n_Args )
{
	m_aVec = new rtl_uString* [n_Args];
	
	sal_uInt32 i;
	for ( i = 0; i < n_Args; ++i )
	{		
		m_aVec[i] = rOther.m_aVec[i];
		rtl_uString_acquire( m_aVec[i] );
	}
}

OArgumentList& OArgumentList::operator=( const OArgumentList& rOther )
{
	if ( this != &rOther )
	{

		// delete the old one
		sal_uInt32 i;
		for ( i = 0; i < n_Args; ++i )
			rtl_uString_release( m_aVec[i] );

		delete [] m_aVec;


		n_Args = rOther.n_Args;
		m_aVec = new rtl_uString* [n_Args];
		for( i = 0; i < n_Args; ++i )
		{
			m_aVec[i] = rOther.m_aVec[i];
			rtl_uString_acquire( m_aVec[i] );
		}
	}

	return *this;
}

OArgumentList::~OArgumentList( )
{
	for( sal_uInt32 i = 0; i < n_Args; ++i ) rtl_uString_release( m_aVec[i] );
	delete[] m_aVec;
}


////////////////////////////////////////////////////////////////////////////////
/// Environment

OEnvironment::OEnvironment() :
	n_Vars( 0 ),
	m_aVec( 0 )
{
}

OEnvironment::OEnvironment( sal_Int32 nVars, const ::rtl::OUString* aArgument1, ... ) :
	n_Vars( nVars )
{
	m_aVec = new rtl_uString* [nVars];
	std::va_list pArgs;
	sal_Int32 i = 0;
	const rtl::OUString* aArgument;
	
	va_start ( pArgs, aArgument1 );
	aArgument = aArgument1;

    while( true ) {
		m_aVec[i] = aArgument->pData;
		rtl_uString_acquire( m_aVec[i++] );
		if( i < n_Vars )
			aArgument = va_arg( pArgs,rtl::OUString* );
		else
			break;
	}
	va_end( pArgs );
}


OEnvironment::OEnvironment( const ::rtl::OUString aVariableList[], sal_Int32 nVars ) :
	n_Vars( nVars )
{
	m_aVec = new rtl_uString* [n_Vars];
	for( sal_Int32 i = 0; i < n_Vars; ++ i )  {
		m_aVec[i] = aVariableList[i].pData;
		rtl_uString_acquire( m_aVec[i] );
	}	
}

OEnvironment::OEnvironment( const OEnvironment& rOther ) : n_Vars( rOther.n_Vars )
{
	m_aVec = new rtl_uString* [n_Vars];
	
	sal_Int32 i;
	for ( i = 0; i < n_Vars; ++i )
	{
		m_aVec[i] = rOther.m_aVec[i];
		rtl_uString_acquire( m_aVec[i] );
	}
}

OEnvironment& OEnvironment::operator=( const OEnvironment& rOther )
{
	if ( this != &rOther )
	{
		sal_Int32 i;
		for ( i = 0; i < n_Vars; ++i )
			rtl_uString_release( m_aVec[i] );

		delete [] m_aVec;

		n_Vars = rOther.n_Vars;
		m_aVec = new rtl_uString* [n_Vars];
		for ( i = 0; i < n_Vars; ++i ) 
		{
			m_aVec[i] = rOther.m_aVec[i];
			rtl_uString_acquire( m_aVec[i] );
		}
	}

	return *this;
}

OEnvironment::~OEnvironment()
{
	for( sal_Int32 i = 0; i < n_Vars; ++i ) rtl_uString_release( m_aVec[i] );
	delete[] m_aVec;
}

/////////////////////////////////////////////////////////////////////////////
// Process


VOS_IMPLEMENT_CLASSINFO(
	VOS_CLASSNAME(OProcess, vos),
	VOS_NAMESPACE(OProcess, vos),
	VOS_NAMESPACE(OObject, vos), 0);


OProcess::OProcess( ) :
	m_strImageName( ),
	m_strDirectory(),
	m_Process(0)
{
}


OProcess::OProcess( const rtl::OUString& strImageName ) :
	m_strImageName( strImageName ),
	m_strDirectory(),
	m_Process(0)
{
	// empty
}


OProcess::OProcess(const rtl::OUString& strImageName, const rtl::OUString& strWorkingDirectory) :
	m_strImageName( strImageName ),
	m_strDirectory( strWorkingDirectory ),
	m_Process(0)
{
	// empty
}


OProcess::~OProcess()
{
	osl_freeProcessHandle(m_Process);
}

OProcess* OProcess::getProcess(TProcessIdentifier Identifier)
{
	oslProcess hProcess = osl_getProcess(Identifier);

	if (hProcess)
	{
		OProcess* pProcess = new OProcess( );

		pProcess->m_Process = hProcess;

		return pProcess;
	}

	return 0;
}


OProcess::TProcessError OProcess::execute(TProcessOption Options,
										  const OArgumentList& aArgumentList,
										  const OEnvironment&  aEnvironment )
{
	return ((TProcessError)osl_executeProcess(m_strImageName.pData, 
								              aArgumentList.m_aVec,
											  aArgumentList.n_Args,
											  Options, 
								              0,
								              m_strDirectory.pData,
								              aEnvironment.m_aVec,
											  aEnvironment.n_Vars,
											  &m_Process));
}


OProcess::TProcessError OProcess::execute( TProcessOption Options,
										   const OSecurity &Security,
										   const OArgumentList& aArgumentList,
										   const OEnvironment&  aEnvironment )
{
	return ((TProcessError)osl_executeProcess(m_strImageName.pData, 
								              aArgumentList.m_aVec,
											  aArgumentList.n_Args,
											  Options, 
								              Security,
								              m_strDirectory.pData,
								              aEnvironment.m_aVec,
											  aEnvironment.n_Vars,
											  &m_Process));
}


OProcess::TProcessError OProcess::terminate()
{
   return (TProcessError)osl_terminateProcess(m_Process);
}

OProcess::TProcessError OProcess::getInfo(TProcessData Data, TProcessInfo* pInfo) const
{
   return (TProcessError)osl_getProcessInfo(m_Process, Data, pInfo);
}

OProcess::TProcessError OProcess::getCurrentInfo(TProcessData Data, TProcessInfo* pInfo)
{
   return (TProcessError)osl_getProcessInfo(0, Data, pInfo);
}

OProcess::TProcessError OProcess::join()
{
	return (TProcessError)osl_joinProcess(m_Process);
}


/*
OProcess::TProcessError OProcess::searchPath(const sal_Char* Name, sal_Char *Buffer, sal_uInt32 Max, 
                                             const sal_Char* Path, sal_Char Separator)
{
	return (TProcessError)osl_searchPath(Name, Path, Separator, Buffer, Max);
}
*/

/////////////////////////////////////////////////////////////////////////////
// StartupInfo

VOS_IMPLEMENT_CLASSINFO(
	VOS_CLASSNAME(OStartupInfo, vos),
	VOS_NAMESPACE(OStartupInfo, vos), 
	VOS_NAMESPACE(OObject, vos), 0);

OStartupInfo::OStartupInfo()
{
}

OStartupInfo::~OStartupInfo()
{
}

OStartupInfo::TStartupError OStartupInfo::getExecutableFile(
    rtl::OUString& strImageName ) const
{
	return (TStartupError) osl_getExecutableFile( &strImageName.pData );
}


OStartupInfo::TStartupError OStartupInfo::getCommandArg(sal_uInt32 nArg, rtl::OUString& strCommandArg)
{
	return ( TStartupError ) osl_getCommandArg( nArg,&strCommandArg.pData );
}

sal_uInt32 OStartupInfo::getCommandArgCount()
{
	return osl_getCommandArgCount();
}

OStartupInfo::TStartupError OStartupInfo::getEnvironment(const rtl::OUString& strVar,
														 rtl::OUString& strValue)
{
	return ( TStartupError ) osl_getEnvironment( strVar.pData, &strValue.pData );
}



/////////////////////////////////////////////////////////////////////////////
//
// OExtCommandLineImpl
//

namespace vos
{  

class OExtCommandLineImpl
{
    void init();
    
    ::std::vector< ::rtl::OUString > aExtArgVector;
    sal_uInt32 m_nArgCount;
    
public:
    
    OExtCommandLineImpl();
    ~OExtCommandLineImpl();

    sal_uInt32 SAL_CALL getCommandArgCount();
    
    sal_Bool SAL_CALL getCommandArg(sal_uInt32 nArg, ::rtl::OUString& strCommandArg);
};
 
}

OExtCommandLineImpl::OExtCommandLineImpl() 
    : m_nArgCount(0)
{
    init();
}

OExtCommandLineImpl::~OExtCommandLineImpl()
{
    
}


sal_uInt32 SAL_CALL OExtCommandLineImpl::getCommandArgCount()
{
    return m_nArgCount;
}


sal_Bool SAL_CALL OExtCommandLineImpl::getCommandArg(sal_uInt32 nArg, ::rtl::OUString& strCommandArg) 
{
    if ( nArg >= m_nArgCount )
    {
        return sal_False;
    }

    strCommandArg = aExtArgVector[nArg];

    return sal_True;
}


void OExtCommandLineImpl::init()
{
    OStartupInfo aStartInfo;
    sal_uInt32 nIndex=0;
    sal_uInt32 nArgs = aStartInfo.getCommandArgCount();

    for ( nIndex = 0 ; nIndex < nArgs ; ++nIndex )
    {
        ::rtl::OUString aString;
        aStartInfo.getCommandArg(nIndex,aString);
        
        if ( aString[0] == (sal_Unicode) '@' )
        {
            ::rtl::OUString aFileName = aString.copy(1);
            ::osl::File aFile(aFileName);
            ::rtl::ByteSequence aSeq;

            ::osl::FileBase::RC aErr = aFile.open(OpenFlag_Read);

            if ( aErr != ::osl::FileBase::E_None )
            {
                break;
            }

            do
            {
                aErr = aFile.readLine(aSeq);
                if ( aSeq.getLength() != 0 )
                {
                    ::rtl::OUString newString((sal_Char*)aSeq.getArray(), aSeq.getLength(), RTL_TEXTENCODING_ASCII_US);
                    aExtArgVector.push_back( newString );
                    m_nArgCount++;
                }
            }
            while ( aErr == ::osl::FileBase::E_None && aSeq.getLength() > 0 );

			aFile.close();
			aFile.remove(aFileName);
        }
        else
        {
            aExtArgVector.push_back( aString );
            m_nArgCount++;
        }
    }
}



/////////////////////////////////////////////////////////////////////////////
//
// OExtCommandLine
//

namespace 
{ 
    struct lclMutex : public rtl::Static< vos::OMutex, lclMutex > {}; 
}

OExtCommandLineImpl* OExtCommandLine::pExtImpl=0;


VOS_IMPLEMENT_CLASSINFO(
	VOS_CLASSNAME(OExtCommandLine, vos),
	VOS_NAMESPACE(OExtCommandLine, vos),
	VOS_NAMESPACE(OObject, vos), 0);

OExtCommandLine::OExtCommandLine()
{
    OGuard Guard(lclMutex::get());

    if ( pExtImpl == NULL )
    {    
        pExtImpl = new OExtCommandLineImpl;
    }
}

OExtCommandLine::~OExtCommandLine()
{

    
}

sal_uInt32 SAL_CALL OExtCommandLine::getCommandArgCount()
{
    return pExtImpl->getCommandArgCount();
}


sal_Bool SAL_CALL OExtCommandLine::getCommandArg(sal_uInt32 nArg, ::rtl::OUString& strCommandArg) 
{
    return pExtImpl->getCommandArg(nArg,strCommandArg);
}

