//----------------------------------------------------------------------
//  memmgr.cpp
//  This program simulates an operating system memory manager.  Actions
//  are specified by user commands from standard input.
//----------------------------------------------------------------------
#include "uqueue.h"     // For IntQueue class
#include <iostream.h>
#include <ctype.h>      // For toupper()

void ReadCommand( char& );
void AddJob( int&, int&, IntQueue&, int[] );
void LoadJob( int, int[] );
void ReadJobID( int& );
void TerminateJob( int, IntQueue&, int[], int& );
void DisplayInfo( int, IntQueue, const int[] );

const int NONE = -1;

int main()
{
    IntQueue jobQ;
    int      partition[3] = {NONE, NONE, NONE};
    int      jobCount = 0;
    int      jobID = 0;
    int      jobToTerminate;
    char     command;

    do {
        ReadCommand(command);
        switch (command) {
            case 'A':
                AddJob(jobID, jobCount, jobQ, partition);
                break;
            case 'T':
                ReadJobID(jobToTerminate);
                TerminateJob(jobToTerminate, jobQ, partition, jobCount);
                break;
            case 'D':
                DisplayInfo(jobCount, jobQ, partition);
                break;
            default:
                ;
        }
    } while (command != 'Q');

    return 0;
}

void ReadCommand( /* out */ char& command )
    //..................................................................
    // POST: User has been prompted for a single char
    //    && command == uppercase equivalent of that char
    //..................................................................
{
    cout << "\nA)dd job  T)erminate job  D)isplay info  Q)uit: ";
    cin >> command;
    command = toupper(command);
}

void AddJob( /* inout */ int&      jobID,
             /* inout */ int&      jobCount,
             /* inout */ IntQueue& jobQ,
             /* inout */ int       partition[] )
    //..................................................................
    // PRE:  All parameters assigned
    // POST: IF jobQ<entry> is full THEN
    //            Error message displayed  &&  All parameters are unchanged
    //       ELSE
    //            jobID == jobID<entry>+1  &&  jobID displayed to user
    //         && (jobCount<entry> < 3 ) --> job loaded into memory
    //         && (jobCount<entry> >= 3 ) --> jobID enqueued in jobQ
    //         && jobCount == jobCount<entry> + 1
    //..................................................................
{
    if (jobQ.IsFull()) {
        cout << "NO ROOM FOR THIS JOB\n";
        return;
    }
    jobID++;
    cout << "Job ID: " << jobID << '\n';
    if (jobCount < 3)
        LoadJob(jobID, partition);
    else
        jobQ.Enqueue(jobID);
    jobCount++;
}

void LoadJob( /* in */    int jobID,
              /* inout */ int partition[] )
    //..................................................................
    // PRE:  All parameters assigned
    //    && For some i, partition[i] == NONE
    // POST: For some i,
    //       partition[i]<entry> == NONE  &&  partition[i] == jobID
    //..................................................................
{
    int i = 0;
    while (i < 3 && partition[i] != NONE)
                                // INV (prior to test):
                                //     Partitions 0 thru i-1 are occupied
                                //  && 0 <= i <= 3
        i++;

    // ASSERT: Partition i is vacant
    partition[i] = jobID;
}

void ReadJobID( /* out */ int& jobToTerminate )
    //..................................................................
    // POST: jobToTerminate == integer prompted for and input from user
    //..................................................................
{
    cout << "Job ID: ";
    cin >> jobToTerminate;
}

void TerminateJob( /* in */    int       jobToTerminate,
                   /* inout */ IntQueue& jobQ,
                   /* inout */ int       partition[],
                   /* inout */ int&      jobCount       )
    //..................................................................
    // PRE:  All parameters assigned
    // POST: IF for some i, partition[i]<entry> == jobToTerminate THEN
    //             jobCount == jobCount<entry> - 1
    //          && IF jobQ<entry> is not empty THEN
    //                 Next job in jobQ loaded into memory
    //             ELSE
    //                 partition[i] == NONE
    //       ELSE
    //          Error message displayed && All parameters are unchanged
    //..................................................................
{
    int i = 0;
    while (i < 3 && partition[i] != jobToTerminate)
                       // INV (prior to test):
                       //     jobToTerminate is not in partition[0..i-1]
                       //  && 0 <= i <= 3
        i++;

    if (i == 3) {
        cout << "THIS JOB NOT IN MEMORY\n";
        return;
    }
    jobCount--;
    partition[i] = NONE;
    if ( !jobQ.IsEmpty() ) {
        int nextJob = jobQ.Front();
        jobQ.Dequeue();
        LoadJob(nextJob, partition);
    }
}

void DisplayInfo( /* in */ int       jobCount,
                  /* in */ IntQueue  q,
                  /* in */ const int partition[] )
    //..................................................................
    // PRE:  All parameters assigned
    // POST: Contents of jobCount, q, and partition displayed
    //..................................................................
{
    cout << "\n     No. of jobs in system: " << jobCount << '\n';
    cout << "     Job queue:   ";
    if (q.IsEmpty())
        cout << "EMPTY";
    else
        do {
            cout << q.Front() << ' ';
            q.Dequeue();
                // INV: All values prior to q.Front() have been displayed
        } while ( !q.IsEmpty() );
    cout << '\n';

    int i;
    for (i = 0; i < 3; i++) {
        cout << "     Partition " << i << ": ";
        if (partition[i] == NONE)
            cout << "NONE\n";
        else
            cout << "Job " << partition[i] << '\n';
    }
}


