/////////////////////////////////////////////////////////////////////////////////
//  
//  Magic Hash Code
//
SV *TieTask( PERL_OBJECT_PROTO ITask *pTask )
{
	HV	*pHvTiedHash = newHV();
    SV	*pSvMagic;
	SV	*pSvRef = &sv_undef;
    SV  *pSvBlessed = NULL;


    //  We are creating two hashes. One with be the blessed hash that contains named/value
    //  pairs regarding the blessed object (such as the ITask interface).
    //  This hash we bless into the Win32::Scheduler::Task class...
	if( ( NULL != pTask ) && ( NULL != pHvTiedHash ) )
	{
        HV  *pHvBlessedHash = newHV();
  
        if( NULL != pHvBlessedHash )
        {
            HASH_STORE_IV( pHvBlessedHash, KEYWORD_TASK_INTERFACE, pTask );

   	        pSvBlessed = sv_bless( newRV_noinc( (SV*) pHvBlessedHash ), gv_stashpv( EXTENSION TEXT( "::Task" ), TRUE ) );

            //  Now that we have a blessed hash (acting as our little data container) we
            //  apply magic to another hash so that it will act as the tied hash. We will
            //  associate the blessed object with this so that Perl knows where to go to
            //  call the TIEDxxxx() functions.
            sv_magic( (SV*) pHvTiedHash, pSvBlessed, 'P', Nullch, 0 );
	        
	        //  At this point we have a tied hash that is associated with a
            //  blessed hash.  When the TIEDxxxx() functions are called the first
            //  parameter passed in will be the blessed hash.  We can
            //  access the contents of this blessed hash directly without fear that
            //  it will trigger the tied hash and recursively call a TIEDxxxx() function!
            

            // When applying magic if the tied hash is not the same as the associated hash
            // (pSvBlessed) then the blessed hash's ref count is increased. Not good...
          	SvREFCNT_dec( (SV*) pSvBlessed );
        }
    }

	return( (SV*) pHvTiedHash );
}

///////////////////////////////////////////////////////////////////////
//	
XS( XS_Win32__Scheduler_TIE_HASH )
{
	dXSARGS;
    EXTENSION_VARS;
	HV	*phvObject = NULL;
	SV	*psvSelf   = NULL;
	SV	*psvResult = NULL;
		

    //  Maybe here we should allow you to connect to a remote machine??
	if( ( 1 > items ) || ( 2 < items  ) )
	{
		croak( "Usage: " EXTENSION  "::TIEHASH( $Self [, $Machine ] )\n" );
    }

	psvSelf = ST( 0 );
	
	phvObject = newHV();
    if( NULL != phvObject )
	{
        ITaskScheduler *pITS = NULL;
        HRESULT hr = CoCreateInstance( CLSID_CTaskScheduler,
                                        NULL,
                                        CLSCTX_INPROC_SERVER,
                                        IID_ITaskScheduler,
                                        (void **) &pITS );
        if( SUCCEEDED( hr ) )
        {
            if( 2 == items )
            {
                LPTSTR pszMachine = SvPV( ST( 1 ), na );
                DWORD dwTemp;

                dwTemp = _tcsspn( pszMachine, TEXT( "\\/" ) );
                pszMachine = &pszMachine[ dwTemp ];
                if( 0 != _tcscmp( pszMachine, TEXT( "" ) ) )
                {
                    TCHAR szBuffer[ 100 ];
                    CUString szuMachine;    

                    _tcscpy( szBuffer, TEXT( "\\\\" ) );
                    _tcscat( szBuffer, pszMachine );
                    szuMachine = szBuffer;
                    hr = pITS->SetTargetComputer( szuMachine );
                }
            }

            if( SUCCEEDED( hr ) )
            {
                HASH_STORE_IV( phvObject, KEYWORD_SCHEDULER_INTERFACE, pITS );
    		    psvResult = sv_bless( sv_2mortal( newRV_noinc( (SV*) phvObject ) ), 
                                      gv_stashpv( EXTENSION , TRUE ) );
            }
        }
	}
		
	if( NULL != psvResult )
	{
        PUSH_NOREF( psvResult );
	}

	EXTENSION_RETURN;
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_FETCH)
{
	dXSARGS;
	EXTENSION_VARS;
    HV		*pHvSelf;
    ITaskScheduler *pITS = NULL;
    HRESULT hr = ERROR_SUCCESS;
    DWORD dwTotal = 0;
    BOOL fResult = FALSE;
	LPTSTR pszName = NULL;
    SV *pSvResult = NULL;
		
	if (items != 2)
	{
		croak("Usage: " EXTENSION  "::TIEFETCH( $Self, $Key )\n");
    }
		
    //	FETCH	
	pHvSelf = (HV*) SvRV( ST( 0 ) );
    pszName = SvPV( ST( 1 ), na );
	
    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        CUString szuBuffer( pszName );
        ITask *pTask = NULL;    

        if( SUCCEEDED( pITS->Activate( szuBuffer, IID_ITask, (IUnknown**) &pTask ) ) )
        {
            //   Here we should create a new object and make it magic...
            pSvResult = TieTask( PERL_OBJECT_ARGS pTask );

	        if( NULL != pSvResult )
	        {
                PUSH_REF( pSvResult );
	        }
        }
    }

    EXTENSION_RETURN;
}

