/*
 * Copyright 2012, UCAR/Unidata.
 * See the LICENSE file for more information.
 */

package dap4.dap4lib;

import dap4.core.util.DapContext;
import dap4.core.util.DapException;
import java.util.ArrayList;
import java.util.List;

/**
 */

public class DSPRegistry {
  //////////////////////////////////////////////////
  // Constants

  // MNemonics
  public static final boolean LAST = true;
  public static final boolean FIRST = false;

  //////////////////////////////////////////////////
  // Type Decls

  protected static class Registration {
    Class<? extends D4DSP> dspclass;
    D4DSP matcher;

    public Registration(Class<? extends D4DSP> cl) {
      this.dspclass = cl;
      try {
        this.matcher = dspclass.newInstance();
      } catch (IllegalAccessException | InstantiationException e) {
        throw new IllegalArgumentException(
            "DSPFactory: cannot create matching instance for Class: " + dspclass.getName());
      }
    }

    public String toString() {
      return dspclass.getName();
    }
  }

  //////////////////////////////////////////////////

  protected static ClassLoader loader = DSPRegistry.class.getClassLoader();

  //////////////////////////////////////////////////
  // Instance Variables

  /**
   * Define a map of known DSP classes.
   * Must be ordered to allow control over
   * test order
   */
  protected List<Registration> registry = new ArrayList<>();

  //////////////////////////////////////////////////
  // Constructor(s)

  public DSPRegistry() {}


  //////////////////////////////////////////////////
  // Accessors

  public static void setLoader(ClassLoader ldr) {
    loader = ldr;
  }

  //////////////////////////////////////////////////
  // API

  /**
   * Register a DSP, using its class string name.
   *
   * @param className Name of class that implements DSP.
   * @throws IllegalAccessException if class is not accessible.
   * @throws InstantiationException if class doesnt have a no-arg constructor.
   * @throws ClassNotFoundException if class not found.
   */
  public synchronized void register(String className, boolean last) throws DapException {
    try {
      Class<? extends D4DSP> klass = (Class<? extends D4DSP>) loader.loadClass(className);
      register(klass, last);
    } catch (ClassNotFoundException e) {
      throw new DapException(e);
    }
  }

  /**
   * Register a DSP class.
   *
   * @param klass Class that implements DSP.
   * @param last true=>insert at the end of the list; otherwise front
   * @throws IllegalAccessException if class is not accessible.
   * @throws InstantiationException if class doesnt have a no-arg constructor.
   * @throws ClassCastException if class doesnt implement DSP interface.
   */
  public synchronized void register(Class<? extends D4DSP> klass, boolean last) {
    // is this already defined?
    if (registered(klass))
      return;
    if (last)
      registry.add(new Registration(klass));
    else
      registry.add(0, new Registration(klass));
  }

  /**
   * See if a specific DSP is registered
   *
   * @param klass Class for which to search
   */

  public synchronized boolean registered(Class<? extends D4DSP> klass) {
    for (Registration r : registry) {
      if (r.dspclass == klass)
        return true;
    }
    return false;
  }

  /**
   * Unregister dsp.
   *
   * @param klass Class for which to search
   */
  public synchronized void unregister(Class<? extends D4DSP> klass) {
    for (int i = 0; i < registry.size(); i++) {
      if (registry.get(i).dspclass == klass) {
        registry.remove(i);
        break;
      }
    }
  }

  /**
   * @param path
   * @return new DSP object that can process this path
   * @throws DapException
   */

  public synchronized D4DSP findMatchingDSP(String path, DapContext cxt) throws DapException {
    for (int i = 0; i < registry.size(); i++) {
      try {
        Registration tester = registry.get(i);
        boolean ismatch = (Boolean) tester.matcher.dspMatch(path, cxt);
        if (ismatch) {
          D4DSP dsp = (D4DSP) tester.dspclass.newInstance();
          return dsp;
        }
      } catch (Exception e) {
        throw new DapException(e);
      }
    }
    throw new IllegalArgumentException("Cannot open " + path);
  }

}

