//Ian Copland 0601545
//*****************************************************
//     Computer Operating Enviroments Coursework
//*****************************************************
//This program Emulates the way memory management works,
//giving a text display, and the option to pick either
//FIFO or LRU conditions for the management.
//you also get various other options.
//*****************************************************
//						Versions
//v0.5 - FIFO working, display is basic.
//v0.6 - Improvements to the display, as well as several
//		 Bug Fixes, such as an error that meant that 
//		 every time a empty page was filled it filled
//		 them all with the same page.
//v1.0 - LRU working, improvements to the display, now
//		 has user input implimented, seems to be finished.
//v1.1 - Fixed a bug that meant if there was only 1 process
//		 running and it died, the program would crash.


#include <windows.h>
#include <process.h>
#include <string.h>
#include <stdio.h>
#include <list>
#include <iostream>
using namespace std;

#define ACTIVE (0)
#define PAUSED (1)
#define END (2)
#define NO_OF_PAGES (10)
#define CHANCE_OF_PROCESS (2000)

//class for holding the pages
class Page
{
public:
	int PID;				//the ID of the process in this page
	int Number;				//the page number within the process.
	int LastUsed;			//the time this page was last used
	int CreatedAt;			//the time this page was created
	Page(){
		PID=-1;
		Number=0;
		LastUsed=0;
		CreatedAt=0;
	}
protected:
};
//and a process class
class Process
{
public:
	int ID;					//the ID of this process
	int LifeSpan;			//the time from creation til it ends
	int Size;				//the size in bytes of the process
	int LastAccessed;		//the last time this process was accessed
protected:
};

//create a list in which to store the currently running processes
list<Process> ProcessList;
list<Process>::iterator PIterator;
//create an array of Pages, this is the physical memory pagetable.
Page PageTable[NO_OF_PAGES];
//First in Last Out, or Last Reciently Used
bool FIFO_or_LRU = 0;
//number of page faults
int PageFaults = 0;
//array of bools for holding the key values
bool keys[255];
//max number of processes
int MaxProcesses=10;
//semaphore variables
HANDLE ProcessListInUse;
//holds how many references have been made, so as to give each "time" variable a unique value
long int ReferenceNo = 0;
//thread structure
typedef struct threadCB
{
	int state; // 0 active; 1 pause; 2 end 
	int id;
} threadCB;

static threadCB Scheduler = {PAUSED, 1};
static threadCB ProcessControler = {PAUSED, 2};
static threadCB DisplayControler = {ACTIVE, 3};

//function for creating and killing processes
VOID ProcessControl(PVOID pvoid);
//function for managing memory
VOID MemoryControl(PVOID pvoid);
//function for managing the Display
VOID Display(PVOID pvoid);

CRITICAL_SECTION updatenHits;

double nHits = 0.0;

HWND gHwnd;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
VOID doit(PVOID pvoid);

void RegisterMyWindow(HINSTANCE hInstance)
{
    WNDCLASSEX  wcex;									

    wcex.cbSize        = sizeof (wcex);				
    wcex.style         = CS_HREDRAW | CS_VREDRAW;		
    wcex.lpfnWndProc   = WndProc;						
    wcex.cbClsExtra    = 0;								
    wcex.cbWndExtra    = 0;								
    wcex.hInstance     = hInstance;						
    wcex.hIcon         = 0; 
    wcex.hCursor       = LoadCursor (NULL, IDC_ARROW);	
															
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
    wcex.lpszMenuName  = (LPCSTR) NULL;
    wcex.lpszClassName = "WindowClass";				
    wcex.hIconSm       = 0; 

	RegisterClassEx (&wcex);							

}

