/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 * Created on 21.09.2004
 * @author Andreas Prlic
 *
 */

package org.biojava.dasobert.das;

import java.net.*;
import java.util.*;
import java.util.logging.*;

import org.biojava.dasobert.dasregistry.*;
import org.biojava.dasobert.eventmodel.*;

/**
 * a thread that connects to a DAS - Feature service and gets the features
 * 
 * @author Andreas Prlic
 */

public class FeatureThread implements Runnable
{

  /**
   * number of times the client tries to reconnect to the server if a "come back
   * later" is returned. the server should provide a reasonable estimation how
   * long it will take him to create results. if this number of requests is
   * still not successfull, give up.
   */
  public static int MAX_COME_BACK_ITERATIONS = 5;

  public static int MAX_NR_FEATURES = 300;

  static Logger logger = Logger.getLogger("org.biojava.spice");

  Das1Source dasSource;

  String ac;

  List featureListeners;

  Thread thread;

  public FeatureThread(String accessionCode, Das1Source dasSource)
  {
    this.dasSource = dasSource;
    this.ac = accessionCode;
    featureListeners = new ArrayList();
  }

  public void addFeatureListener(FeatureListener li)
  {
    featureListeners.add(li);
  }

  public void clearFeatureListeners()
  {
    featureListeners.clear();
  }

  public synchronized void stop()
  {
    thread = null;
    notify();
  }

  public void run()
  {
    Thread me = Thread.currentThread();
    while (thread == me)
    {
      String url = dasSource.getUrl();
      String queryString = url + "features?segment=" + ac;
      URL cmd = null;
      try
      {
        cmd = new URL(queryString);
      } catch (MalformedURLException e)
      {
        logger.warning("got MalformedURL from das source " + dasSource);
        e.printStackTrace();

      }

      logger.info("requesting features from " + cmd);
      DAS_FeatureRetrieve ftmp = new DAS_FeatureRetrieve(cmd);

      int comeBackLater = ftmp.getComeBackLater();
      int securityCounter = 0;
      while ((thread == me) && (comeBackLater > 0))
      {
        securityCounter++;
        if (securityCounter >= MAX_COME_BACK_ITERATIONS)
        {
          comeBackLater = -1;
          break;

        }
        notifyComeBackLater(comeBackLater);
        // server is still calculating - asks us to come back later
        try
        {
          wait(comeBackLater);
        } catch (InterruptedException e)
        {
          comeBackLater = -1;
          break;
        }

        ftmp.reload();
        comeBackLater = ftmp.getComeBackLater();
      }

      if (!(thread == me))
      {
        break;
      }

      List features = ftmp.get_features();
      String version = ftmp.getVersion();

      // a fallback mechanism to prevent DAS sources from bringing down spice
      if (features.size() > MAX_NR_FEATURES)
      {
        logger
                .warning("DAS source returned more than " + MAX_NR_FEATURES
                        + "features. "
                        + " throwing away excess features at " + cmd);
        features = features.subList(0, MAX_NR_FEATURES);
      }

      // notify FeatureListeners
      Map[] feats = (Map[]) features.toArray(new Map[features.size()]);
      notifyFeatureListeners(feats, version);

      break;

    }
    thread = null;

  }

  public void start()
  {
    thread = new Thread(this);
    thread.start();
  }

  private void notifyFeatureListeners(Map[] feats, String version)
  {
    logger.finest("FeatureThread found " + feats.length + " features");
    FeatureEvent fevent = new FeatureEvent(feats, dasSource, version);
    Iterator fiter = featureListeners.iterator();
    while (fiter.hasNext())
    {
      FeatureListener fi = (FeatureListener) fiter.next();
      fi.newFeatures(fevent);
    }
  }

  /**
   * the Annotation server requested to be queried again in a while
   * 
   * @param comeBackLater
   */
  private void notifyComeBackLater(int comeBackLater)
  {
    FeatureEvent event = new FeatureEvent(new HashMap[0], dasSource, "");
    event.setComeBackLater(comeBackLater);
    Iterator fiter = featureListeners.iterator();
    while (fiter.hasNext())
    {
      FeatureListener fi = (FeatureListener) fiter.next();
      fi.comeBackLater(event);
    }

  }

}