///////////////////////////////////////////////////////////////////////
//	
XS( XS_Win32__Scheduler_TIE_STORE )
{
	dXSARGS;
	EXTENSION_VARS;
    HV		*pHvSelf;
    HV      *pHv;
    ITaskScheduler *pITS = NULL;
    ITask          *pTask = NULL;
    HRESULT hr = ERROR_SUCCESS;
    DWORD dwTotal = 0;
    BOOL fResult = FALSE;
	LPTSTR pszName = NULL;
	
	if( 3 != items )
	{
		croak( "Usage: " EXTENSION  "::TIESTORE( $Self, $Key, [ $Task | \\%Job ] )\n" );
    }

	pHvSelf = (HV*) SvRV( ST( 0 ) );
    pszName = SvPV( ST( 1 ), na );
	if( NULL != ( pHv = EXTRACT_HV( ST( 2 ) ) ) )
    {
        // If we get here then the Win32::Scheduler::Task object is
        //  recognized as a hash.
        if( sv_isa( (SV*) pHv, EXTENSION TEXT( "::Task" ) ) )
        {
            pTask = (ITask *) HASH_GET_IV( pHv, KEYWORD_TASK_INTERFACE );
            //  pTask = (ITask *) 
        }        
    }
    
    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( ( NULL != pITS ) && ( NULL != pHv ) )
    {
        CUString szuBuffer( pszName );
        //  IScheduledWorkItem *pWorkItem = NULL;
        ITask *pTask = NULL;
        BOOL fNewTask = FALSE;

        //  First check if the element already exists. If so then modify it
        //  otherwise create a new task
        hr = pITS->Activate( szuBuffer, IID_ITask, (IUnknown**) &pTask );
        if( FAILED( hr ) )
        {
            hr = pITS->NewWorkItem( szuBuffer, CLSID_CTask, IID_ITask, (IUnknown**) &pTask );
            fNewTask = TRUE;
        }

        if( SUCCEEDED( hr ) )
        {
            HRESULT hr = ModifyTask( PERL_OBJECT_ARGS pTask, pHv );
            if( SUCCEEDED( hr ) )
            {
                if( SUCCEEDED( SaveTask( pTask ) ) )
                {
                    PUSH_IV( 1 );
                }
            }
/*
            CUString szuBuffer2;

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_ACCOUNT ) ) )
            {
                szuBuffer = HASH_GET_PV( pHvJob, KEYWORD_JOB_ACCOUNT );
                szuBuffer2 = HASH_GET_PV( pHvJob, KEYWORD_JOB_PASSWORD );
                hr = pTask->SetAccountInformation( szuBuffer, szuBuffer2 );
            }

            if( SUCCEEDED( hr ) 
                && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_PASSWORD ) ) 
                && ( ! HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_ACCOUNT ) )
                && ( NULL != pTask ) )
            {
                LPWSTR pszwAccount = NULL;

                if( SUCCEEDED( pTask->GetAccountInformation( &pszwAccount ) ) )
                {
                    szuBuffer2 = HASH_GET_PV( pHvJob, KEYWORD_JOB_PASSWORD );
                    hr = pTask->SetAccountInformation( pszwAccount, szuBuffer2 );
                    CoTaskMemFree( pszwAccount );
                }
            }
        
            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_COMMENT ) ) )
            {
                szuBuffer = HASH_GET_PV( pHvJob, KEYWORD_JOB_COMMENT );
                hr = pTask->SetComment( szuBuffer );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_CREATOR ) ) )
            {
                szuBuffer = HASH_GET_PV( pHvJob, KEYWORD_JOB_CREATOR );
                hr = pTask->SetCreator( szuBuffer );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_RETRY_COUNT ) ) )
            {
                WORD wRetryCount = (WORD) HASH_GET_IV( pHvJob, KEYWORD_JOB_RETRY_COUNT );
                hr = pTask->SetErrorRetryCount( wRetryCount );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_RETRY_INTERVAL ) ) )
            {
                WORD wRetryInterval = (WORD) HASH_GET_IV( pHvJob, KEYWORD_JOB_RETRY_INTERVAL );
                hr = pTask->SetErrorRetryInterval( wRetryInterval );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_FLAGS ) ) )
            {
                DWORD dwFlags = HASH_GET_IV( pHvJob, KEYWORD_JOB_FLAGS );
                hr = pTask->SetFlags( dwFlags );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_CREATOR ) ) )
            {
                WORD wIdleMinutes = HASH_GET_IV( pHvJob, KEYWORD_JOB_IDLE_MINUTES );
                WORD wDeadlineMinutes = HASH_GET_IV( pHvJob, KEYWORD_JOB_DEADLINE_MINUTES );
                hr = pTask->SetIdleWait( wIdleMinutes, wDeadlineMinutes );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_DATA ) ) )
            {
                SV* pSv = HASH_GET_SV( pHvJob, KEYWORD_JOB_DATA );
                PBYTE pData = (PBYTE) SvPV( pSv, na );
                DWORD dwDataLength = SvLEN( pSv );
                hr = pTask->SetWorkItemData( dwDataLength, (BYTE*) pData );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_APPLICATION ) ) )
            {
                szuBuffer = HASH_GET_PV( pHvJob, KEYWORD_JOB_APPLICATION );
                hr = pTask->SetApplicationName( szuBuffer );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_DIRECTORY ) ) )
            {
                szuBuffer = HASH_GET_PV( pHvJob, KEYWORD_JOB_DIRECTORY );
                hr = pTask->SetWorkingDirectory( szuBuffer );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_PRIORITY ) ) )
            {
                DWORD dwPriority = HASH_GET_IV( pHvJob, KEYWORD_JOB_PRIORITY );
                hr = pTask->SetPriority( dwPriority );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_MAX_RUN_TIME ) ) )
            {
                DWORD dwMaxRunTime = HASH_GET_IV( pHvJob, KEYWORD_JOB_MAX_RUN_TIME );
                hr = pTask->SetMaxRunTime( dwMaxRunTime );
            }

            if( SUCCEEDED( hr ) && ( HASH_KEY_EXISTS( pHvJob, KEYWORD_JOB_PARAMETERS ) ) )
            {
                szuBuffer = HASH_GET_PV( pHvJob, KEYWORD_JOB_PARAMETERS );
                hr = pTask->SetParameters( szuBuffer );
            }
*/
            if( SUCCEEDED( hr ) )
            {
                if( TRUE == fNewTask )
                {
                    szuBuffer = pszName;
                    if( SUCCEEDED( pITS->AddWorkItem( szuBuffer, pTask ) ) )
                    {
                        fResult = TRUE;
                    }
                }
                else
                {
                    fResult = TRUE;
                }
            }
        
            if( TRUE == fResult )
            {
                PUSH_IV( 1 );
            }

            pTask->Release();
        }
    }
    EXTENSION_RETURN;
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_FIRSTKEY)
{
	dXSARGS;
    EXTENSION_VARS;
    HV		*pHvSelf;
    ITaskScheduler *pITS = NULL;
    HRESULT hr = ERROR_SUCCESS;
    DWORD dwTotal = 0;
    BOOL fResult = FALSE;
	LPTSTR pszPreviousKey = NULL;
	
	if( 1 != items )
	{
		croak( "Usage: " EXTENSION  "::FIRSTKEY( $Self )\n" );
    }

    pHvSelf = (HV*) SvRV( ST( 0 ) );
    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        IEnumWorkItems *pEnum = NULL;
        if( SUCCEEDED( pITS->Enum( &pEnum ) ) )
        {
            
            LPWSTR *pszwNames;
            DWORD dwTasks = 0;
            DWORD dwTasksToFetch = 1;
            CUString szuName;
            BOOL fFound = FALSE;

            if( SUCCEEDED( pEnum->Next( dwTasksToFetch,
                                            &pszwNames,
                                            &dwTasks ) )
                                        && ( 0 != dwTasks ) )
            {
                DWORD dwCount;

                szuName = pszwNames[ 0 ];
                CoTaskMemFree( pszwNames[ 0 ] );

//                if( 0 == ( dwCount = FindSubString( szuName, TEXT( ".job" ) ) ) )
                {
                    dwCount = _tcslen( (LPTSTR) szuName );
                }
                PUSH_PNV( (LPTSTR) szuName, dwCount );
            }
            CoTaskMemFree( pszwNames );
            pEnum->Release();
        }    
    }

    EXTENSION_RETURN;
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_NEXTKEY)
{
	dXSARGS;
    EXTENSION_VARS;
    HV		*pHvSelf;
    ITaskScheduler *pITS = NULL;
    HRESULT hr = ERROR_SUCCESS;
    DWORD dwTotal = 0;
    BOOL fResult = FALSE;
	LPTSTR pszPreviousKey = NULL;
	
	if( 2 != items )
	{
		croak( "Usage: " EXTENSION  "::NEXTKEY( $Self, $PreviousKey )\n" );
    }

	pszPreviousKey = SvPV( ST( 1 ), na );
    pHvSelf = (HV*) SvRV( ST( 0 ) );
    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        IEnumWorkItems *pEnum = NULL;
        if( SUCCEEDED( pITS->Enum( &pEnum ) ) )
        {
            
            LPWSTR *pszwNames;
            DWORD dwTasks = 0;
            DWORD dwTasksToFetch = TOTAL_TASKS_TO_RETRIEVE;
            CUString szuName;
            BOOL fFound = FALSE;

            while( SUCCEEDED( pEnum->Next( dwTasksToFetch,
                                            &pszwNames,
                                            &dwTasks ) )
                                        && ( 0 != dwTasks )
                                        && ( FALSE == fResult ) )
            {
                DWORD dwIndex = 0;
                while( dwIndex < dwTasks )
                {
                    szuName = pszwNames[ dwIndex ];
                    CoTaskMemFree( pszwNames[ dwIndex++ ] );

                    if( TRUE == fFound )
                    {
                        DWORD dwCount;
                        fResult = TRUE; 
//                        if( 0 == ( dwCount = FindSubString( szuName, TEXT( ".job" ) ) ) )
                        {
                            dwCount = _tcslen( (LPTSTR) szuName );
                        }
                        PUSH_PNV( (LPTSTR) szuName, dwCount );

                        while( dwIndex < dwTasks )
                        {
                            CoTaskMemFree( pszwNames[ dwIndex++ ] );
                        }
                        break;
                    }
                    else if( 0 == _tcsicmp( szuName, pszPreviousKey ) )
                    {
                        fFound = TRUE;
                    }
                }
                CoTaskMemFree( pszwNames );
            }
            pEnum->Release();
        }    
    }
    EXTENSION_RETURN;
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_EXISTS)
{
	dXSARGS;
    EXTENSION_VARS;
    HV		*pHvSelf;
    ITaskScheduler *pITS = NULL;
    HRESULT hr = ERROR_SUCCESS;
    LPTSTR pszKey = NULL;
    DWORD dwTotal = 0;
    BOOL fResult = FALSE;
	
	if( 2 != items )
	{
		croak( "Usage: " EXTENSION  "::EXISTS( $Self, $Key )\n" );
    }

	pszKey  = SvPV( ST( 1 ), na);

    pHvSelf = (HV*) SvRV( ST( 0 ) );
    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        CUString szuBuffer( pszKey );
        ITask *pTask = NULL;
        BOOL fNewTask = FALSE;

        //  First check if the element already exists. If so then modify it
        //  otherwise create a new task

        if( SUCCEEDED( pITS->Activate( szuBuffer, IID_ITask, (IUnknown**) &pTask ) ) )
        {
            fResult = TRUE;
            pTask->Release();
        }
    }
        