BOOL InitialiseWindow(HINSTANCE hInstance, int nCmdShow)
{

   gHwnd = CreateWindow ("WindowClass",					
						 "Window",		  			
						 WS_OVERLAPPEDWINDOW,				
						 CW_USEDEFAULT,						
						 CW_USEDEFAULT,						
						 CW_USEDEFAULT,						
						 CW_USEDEFAULT,					    
						 NULL,								
						 NULL,								
						 hInstance,						    
						 NULL);								
	if (!gHwnd)
	{
		return FALSE;
	}
    ShowWindow (gHwnd, nCmdShow);							
    UpdateWindow (gHwnd);									
	return TRUE;

}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int nCmdShow)			
    {														
	ProcessListInUse = CreateSemaphore(NULL,  1L, 1L, NULL);									
    MSG         msg;										

	RegisterMyWindow(hInstance);

    if (!InitialiseWindow(hInstance, nCmdShow))
		return FALSE;
	
  	while (GetMessage(&msg,NULL,0,0))					
		{		
		  TranslateMessage (&msg);							
		  DispatchMessage (&msg);								
		}
    
	return msg.wParam ;										
}
															
															
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    switch (message)											
    {	
    case WM_COMMAND:
	break;
			
		case WM_CREATE:		
			InitializeCriticalSection(&updatenHits);
			_beginthread(ProcessControl, 0, (PVOID) &ProcessControler);
			_beginthread(Display, 0, (PVOID) &DisplayControler);
			_beginthread(MemoryControl, 0, (PVOID) &Scheduler);

			break;
		case WM_KEYDOWN:
			keys[wParam]=true;
			//switch on FIFO
			if (keys['F']==true)
				FIFO_or_LRU = 0;
			//switch on LRU
			if (keys['L']==true)
				FIFO_or_LRU = 1;
			//Begin
			if (keys[' ']==true){
				Scheduler.state=ACTIVE;
				ProcessControler.state=ACTIVE;
			}
			if (keys[VK_UP]==true && MaxProcesses<NO_OF_PAGES)
				MaxProcesses++;
			if (keys[VK_DOWN]==true && MaxProcesses>1)
				MaxProcesses--;

			break;

		case WM_KEYUP:
			keys[wParam]=false;
			break;

		case WM_SIZE:
			
			break;	

		case WM_PAINT:
			
		    break;		

		case WM_DESTROY:	
			DeleteCriticalSection(&updatenHits);
			PostQuitMessage(0);	
								
			break;				
	}													

	return DefWindowProc (hwnd, message, wParam, lParam);		
															
}	

VOID MemoryControl(PVOID pvoid){
	static int memoryReference;
	static int i;
	static bool found;
	static long int LargestTime;
	static long int SmallestTime;
	static int PageToBeMoved;
	while (((threadCB *) pvoid) -> state != END)		//keep looping until the thread has been ended
	{
		 if (((threadCB *) pvoid)-> state != PAUSED)	//only perform actions if the thread isnt paused
		 {
			WaitForSingleObject(ProcessListInUse,INFINITE);	//stops the other threads accessing memory while this one is doing stuff
			for(PIterator=ProcessList.begin();PIterator!=ProcessList.end();PIterator++){//loop thru all the processes
				found = false;									//tell the thread that the page has not been found
				LargestTime = 0;								//resets the largest time to 0
				SmallestTime = 9999999999;						//resets the smallest time to 0
				memoryReference = (int)((rand()%PIterator->Size)/2000);	//find what page the process wants within its 
																		//allocated memory.
				//search through the pagetable to see if the actual page the process wants is there
				for (i=0;i<NO_OF_PAGES;i++){
					//find the pages within the PageTable that has been allocated to the process
					if (PageTable[i].PID== PIterator->ID){
						//check if this page is the one the process is after
						if (PageTable[i].Number == memoryReference){
							found = true;
							PageTable[i].LastUsed = ReferenceNo++;//stores when it was last used
						}
					}
				}
				//if found is still 0 then the page the process wants isnt in physical memory
				if (found == 0){
					//first of all check if theres a free space
					for (i=0;i<NO_OF_PAGES;i++){
						if (PageTable[i].PID== -1 && found ==0){	//if its -1 then theres no process using it
							PageTable[i].PID= PIterator->ID;		//set the ProcessID of this page to that of the process
							PageTable[i].CreatedAt = ReferenceNo;	//stores when the the page was made
							PageTable[i].LastUsed = ReferenceNo++;	//stores when it was last used
							PageTable[i].Number = memoryReference;	//sets which page within the memory the process has access
																	//to this is
							found = true;
						}
					}
				}
				//if found is still 0 then there are no empty pages in memory
				//if its FIFO:
				if (found == 0 && FIFO_or_LRU == 0){
					//therefor find the page that should be taken out to put it in...
					for (i=0;i<NO_OF_PAGES;i++){
						if (ReferenceNo - PageTable[i].CreatedAt > LargestTime){
							LargestTime = ReferenceNo - PageTable[i].CreatedAt;
							PageToBeMoved = i;
						}
					}
					//increase the Page Fault counter
					PageFaults++;
					//once the page to be moved is found, move it
					PageTable[PageToBeMoved].PID= PIterator->ID;		//set the ProcessID of this page to that of the process
					PageTable[PageToBeMoved].CreatedAt = ReferenceNo;	//stores when the the page was made
					PageTable[PageToBeMoved].LastUsed = ReferenceNo++;	//stores when it was last used
					PageTable[PageToBeMoved].Number = memoryReference;	//sets which page within the memory the process has access
																			//to this is
				}
				//if its LRU:
				if (found == 0 && FIFO_or_LRU == 1){
					//therefor find the page that should be taken out to put it in...
					for (i=0;i<NO_OF_PAGES;i++){
						if (PageTable[i].LastUsed < SmallestTime){
							SmallestTime = PageTable[i].LastUsed;
							PageToBeMoved = i;
						}
					}
					//increase the Page Fault counter
					PageFaults++;
					//once the page to be moved is found, move it
					PageTable[PageToBeMoved].PID= PIterator->ID;		//set the ProcessID of this page to that of the process
					PageTable[PageToBeMoved].CreatedAt = ReferenceNo;	//stores when the the page was made
					PageTable[PageToBeMoved].LastUsed = ReferenceNo++;	//stores when it was last used
					PageTable[PageToBeMoved].Number = memoryReference;	//sets which page within the memory the process has access
																		//to this is
				}
			}
			//slow down the program if S is pressed
			if (keys['S']==true)
				Sleep(1000);
			ReleaseSemaphore(ProcessListInUse,1,NULL);//allows the other threads access to memory again
		 }
	}
	_endthread();//end the thread 
}


