package towersofhanoi;

import java.util.ArrayList;
import java.util.List;
/**
 * Creates a new instance of ExecParallel
 */
public class ExecParallelSync extends ExecutionModel {
//  private List<ObjectSync> lSync = new ArrayList<ObjectSync>();
  ExecParallelSync() {
    super();
  }
  ExecParallelSync(int nrDisks) {
    super(nrDisks);
  }
  void cleanup() {
//    lSync.removeAll(lSync);
  }
  void hanoi(boolean flgDisplay, int n, int src, int dst, int aux) {
    if( n==1 ) {
      if( flgDisplay ) {
        System.out.println("    moving disk " + n + ": " + "pino-" + src + " ---> " + "pino-" +dst); System.out.flush();
      }
    } else {
      // Create sync variable for synchronization
      ObjectSync sync = new ObjectSync(); sync.podeIr = false; //lSync.add(sync);
      
      // move n-1 discos de src para aux deixando disco de numero #n em src
      ExecParallelSync.ThreadA thA = (new ExecParallelSync()).new ThreadA(sync, flgDisplay, n, src, dst, aux);
      thA.start();
      
      // move disco #n de src para dst
      synchronized(sync) {
        while( sync.podeIr == false ) {  // espera liberacao da ThreadB
          try {
            sync.wait();
          } catch (InterruptedException ex) {
            ex.printStackTrace();
          }
        }
        if( flgDisplay ) {
          System.out.println("    moving disk " + n + ": " + "pino-" + src + " ---> " + "pino-" +dst);System.out.flush();
        }
        sync.notify();
      }
      
      // move n-1 discos de Aux para Dst sobre disco #n
      ExecParallelSync.ThreadB thB = (new ExecParallelSync()).new ThreadB(sync, flgDisplay, n, src, dst, aux);
      thB.start();
      
      // Espera termino das threads
      try {
        thA.join();
        thB.join();
      } catch (InterruptedException ex) {
        ex.printStackTrace();
      }
    }
  }
  class ObjectSync {
    boolean podeIr;
  }
  class ThreadA extends Thread {
    volatile ObjectSync sync;
    boolean  flgDisplay;
    int n;
    int src;
    int dst;
    int aux;
    ThreadA(ObjectSync sync, boolean flgDisplay, int n, int src, int dst, int aux) {
      this.sync   = sync;
      this.flgDisplay = flgDisplay;
      this.n   = n;
      this.src = src;
      this.dst = dst;
      this.aux = aux;
    }
    public void run(){
      // move n-1 discos de src para aux deixando disco de numero #n em src
      hanoi(flgDisplay, n-1, src, aux, dst);
      synchronized(sync) {
        sync.podeIr = true;
        sync.notify(); // libera execucao da ThreadB
      }
    }
  }
  class ThreadB extends Thread {
    volatile ObjectSync sync;
    boolean  flgDisplay;
    int n;
    int src;
    int dst;
    int aux;
    ThreadB(ObjectSync sync, boolean flgDisplay, int n, int src, int dst, int aux) {
      this.sync = sync;
      this.flgDisplay = flgDisplay;
      this.n = n;
      this.src = src;
      this.dst = dst;
      this.aux = aux;
    }
    public void run(){
      synchronized(sync) {
        while( sync.podeIr == false ) {  // espera liberacao da ThreadA
          try {
            sync.wait();
          } catch (InterruptedException ex) {
            ex.printStackTrace();
          }
        }
      }
      // move n-1 discos de Aux para Dst sobre disco #n
      hanoi(flgDisplay, n-1, aux, dst, src);
    }
  }
}