package osBridge;

import osBridge.*;

public class OsBridge{

   private boolean[] yellowQueue;   //where yellows are in the visible queue
   private boolean[] yellowBridge;  //where yellows are on the bridge
   private boolean[] yellowExit;    //where yellows are on the exit queue

   private boolean[] purpleQueue;
   private boolean[] purpleBridge;
   private boolean[] purpleExit;

   // queue for the queue
   private int pQ2 = 0;
   private int yQ2 = 0;
   // queue for the queue


   // for the threads!
   private boolean runThreads = true;
   private int pThreadSleep = 100;
   private int yThreadSleep = 100;

   private boolean useMutexes = true;
   private boolean allowStarvation = false;

   private boolean starvationNext = false;   
   private boolean yellowWaiting = true;
   private boolean purpleWaiting = true;
      // false means yellow next
	 // true means purple next

   // assignment given variables
   private boolean bridgeMutex;
   private boolean yellowMutex;
   private boolean purpleMutex;

   private int yellowCount = 0;
   private int purpleCount = 0;

   private static final int MAXHIDDENQ =2;

   // *****
   private BridgeListener bl;


   public OsBridge(int bridgeSize, int yellowQueue, int yellowExit, 
		   int purpleQueue, int purpleExit,
		   BridgeListener list){

      yellowBridge = new boolean[bridgeSize];
      purpleBridge = new boolean[bridgeSize];

      this.yellowQueue = new boolean[yellowQueue];
      this.purpleQueue = new boolean[purpleQueue];

      this.yellowExit = new boolean[yellowExit];
      this.purpleExit = new boolean[purpleExit];   

      bl = list;

      Thread pThread = new Thread( new Runnable(){

	 public void run(){
	 while(true){
	       try{
		  Thread.sleep(10);
	       }catch(Exception e){

	       }
	    while(runThreads){
	       pulsePurple();
	       pulseUpdate();
	       try{
		  Thread.sleep(pThreadSleep);
	       }catch(Exception e){

	       }

	    }
	 }
	 }

      });
	 
      Thread yThread = new Thread( new Runnable(){

	 public void run(){
	 while(true){
	       try{
		  Thread.sleep(10);
	       }catch(Exception e){

	       }
	    while(runThreads){
	       pulseYellow();
	       pulseUpdate();
	       try{
		  Thread.sleep(yThreadSleep);
	       }catch(Exception e){

	       }
	    }
	 }
	 }

      });


   yThread.start();
   pThread.start();

   }

   public synchronized void setPThreadSleep(int newVal){
      pThreadSleep = newVal;
   }

   public synchronized void setYThreadSleep(int newVal){
      yThreadSleep = newVal;
   }

   public synchronized void setThreadsRunning(boolean newVal){
      runThreads = newVal;
   }

   public synchronized void setAllowStarvation(boolean newVal){
     allowStarvation = newVal; 
   }
   
   public synchronized void setUseMutexes(boolean newVal){
      useMutexes = newVal;
   }



   public synchronized void pulseUpdate(){ // public so outside world can request it also
					   // synchronized to stop concurrent throwing of the details up
      
      boolean[] myYells = new boolean[yellowQueue.length + yellowBridge.length + yellowExit.length];
      boolean[] myPurps = new boolean[purpleQueue.length + purpleBridge.length + purpleExit.length];

      boolean[] myBridge  = new boolean[yellowBridge.length]; //arbitary choice could have used purples

      // yellow matches bridge, purple has bridge in reverse.
      // any collisions are marked in the bridge class 
      int delta;
//yellows:
      delta = 0;

      for(int i=0;i<yellowQueue.length;i++){
	 myYells[i+delta] = yellowQueue[i];
      }


      delta = yellowQueue.length;
      for(int i=0;i<yellowBridge.length;i++){
	 myYells[i+delta] = yellowBridge[i];
      }


      delta = yellowQueue.length + yellowBridge.length;
      for(int i=0;i<yellowExit.length;i++){
	 myYells[i+delta] = yellowExit[i];
      }
//purples:
      delta = 0;

      for(int i=0;i<purpleQueue.length;i++){
	 myPurps[i+delta] = purpleQueue[i];
      }


      delta = purpleQueue.length;
      for(int i=0;i<purpleBridge.length;i++){
	 myPurps[i+delta] = purpleBridge[i];
      }


      delta = purpleQueue.length + purpleBridge.length;
      for(int i=0;i<yellowExit.length;i++){
	 myPurps[i+delta] = purpleExit[i];
      }
// the bridge:
      for(int i=0;i<myBridge.length;i++){
	 if(yellowBridge[i] && purpleBridge[purpleBridge.length-i-1]){
	    myBridge[i] = true;
	 }
      }
      
      bl.update(myYells,myPurps,myBridge,bridgeMutex,yellowMutex,purpleMutex);

   }

// ****************************
// BRIDGE MUTEX CONTROL METHODS
// ****************************