VOID ProcessControl(PVOID pvoid){
	static int ProcessID = 0;
	Process NewProcess;
	while (((threadCB *) pvoid) -> state != END)		//keep looping until the thread has been ended
	{
		 if (((threadCB *) pvoid)-> state != PAUSED)	//only perform actions if the thread isnt paused
		 {
			WaitForSingleObject(ProcessListInUse,INFINITE);	//stops the other threads accessing memory while this one is doing stuff
														//loop through all the elements in the ProcessList
			for(PIterator=ProcessList.begin();PIterator!=ProcessList.end();PIterator++){//loop thru all the processes
				if (--(PIterator->LifeSpan)<1){			//if the process has died
					if (++PIterator!=ProcessList.end()){//if its not the last Process in the list
						PIterator--;					
						PIterator = ProcessList.erase(PIterator);//Delete the Entry from the list and set the Iterator
														//to the next position
					}else if (--PIterator!=ProcessList.begin()){//if theres not only 1 process
						PIterator = ProcessList.erase(PIterator);
						PIterator--;
					}else{
						PIterator = ProcessList.erase(PIterator); break;
					}
				}
			}


			//if the ProcessList is less than NO_OF_PAGES then theres a chance to add annother process
			if (ProcessList.size()<MaxProcesses){
				// 1:CHANCE_OF_PROCESS chance of a process being added
				if (rand()%CHANCE_OF_PROCESS == 0){
					//create a new process
					NewProcess.ID = ProcessID++;
					NewProcess.LifeSpan = 10000+rand()%90000;
					NewProcess.Size = 5000 +rand()%25000;
					ProcessList.push_back(NewProcess);
				}
			}
			ReleaseSemaphore(ProcessListInUse,1,NULL);//allows the other threads access to memory again
		 }
	}
   _endthread();//end the thread

}

	 
VOID Display(PVOID pvoid){
	HDC hdc;
	int n = 0;
	int i;
	int PercentFaults;
	int xpos,ypos;
	hdc = GetDC(gHwnd);
	char displayString[80];

	while (((threadCB *) pvoid) -> state != END)		//keep looping until the thread has been ended
	{
		 if (((threadCB *) pvoid)-> state != PAUSED)	//only perform actions if the thread isnt paused
		 {
			hdc = GetDC(gHwnd);
			
			WaitForSingleObject(ProcessListInUse,INFINITE);	//stops the other threads accessing memory while this one is doing stuff
			xpos = ypos = 1;
			//display the page table
			sprintf(displayString,"Page Table");
			TextOut(hdc, 420, 0, displayString, strlen(displayString));
			for (i=0;i<NO_OF_PAGES;i++){//loop thru all the pages
				//display the page
				sprintf(displayString,"Page: %d",i);
				TextOut(hdc, 20*xpos, 20*(ypos++), displayString, strlen(displayString));
				//display the ProcessID
				sprintf(displayString,"Process ID: %d    ",PageTable[i].PID);
				TextOut(hdc, 20*xpos, 20*(ypos++), displayString, strlen(displayString));
				//display the Number
				sprintf(displayString,"Number: %d     ",PageTable[i].Number);
				TextOut(hdc, 20*xpos, 20*(ypos++), displayString, strlen(displayString));
				//display the Creation Time
				sprintf(displayString,"Created: %d    ",PageTable[i].CreatedAt);
				TextOut(hdc, 20*xpos, 20*(ypos++), displayString, strlen(displayString));
				//display the Time last used
				sprintf(displayString,"Last Used: %d     ",PageTable[i].LastUsed);
				TextOut(hdc, 20*xpos, 20*(ypos++), displayString, strlen(displayString));
				//reset the x and y coords of the text to arrange it in a table
				ypos-=5;
				xpos+=10;
				if (xpos>50){ypos= 7;xpos = 1;}
			}
			//draw the process list
			sprintf(displayString,"Process List");
			TextOut(hdc, 120, 260, displayString, strlen(displayString));
			//draw the process list headings
			sprintf(displayString,"Process ID");
			TextOut(hdc, 20, 280, displayString, strlen(displayString));
			sprintf(displayString,"Size");
			TextOut(hdc, 120, 280, displayString, strlen(displayString));
			sprintf(displayString,"Lifespan");
			TextOut(hdc, 220, 280, displayString, strlen(displayString));

			ypos=15;
			xpos=1;
			for(PIterator=ProcessList.begin();PIterator!=ProcessList.end();PIterator++){//loop thru all the processes
				//display the Process ID
				sprintf(displayString,"%d     ",PIterator->ID);
				TextOut(hdc, 20*(xpos), 20*(ypos), displayString, strlen(displayString));
				//display the Process Size in Memory
				sprintf(displayString,"%dBytes     ",PIterator->Size);
				TextOut(hdc, 20*(xpos+=5), 20*(ypos), displayString, strlen(displayString));
				//display the Processes Life
				sprintf(displayString,"%d     ",PIterator->LifeSpan);
				TextOut(hdc, 20*(xpos+=5), 20*(ypos), displayString, strlen(displayString));
				xpos=1;
				ypos++;
			}
			//clear the other lines of text by replacing them with blank strings
			for (i=0;i<(NO_OF_PAGES - ProcessList.size());i++){
				sprintf(displayString,"                       ");
				TextOut(hdc, 20*(xpos), 20*(ypos), displayString, strlen(displayString));
				sprintf(displayString,"                       ");
				TextOut(hdc, 20*(xpos+=5), 20*(ypos), displayString, strlen(displayString));
				sprintf(displayString,"                       ");
				TextOut(hdc, 20*(xpos+=5), 20*(ypos), displayString, strlen(displayString));
				xpos=1;
				ypos++;
			}
			//display some statistics about page faults etc...
			//no of Refereneces
			sprintf(displayString,"No of Memory References: %d", ReferenceNo);
			TextOut(hdc, 400, 320, displayString, strlen(displayString));
			//no of page faults
			sprintf(displayString,"No of PageFaults: %d", PageFaults);
			TextOut(hdc, 400, 340, displayString, strlen(displayString));
			//% of faults
			//calc % of faults
			if (PageFaults > 0)
				PercentFaults  = (int)(100*((float)PageFaults/(float)ReferenceNo));
			else
				PercentFaults = 0;
			sprintf(displayString,"Percentage of Faults: %d%%                      ", PercentFaults);
			TextOut(hdc, 400, 360, displayString, strlen(displayString));
			//Mode
			sprintf(displayString,((FIFO_or_LRU ==0) ? "Mode: FIFO " : "Mode: LRU "));
			TextOut(hdc, 400, 380, displayString, strlen(displayString));
			//Max Processes
			sprintf(displayString,"Max Processes: %d    ", MaxProcesses);
			TextOut(hdc, 400, 400, displayString, strlen(displayString));
			
			ReleaseSemaphore(ProcessListInUse,1,NULL);//allows the other threads access to memory again

			//display Controls
			//Controls
			sprintf(displayString,"Controls: ");
			TextOut(hdc, 700, 320, displayString, strlen(displayString));
			//S
			sprintf(displayString,"Press S to slow the program down");
			TextOut(hdc, 700, 340, displayString, strlen(displayString));
			//F
			sprintf(displayString,"Press F to set to FIFO");
			TextOut(hdc, 700, 360, displayString, strlen(displayString));
			//L
			sprintf(displayString,"Press L to set to LRU");
			TextOut(hdc, 700, 380, displayString, strlen(displayString));
			//Space
			sprintf(displayString,"Press SPACE to begin");
			TextOut(hdc, 700, 400, displayString, strlen(displayString));
			//UP
			sprintf(displayString,"Press Up to increase Processes");
			TextOut(hdc, 700, 420, displayString, strlen(displayString));
			//Down
			sprintf(displayString,"Press Down to decrease Processes");
			TextOut(hdc, 700, 440, displayString, strlen(displayString));
	 
		    ReleaseDC(gHwnd, hdc);
			Sleep(100);
		 }
	 }
   _endthread();//end the thread

}
