//--------------------------------
// ClockBuffer - emulator of the Clock family of buffer management algorithms
//--------------------------------
// Alexander Dekhtyar, 2006
//
//---------------------------------------------------------------


public class ClockBuffer {

	public String pageIds[];
	public int counters[];
	public int pin[];
	public int ready[];
	public int hand;
	public int bufferSize;
	
	public static final int PINNED = 1;
	public static final int UNPINNED = 0;
	public static final int READY = 1;
	public static final int NOT_READY = 0;
	public static final int FREE = -1;
	public static final int NOT_FOUND = -1;
	
	
// clock buffer constructor prepares an empty buffer of given size

	public ClockBuffer(int size)
	{
		pageIds = new String[size+1];
		counters= new int[size+1];
		pin = new int[size+1];
		ready= new int[size+1];
		
		for (int i=1; i<=size; i++)
		{
			pin[i]=UNPINNED;
			ready[i] = FREE;
            counters[i]= 0;
            pageIds[i]= "";
		}
		
		hand = 1;		
		bufferSize=size;
	}
	


// moveHand(): the heart of the clock algorithm, controls the
// movement of the clock hand to the next position.

	public void moveHand()
	{
		counters[hand]--;
		do
		 { hand = hand+1;
	   	   if (hand > bufferSize) {hand=1;}		
		 } while (pin[hand]== PINNED);
		
		 }
	

// print(): outputs the current state of the buffer

	
	public void print()
	{ 
		
		System.out.println();
		System.out.println("              PageId     Counter     Pin    Ready");
		
		for (int i = 1; i<= bufferSize; i++)
		{
			if (i == hand) {System.out.print("--->");}
			else {System.out.print("    ");}			
			System.out.print("Slot ");
			System.out.print(i);
			System.out.print(": ");
			System.out.print(pageIds[i]);
			System.out.print("    ");
			System.out.print(counters[i]);
			if (pin[i]== PINNED) {System.out.print("  PINNED   ");}
			else {System.out.print("  NOT PINNED");}
			
			if (ready[i]== FREE) {System.out.print("    FREE     ");}
			else if (ready[i]== READY) {System.out.print("    READY    ");}
			else {System.out.print("    NOT READY");}
			
			System.out.println();							
			}
		System.out.println();
		}
	

// seek(): determines with the buffer contains a given page

	public int seek(String pageId)
	{
	   for (int i=1; i<= bufferSize; i++)
	   {
		  if (pageIds[i].equals(pageId))
		  {
			return(i);  
		  }		  
	   }
		return(NOT_FOUND);	   
	}
	
	
// findFree(): finds the location of a free slot in the buffer, if
// one exists


	public int findFree()
	{
		 for (int i=1; i<= bufferSize; i++)
		   {
			  if (ready[i]== FREE)
			  {
				return(i);  
			  }		  
		   }
			return(NOT_FOUND);	  
	}
	
// findReady(): finds the location of a slot ready to be flushed,
// if one exists in the buffer


	public int findReady()
	{
		 for (int i=1; i<= bufferSize; i++)
		   {
			  if (ready[i]== READY)
			  {
				return(i);  
			  }		  
		   }
			return(NOT_FOUND);	  
	}
	
// read(): implements the read command. pageID: the id of the page
// to be stored, counter - the value of the counter

	public void read(String pageId, int counter)
	{
		
		int idx = seek(pageId);
		if (idx != NOT_FOUND) return;
		
	    idx = findFree();
		if (idx == NOT_FOUND) {idx = findReady();}
		if (idx == NOT_FOUND) 
		{
			int success = 0;
			while (success==0)
			{
				if (counters[hand]>0)
				{
					moveHand();					
				}
				else {success=1;
          	  		  idx = hand;
          	  		  moveHand();}
			}			
		} // if
		
		pageIds[idx]= pageId;
		counters[idx]= counter;		
		pin[idx]= UNPINNED;
		ready[idx]= NOT_READY;
	}
	
		
// write(): implements the write command
	
	public void write(String pageId)
	{
		int idx = seek(pageId);
		if (idx == NOT_FOUND) 
		{
			read(pageId, 1);
			write(pageId);
			return;
		}
		else
		{
			ready[idx]= READY;
		}
		
	}
	
// flush(): implements the flush() command

	public void flush(String pageId)
	{
	   int idx = seek(pageId);
	   if (idx == NOT_FOUND) {return;}
	   else
	   {
		  if (pin[idx]== UNPINNED) 
		  { pageIds[idx]="";
		   counters[idx]= 0;
		   ready[idx] = FREE;
		   pin[idx] = UNPINNED;
		  }
	   }
	
	}
	
	
// pinSlot(): implements the pin command

	public void pinSlot(String pageId)
	{
		int idx = seek(pageId);
		
		if (idx == NOT_FOUND)
		{
			read(pageId,1);
			pinSlot(pageId);			
		}
		else
		{
			pin[idx]= PINNED;			
		}		
	}


// unpinSlot(): implements the unpin command.	

	public void unpinSlot(String pageId)
	{
		int idx = seek(pageId);
		
		pin[idx]= UNPINNED;			
	}
	
	
	
}