/*        
        
        
        
        IEnumWorkItems *pEnum = NULL;
        if( SUCCEEDED( pITS->Enum( &pEnum ) ) )
        {
            
            LPWSTR *pszwNames;
            DWORD dwTasks = 0;
            DWORD dwTasksToFetch = TOTAL_TASKS_TO_RETRIEVE;
            CUString szuName;

            while( SUCCEEDED( pEnum->Next( dwTasksToFetch,
                                            &pszwNames,
                                            &dwTasks ) )
                                        && ( 0 != dwTasks )
                                        && ( FALSE == fResult ) )
            {
                DWORD dwIndex = 0;
                while( dwIndex < dwTasks )
                {
                    szuName = pszwNames[ dwIndex ];
                    CoTaskMemFree( pszwNames[ dwIndex++ ] );

                    if( ( FALSE == fResult ) && ( 0 == _tcsicmp( szuName, pszKey ) ) )
                    {
                        fResult = TRUE; 
                    }
                }
                CoTaskMemFree( pszwNames );
            }
            pEnum->Release();
        }    
    }
*/

    EXTENSION_RETURN_BOOL( fResult );
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_CLEAR)
{
	dXSARGS;
    EXTENSION_VARS;
    HV		*pHvSelf;
    ITaskScheduler *pITS = NULL;
    HRESULT hr = ERROR_SUCCESS;
    DWORD dwTotal = 0;
	
	if( 1 != items )
	{
		croak( "Usage: " EXTENSION  "::CLEAR( $Self )\n" );
    }

#ifdef CLEAR_WORK_ITEMS
    //  This is VERY dangerous as it will clear out all of the
    //  registered work items.  BE CAREFULL IF YOU USE THIS!!!
    pHvSelf = (HV*) SvRV( ST( 0 ) );
    pITS = (ITaskScheduler *) HASH_GET_IV( hvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        IEnumWorkItems *pEnum = NULL;
        if( SUCCEEDED( pITS->Enum( &pEnum ) )
        {
            LPWSTR *pszwNames;
            DWORD dwTasks = 0;
            DWORD dwTasksToFetch = TOTAL_TASKS_TO_RETRIEVE;
            CUString szuName;
     
            while( SUCCEEDED( pIEnum->Next( dwTasksToFetch,
                                            &pszwNames,
                                            &dwTasks ) )
                                        && ( 0 != dwTasks ) )
            {
                while( dwTasks )
                {
                    szuName = pszwNames[ --dwTasks ];
                    CoTaskMemFree( pszwNames[ dwTasks ] );

                    dwTotal++;
                    hr = pITS->Delete( pszuName );
                }
                CoTaskMemFree( pszwNames );
            }
            pIEnum->Release();
        }    
    }
#endif  //  CLEAR_WORK_ITEMS

    EXTENSION_RETURN_BOOL( SUCCEEDED( hr ) );
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_DELETE)
{
	dXSARGS;
    EXTENSION_VARS;
    HV		*pHvSelf;
    ITaskScheduler *pITS = NULL;
    HRESULT hr;
    CUString szuName;
	
	if( 2 != items )
	{
		croak( "Usage: " EXTENSION  "::DELETE( $Self, $Key )\n" );
    }

	//	DELETE	
	pHvSelf = (HV*) SvRV( ST( 0 ) );
	szuName = SvPV( ST( 1 ), na);

    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        hr = pITS->Delete( szuName );
    }

    EXTENSION_RETURN_BOOL( SUCCEEDED( hr ) );
}

///////////////////////////////////////////////////////////////////////
//	
XS(XS_Win32__Scheduler_TIE_DESTROY)
{
	dXSARGS;
    EXTENSION_VARS;
    HV		*pHvSelf;
    SV      *pSvSelf = NULL;
    ITaskScheduler *pITS = NULL;
	
	if( 1 != items )
	{
		croak( "Usage: " EXTENSION  "::DESTROY( $Self )\n" );
    }
    
	//	DESTROY
    pHvSelf = (HV*) SvRV( ST( 0 ) );
    pITS = (ITaskScheduler *) HASH_GET_IV( pHvSelf, KEYWORD_SCHEDULER_INTERFACE );
    if( NULL != pITS )
    {
        pITS->Release();
    }

    XSRETURN_EMPTY;
}