/*****************************************************************************\
* MODULE: splpjm.c
*
* This module contains the routines to deal with job-mapping list.
*
*
* Copyright (C) 1996-1997 Microsoft Corporation
* Copyright (C) 1996-1997 Hewlett Packard
*
* History:
*   13-Jan-1997 ChrisWil    Created.
*
\*****************************************************************************/

#include "precomp.h"
#include "priv.h"

/*****************************************************************************\
* pjm_OldEntry (Local Routine)
*
*
\*****************************************************************************/
_inline BOOL pjm_IsOldEntry(
    PJOBMAP      pjm)
{
    return !pjm->bRemoteJob && !pjmChkState(pjm, PJM_SPOOLING);
}

/*****************************************************************************\
* pjm_IsLocalJob (Local Routine)
*
*
\*****************************************************************************/
_inline BOOL pjm_IsLocalJob(
    PJOBMAP      pjm)
{
    return !pjm->bRemoteJob && pjmChkState(pjm, PJM_SPOOLING);
}

    


/*****************************************************************************\
* pjm_DelEntry (Local Routine)
*
*
\*****************************************************************************/
_inline VOID pjm_DelEntry(
    PJOBMAP     pjm)
{
    if (pjm->hSplFile)
        SplFree(pjm->hSplFile);

    if (pjm->lpszUri)
        memFreeStr(pjm->lpszUri);

    if (pjm->lpszUser)
        memFreeStr(pjm->lpszUser);

    memFree(pjm, sizeof(JOBMAP));
}


/*****************************************************************************\
* pjmAdd
*
* Add a job-mapping to the list.  Access to this list needs ownership of the
* crit-sect.
*
\*****************************************************************************/
PJOBMAP pjmAdd(
    PJOBMAP         *pjmList,
    PCINETMONPORT   pIniPort,
    LPCTSTR         lpszUser,
    LPCTSTR         lpszDocName)
{
    PJOBMAP pjm = NULL;

    static DWORD s_idJobLocal = 0;

    semCheckCrit();

    if (pjmList) {

        
        // Add the new job-entry to the list.
        //
        if (pjm = (PJOBMAP)memAlloc(sizeof(JOBMAP))) {
    
            // Initialize our job-entry.
            //
            GetSystemTime (&pjm->SubmitTime);

            pjm->dwState        = 0;
            pjm->pIniPort       = pIniPort;
            pjm->idJobLocal     = ++s_idJobLocal;
            pjm->idJobRemote    = 0;
            pjm->lpszUri        = NULL;
            pjm->lpszUser       = memAllocStr(lpszUser);
            pjm->lpszDocName    = memAllocStr(lpszDocName);
            pjm->hSplFile       = NULL;
            pjm->dwLocalJobSize = 0;
            pjm->dwStatus       = JOB_STATUS_SPOOLING;
            pjm->pNext          = NULL;
    
    
            // Place it at the end of the list.
            //

            if (! *pjmList) {
                *pjmList = pjm;
            }
            else {

                for (PJOBMAP pjPtr = *pjmList; pjPtr->pNext; pjPtr = pjPtr->pNext);
                pjPtr->pNext = pjm;
            }
        }
    }

    return pjm;
}


/*****************************************************************************\
* pjmDel
*
* Delete a job-mapping from the list.  Access to this list needs owenership
* of the crit-sect.
*
\*****************************************************************************/
VOID pjmDel(
    PJOBMAP* pjmList,
    PJOBMAP  pjm)
{
    PJOBMAP pjPtr;
    PJOBMAP pjPrv;
    DWORD   idx;

    semCheckCrit();

    if (pjmList) {

        for (pjPtr = *pjmList, idx = 0; pjPtr && (pjm != pjPtr); ) {
    
            // Reposition.
            //
            pjPrv = pjPtr;
            pjPtr = pjPtr->pNext;
    
    
            // If we've iterated 1000 jobs in this list, then we've
            // either got a very heavily taxed print-system, or we
            // may have an infinite loop.
            //
            if (idx++ >= g_dwJobLimit) {
    
                DBG_MSG(DBG_LEV_ERROR, (TEXT("Error: pjmDel : Looped 1000 jobs, possible infinite loop")));
    
                return;
            }
        }
    
    
        // If we found the entry, then delete it.
        //
        if (pjPtr) {
    
            if (pjPtr == *pjmList) {
    
                *pjmList = pjPtr->pNext;
    
            } else {
    
                pjPrv->pNext = pjPtr->pNext;
            }
    
            pjm_DelEntry(pjPtr);
        }
    }
}

