/**************************************************************
 *
 * 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);
}