package com.joelpm.bidiMessages.client;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

/**
* This class is responsible for notifying others when the
* connection is lost or established and for attempting to
* reconnect when the connection is lost.
*
* @author Joel Meyer
*/
public class ConnectionStatusMonitor {
  /**
* Simple task used to attempt a reconnect every few seconds.
*/
  private class RetryTask extends TimerTask {
    @Override public void run() {
      tryOpen();
    }
  }
  
  private final Timer timer;
  
  private final TTransport transport;
  private final AtomicBoolean connected;
  private final List<ConnectionStatusListener> listeners;
  
  public ConnectionStatusMonitor(TTransport transport) {
    this.transport = transport;
    this.connected = new AtomicBoolean(false);
    this.listeners = new ArrayList<ConnectionStatusListener>();
    
    this.timer = new Timer();
  }
  
  public void addListener(ConnectionStatusListener listener) {
    listeners.add(listener);
  }
  
  public void disconnected(ConnectionStatusListener noticer) {
    if (connected.compareAndSet(true, false)) {
      for (ConnectionStatusListener listener : listeners) {
        // The thread running the noticer is our current execution thread. If we
        // notify him he'll block and we'll be deadlocked. Since he noticed the
        // disconnect he is responsible for initiating his own wait state.
        if (listener == noticer) continue;
        listener.connectionLost();
      }
      
      // Try to reconnect in five seconds
      timer.schedule(new RetryTask(), 5 * 1000);
    }
  }
  
  /**
* Attempts to reconnect to the server.
*/
  public void tryOpen() {
    if (connected.get()) return;
    
    // Make sure it's closed
    transport.close();
    
    try {
      transport.open();
      connected.set(true);
      for (ConnectionStatusListener listener : listeners) {
        listener.connectionEstablished();
      }
      return;
    } catch (TTransportException e) {
      
    }
    
    timer.schedule(new RetryTask(), 5 * 1000);
  }
}