   private void upBridge(){
      bridgeMutex = false;
   }

   private synchronized boolean downBridge(){
      if(bridgeMutex){
	 return false;	// you failed to get a lock
      }else{
	 bridgeMutex = true;
	 pulseUpdate();
	 return true;
      }
   }


// methods for adding / removing people to/from the invisible queue to the visible queue
   public synchronized void addPurple(){
      if(pQ2 > MAXHIDDENQ){
	 return;
      }else{
	 pQ2++;
      }
   }

   private synchronized boolean decPurple(){
      if(pQ2 > 0 ){
	 pQ2--;
	 return true;
      }else{
	 return false;
      }
   }

   public synchronized void addYellow(){
      if(yQ2 > MAXHIDDENQ){
	 return;
      }else{
	 yQ2++;	  
      }
   }

   private synchronized boolean decYellow(){
      if( yQ2 > 0 ){
	 yQ2--;
	 return true;
      }else{
	 return false;
      }
   }
// ***************************************************************************************



   public void pulsePurple(){
      pulsePurpleExit();
      pulsePurpleBridge();
      pulsePurpleQueue();
   }


   public void pulseYellow(){
      pulseYellowExit();
      pulseYellowBridge();
      pulseYellowQueue();
   }

// **********************************************************************************************
// ==============================================================================================
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

//queue works like "outside world|0123456789...bridge0123456789...end queue0123...|outisde world"

   private void pulseYellowQueue(){
	 // work from 'front' (getting onto bridge) going backwards (ppl waiting to get onto queue)


      if(yellowQueue[yellowQueue.length-1] && (!useMutexes || downBridge() ) ){// is some1 waiting to get onto bridge
							      // and can we get a lock on the bridge (if we 'have' to
	 // we now own the exclusive rights to the bridge and colour mutexes.

	 if(allowStarvation){

	    if( (!useMutexes || yellowMutex) && !yellowBridge[0]){ 
						   // see if our colour are on the bridge already and space for me
	       yellowBridge[0] = true;
	       yellowQueue[yellowQueue.length-1] = false;
	       yellowCount++;  // there's one more purple on the bridge   
	    }else if( !useMutexes || !purpleMutex ){ // if there are no yellows on the bridge
	       yellowMutex = true;
	       yellowBridge[0] = true;
	       yellowQueue[yellowQueue.length-1] = false;
	       yellowCount++;  // there's one more purple on the bridge   
	    }

	 }else{	  // we are not allowing starvation

	    if( !useMutexes || yellowMutex){

	       yellowBridge[0] = true;
	       yellowQueue[yellowQueue.length-1] = false;
	       yellowCount++;  // there's one more yellow on the bridge   
	    }else if( (!purpleWaiting || ( yellowCount==0 && !starvationNext )) && purpleCount== 0 ){
	       yellowBridge[0] = true;
	       yellowQueue[yellowQueue.length-1] = false;
	       yellowCount++;
	       yellowMutex = true;
	       starvationNext = true;

	    }

	 }

	 upBridge(); // release our lock on the bridge (ignored if we are not using BridgeMutex)
      }

      for(int i=yellowQueue.length-1;i > 0 ; i--){ // go through queue and cascade up those who can move
	 if(!yellowQueue[i] && yellowQueue[i-1]){
	    yellowQueue[i] = true;
	    yellowQueue[i-1] = false;
	 }
      }

      if(!yellowQueue[0]){ // check if space at front of purple queue
	 yellowQueue[0] = decYellow();
      }

      yellowWaiting = yellowQueue[yellowQueue.length-1];


   }

   private void pulseYellowBridge(){

      if( !yellowExit[0] && yellowBridge[yellowBridge.length-1] // if space to move into (and som1 to move into it)
	  && ( !useMutexes || downBridge() )){  // and we can get lock on bridge if we have to

	    if(allowStarvation){ //allow starvation mode
	       yellowCount--;
	       if(yellowCount == 0){
		  yellowMutex = false;
	       }

		  yellowBridge[yellowBridge.length-1] = false;
		  yellowExit[0] = true;
	    }else{		 // don't allow starvation mode
	       if(!useMutexes || yellowMutex){
		  yellowMutex = false;
	       }
		  yellowCount--;
		  yellowBridge[yellowBridge.length-1] = false;
		  yellowExit[0] = true;
	    }

	 upBridge(); //release our lock on the bridge      

      }
// now pulse rest of the array
      for(int i=yellowBridge.length-1;i > 0 ; i--){ // go through queue and cascade up those who can move
	 if(!yellowBridge[i] && yellowBridge[i-1]){
	    yellowBridge[i] = true;
	    yellowBridge[i-1] = false;
	 }
      }

   } 