/*****************************************************************************\
* pjmCleanRemoteFlag
*
* Cleanup remote jobid from list.  Access to this list needs ownership of the
* crit-sect. 
*
\*****************************************************************************/
VOID pjmCleanRemoteFlag(
    PJOBMAP* pjmList)
{
    PJOBMAP pjPtr = NULL;

    semCheckCrit();

    if (pjmList) {
    
        for (pjPtr = *pjmList; pjPtr; ) {

            pjPtr->bRemoteJob = FALSE;
    
            // Next item.
            //
            pjPtr = pjPtr->pNext;
    
        }
    }
    
}

/*****************************************************************************\
* pjmGetLocalJobCount
*
* Locate number of job-entries from list.  Access to this list needs ownership of the
* crit-sect. 
*
\*****************************************************************************/
DWORD pjmGetLocalJobCount(
    PJOBMAP* pjmList,
    DWORD*   pcbItems)
{
    PJOBMAP pjPtr = NULL;
    DWORD   idx = 0;

    semCheckCrit();

    *pcbItems = 0;
    if (pjmList) {
    
        for (pjPtr = *pjmList; pjPtr; ) {

            if (pjm_IsLocalJob (pjPtr)) {

                idx++;

                *pcbItems += sizeof (JOB_INFO_2) +
                             utlStrSize (pjPtr->lpszDocName) +
                             utlStrSize (pjPtr->lpszUser);
            }

            // Next item.
            //
            pjPtr = pjPtr->pNext;

        }
    }

    return idx;
}

/*****************************************************************************\
* pjmFind
*
* Locate job-entry from list.  Access to this list needs ownership of the
* crit-sect.  This will lookup either Local/Remote id's and return its
* position.
*
\*****************************************************************************/
PJOBMAP pjmFind(
    PJOBMAP* pjmList,
    DWORD    fType,
    DWORD    idJob)
{
    PJOBMAP pjPtr = NULL;
    DWORD   idx;

    semCheckCrit();

    if (pjmList) {
    
        // Search.
        //
        for (pjPtr = *pjmList, idx = 0; pjPtr && (pjmJobId(pjPtr, fType) != idJob); ) {
    
            // Next item.
            //
            pjPtr = pjPtr->pNext;
    
    
            // If we've iterated 1000 jobs in this list, then we've
            // either got a very heavily taxed print-system, or we
            // may have an infinite loop.
            //
            if (idx++ >= g_dwJobLimit) {
    
                DBG_MSG(DBG_LEV_ERROR, (TEXT("Error: pjmFind : Looped 1000 jobs, possible infinite loop")));
    
                return NULL;
            }
        }

        if (pjPtr && fType == PJM_REMOTEID) {
            pjPtr->bRemoteJob = TRUE;
        }

    }

    return pjPtr;
}

/*****************************************************************************\
* pjmNextLocalJob
*
* Walk the list and look for any expired entries.  Access to this list needs
* ownership of the crit-sect.
*
\*****************************************************************************/
PJOBMAP pjmNextLocalJob(
    PJOBMAP*    pjmList,
    PJOB_INFO_2 pJobInfo2,
    PBOOL       pbFound)
{
    PJOBMAP pjPtr = NULL;
    DWORD   idx;
    BOOL    bFound = FALSE;

    semCheckCrit();

    if (pjmList) {
    
        for (pjPtr = *pjmList, idx = 0; pjPtr && !bFound; ) {
    
            // If we're spooling, then we can't be an old-job.
            //
            if (pjm_IsLocalJob (pjPtr)) {

                // It is a new entry
                //
                ZeroMemory (pJobInfo2, sizeof (JOB_INFO_2));
                pJobInfo2->JobId        = pjPtr->idJobLocal;
                pJobInfo2->pDocument    = pjPtr->lpszDocName;
                pJobInfo2->pUserName    = pjPtr->lpszUser;
                pJobInfo2->Size         = pjPtr->dwLocalJobSize;
                pJobInfo2->Submitted    = pjPtr->SubmitTime;
                pJobInfo2->Status       = pjPtr->dwStatus;

                if (pjmChkState (pjPtr, PJM_CANCEL)) {
                    pJobInfo2->Status |= JOB_STATUS_DELETING;
                }

                bFound = TRUE;
            }
            
            // Next item.
            //
            pjPtr = pjPtr->pNext;
    
        }
    }

    *pbFound = bFound;
    return pjPtr;
}

/*****************************************************************************\
* pjmRmoveOldEntries
*
* Walk the list and look for any expired entries.  Access to this list needs
* ownership of the crit-sect.
*
\*****************************************************************************/
VOID pjmRemoveOldEntries(
    PJOBMAP      *pjmList)
{
    PJOBMAP pjPtr;
    PJOBMAP pjDel;
    PJOBMAP pjPrv;
    DWORD   idx;

    semCheckCrit();


    for (pjPtr = *pjmList, pjPrv = *pjmList, idx = 0; pjPtr; ) {

        // If we're an old entry, then delete it.
        //
        if (pjm_IsOldEntry (pjPtr)) {

            // No remote job ID and the state is not spooling
            // It is an old entry, delete it

            pjDel = pjPtr;

            if (pjPtr == *pjmList) {

                *pjmList = pjPtr->pNext;
                pjPtr    = *pjmList;
                pjPrv    = *pjmList;

            } else {

                pjPrv->pNext = pjPtr->pNext;
                pjPtr        = pjPrv->pNext;
            }

            pjm_DelEntry(pjDel);


        } else {

            pjPrv = pjPtr;
            pjPtr = pjPtr->pNext;
        }


        // If we've iterated 1000 jobs in this list, then we've
        // either got a very heavily taxed print-system, or we
        // may have an infinite loop.
        //
        if (idx++ >= g_dwJobLimit) {

            DBG_MSG(DBG_LEV_ERROR, (TEXT("Warn : pjmWalk : Looped 1000 jobs, possible infinite loop")));

            return;
        }
    }
}




/*****************************************************************************\
* pjmDelList
*
*
\*****************************************************************************/
VOID pjmDelList(
    PJOBMAP pjmList)
{
    PJOBMAP pjPtr;

    semCheckCrit();


    for (pjPtr = pjmList; pjPtr; ) {

        pjmList = pjPtr->pNext;

        pjm_DelEntry(pjPtr);

        pjPtr = pjmList;
    }
}


/*****************************************************************************\
* pjmSetState
*
* Set the state of the job-entry.  If we're setting this to spooling-state,
* then we go through the extra-trouble of setting up a spool-file entry as
* well.
*
\*****************************************************************************/
BOOL pjmSetState(
    PJOBMAP pjm,
    DWORD   dwState)
{
    DWORD fType;
    BOOL  bSet = FALSE;

    semCheckCrit();


    if (pjm) {

        // If the caller is setting the job into spooling-state, then
        // we need to create a spool-file.  If this state is already
        // set, then ignore this particular call.
        //
        if ((dwState & PJM_SPOOLING) && !pjmChkState(pjm, PJM_SPOOLING)) {

            // Determine the type of spl-file to open.  If PJM_NOOPEN
            // is specified, then we will close the file-handle so that
            // other processes can obtain exclusive access (i.e. AddJob,
            // and ScheduleJob require this).
            //
            fType = (dwState & PJM_NOOPEN ? SPLFILE_TMP : SPLFILE_SPL);


            // Allocate the spool-file, and close if necessary.
            //
            if (pjm->hSplFile = SplCreate(pjm->idJobLocal, fType)) {

                if (dwState & PJM_NOOPEN)
                    SplClose(pjm->hSplFile);

                bSet = TRUE;

            } else {

                goto EndSet;
            }

        } else {

            bSet = TRUE;
        }


        // Set the state.
        //
        pjm->dwState |= dwState;
    }

EndSet:

    return bSet;
}