   private void pulseYellowExit(){
      yellowExit[yellowExit.length-1] = false;

      for(int i= yellowExit.length-1; i > 0 ; i--){
	 if(!yellowExit[i] && yellowExit[i-1]){
	    yellowExit[i] = true;
	    yellowExit[i-1] = false;
	 }
      }
   }


// **********************************************************************************************
// ==============================================================================================
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

//queue works like "outside world|0123456789...bridge0123456789...end queue0123...|outisde world"

   private void pulsePurpleQueue(){
	 // work from 'front' (getting onto bridge) going backwards (ppl waiting to get onto queue)

      if(purpleQueue[purpleQueue.length-1] && (!useMutexes || downBridge()) ){// is some1 waiting to get onto bridge
							      // and can we get a lock on the bridge (if we 'have' to
	 // we now own the exclusive rights to the bridge and colour mutexes.

	 if(allowStarvation){

	    if((!useMutexes || purpleMutex) && !purpleBridge[0]){ 
				 // see if our colour are on the bridge already and space for me
	       purpleBridge[0] = true;
	       purpleQueue[purpleQueue.length-1] = false;
	       purpleCount++;  // there's one more purple on the bridge   

	    }else if( !useMutexes || !yellowMutex ){ // if there are no yellows on the bridge
	       purpleMutex = true;
	       purpleBridge[0] = true;
	       purpleQueue[purpleQueue.length-1] = false;
	       purpleCount++;  // there's one more purple on the bridge   
	    }

	 }else{	  // we are not allowing starvation

	    if(!useMutexes || purpleMutex){

	       purpleBridge[0] = true;
	       purpleQueue[purpleQueue.length-1] = false;
	       purpleCount++;  // there's one more purple on the bridge   

	    }else if( (!yellowWaiting || (starvationNext && purpleCount==0)) && yellowCount==0 ){

	       purpleBridge[0] = true;
	       purpleQueue[purpleQueue.length-1] = false;
	       purpleCount++;
	       purpleMutex = true;
	       starvationNext = false;
	    }

	 }

	 upBridge(); // release our lock on the bridge (ignored if we are usingBridgeMutex)
      }

      for(int i=purpleQueue.length-1;i > 0 ; i--){ // go through queue and cascade up those who can move
	 if(!purpleQueue[i] && purpleQueue[i-1]){
	    purpleQueue[i] = true;
	    purpleQueue[i-1] = false;
	 }
      }

      if(!purpleQueue[0]){ // check if space at front of purple queue
	 purpleQueue[0] = decPurple();
      }

      purpleWaiting = purpleQueue[purpleQueue.length-1];

   }

   private void pulsePurpleBridge(){

      if( !purpleExit[0] && purpleBridge[purpleBridge.length-1] // if space to move into (and som1 to move into it)
	  && (!useMutexes || downBridge() )){  // and we can get lock on bridge if we have to

	    if(allowStarvation){ //allow starvation mode
	       purpleCount--;
	       if(purpleCount == 0){
		  purpleMutex = false;
	       }

		  purpleBridge[purpleBridge.length-1] = false;
		  purpleExit[0] = true;
	    }else{		 // don't allow starvation mode
	       if(!useMutexes || purpleMutex){
		  purpleMutex = false;
	       }
		  purpleCount--;
		  purpleBridge[purpleBridge.length-1] = false;
		  purpleExit[0] = true;
	    }

	 upBridge(); //release our lock on the bridge      

      }
// now pulse rest of the array
      for(int i=purpleBridge.length-1;i > 0 ; i--){ // go through queue and cascade up those who can move
	 if(!purpleBridge[i] && purpleBridge[i-1]){
	    purpleBridge[i] = true;
	    purpleBridge[i-1] = false;
	 }
      }

   } 

   private void pulsePurpleExit(){
      purpleExit[purpleExit.length-1] = false;

      for(int i= purpleExit.length-1; i > 0 ; i--){
	 if(!purpleExit[i] && purpleExit[i-1]){
	    purpleExit[i] = true;
	    purpleExit[i-1] = false;
	 }
      }
   }

// **********************************************************************************************
// ==============================================================================================
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&


}