/*****************************************************************************\
* pjmClrState
*
* Clear the state specified in the job-entry.  If we're turning of spooling,
* then we need to free up the spool-file-object.
*
\*****************************************************************************/
VOID pjmClrState(
    PJOBMAP pjm,
    DWORD   dwState)
{
    semCheckCrit();


    if (pjm) {

        // If the caller is turning off spooling, then we need to
        // clean our spool-file resources.  If there is no spooling
        // going on, then ignore.
        //
        if ((dwState & PJM_SPOOLING) && pjmChkState(pjm, PJM_SPOOLING)) {

            // Clean our spooling-file resources.
            //
            SplFree(pjm->hSplFile);
            pjm->hSplFile = NULL;
        }


        // Clear the state.
        //
        pjm->dwState &= ~dwState;
    }
}


/*****************************************************************************\
* pjmSetJobRemote
*
* Set the remote-job-id into the job-entry.  This is usually called once
* the job-id is obtained from the print-server.  We will maintain this for
* local/remote job-mapping.
*
\*****************************************************************************/
VOID pjmSetJobRemote(
    PJOBMAP pjm,
    DWORD   idJobRemote,
    LPCTSTR lpszUri)
{
    semCheckCrit();

    if (pjm) {

        // If we had a previous job-url, then we need to free it
        // before reseting it with the new.
        //
        if (pjm->lpszUri)
            memFreeStr(pjm->lpszUri);


        // Store remote-job information.
        //
        pjm->idJobRemote = idJobRemote;
        pjm->lpszUri     = memAllocStr(lpszUri);
        pjm->bRemoteJob  = TRUE;
    }
}

/*****************************************************************************\
* pjmAddJobSize
*
* Add the job size to the local job info. This is usually called by
* PPWritePrinter 
*
\*****************************************************************************/
VOID pjmAddJobSize(
    PJOBMAP pjm,
    DWORD   dwSize)
{
    semCheckCrit();

    if (pjm) {

        pjm->dwLocalJobSize += dwSize;
    }
}



/*****************************************************************************\
* pjmSplLock
*
* Lock the spool-file for reading.  This returns a file-map pointer to the
* caller.
*
\*****************************************************************************/
CFileStream* pjmSplLock(
    PJOBMAP pjm)
{
    CFileStream *pStream = NULL;

    semCheckCrit();

    if (pjm) {

        // If the state of our spool-object requires that we not
        // keep open-handles on the file, then we need to open
        // it here.  This is closed at pjmSplUnlock().
        //
        if (pjmChkState(pjm, PJM_NOOPEN))
            SplOpen(pjm->hSplFile);

        pStream = SplLock(pjm->hSplFile);
    }

    return pStream;
}


/*****************************************************************************\
* pjmSplUnlock
*
* This unlocks our file-mapped-pointer on the spool-file.
*
\*****************************************************************************/
BOOL pjmSplUnlock(
    PJOBMAP pjm)
{
    BOOL bRet = FALSE;

    semCheckCrit();


    // The spool-file must have already been locked in order to proceed
    // with this call.
    //
    if (pjm) {

        // Unlock the spool-file.
        //
        bRet = SplUnlock(pjm->hSplFile);


        // If the state of our spool-object requires that
        // we keep no open-handles, then we need to close
        // the spool-handle.
        //
        if (pjmChkState(pjm, PJM_NOOPEN))
            SplClose(pjm->hSplFile);
    }

    return bRet;
}


/*****************************************************************************\
* pjmSplWrite
*
* Write out data to the spool-file.
*
\*****************************************************************************/
BOOL pjmSplWrite(
    PJOBMAP pjm,
    LPVOID  lpMem,
    DWORD   cbMem,
    LPDWORD lpcbWr)
{
    BOOL bRet = FALSE;

    semCheckCrit();


    if (pjm) {

        if (pjmChkState(pjm, PJM_NOOPEN))
            SplOpen(pjm->hSplFile);

        bRet = SplWrite(pjm->hSplFile, (LPBYTE) lpMem, cbMem, lpcbWr);

        if (pjmChkState(pjm, PJM_NOOPEN))
            SplClose(pjm->hSplFile);
    }

    return bRet;
}

/*****************************************************************************\
* pjmSetState
*
* Set the status of the local job-entry. 
*
\*****************************************************************************/
VOID pjmUpdateLocalJobStatus(
    PJOBMAP pjm,
    DWORD   dwStatus)
{
    semCheckCrit();


    if (pjm) {

        pjm->dwStatus = dwStatus;
    }

}