package org.rabix.bindings;

import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.lang.NotImplementedException;
import org.rabix.bindings.model.Application;
import org.rabix.bindings.model.Job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BindingsFactory {

  private final static Logger logger = LoggerFactory.getLogger(BindingsFactory.class);
  
  public static final String MULTIPROTOCOL_KEY = "rabix.multiprotocol";
  
  private static SortedSet<Bindings> bindings = new TreeSet<>(new Comparator<Bindings>() {
    @Override
    public int compare(Bindings b1, Bindings b2) {
      return b1.getProtocolType().order - b2.getProtocolType().order;
    }
  });

  static {
    for (ProtocolType type : ProtocolType.values()) {
      try {
        Class<?> clazz = Class.forName(type.bindingsClass);
        if (clazz == null) {
          continue;
        }
        try {
          bindings.add((Bindings) clazz.newInstance());
        } catch (Exception e) {
          logger.warn("Failed to instantiate class " + clazz, e);
        }
      } catch (Exception e) {
        // ignore
      }
    }
  }

  public static synchronized Bindings create(String appURL) throws BindingException {
    int wrongVersions = 0;
    Exception finalException = null;
    for (Bindings binding : bindings) {
      try {
        Application app = binding.loadAppObject(appURL);
        if (app == null) {
          continue;
        }
        if (app.getVersion() != null && binding.getProtocolType().appVersion.equalsIgnoreCase(app.getVersion())) {
          return binding;
        }
        else if(app.getVersion() == null && binding.getProtocolType().appVersion == null) {
          return binding;
        }
      } catch (NotImplementedException e) {
        throw e; // fail if we do not support this kind of deserialization (Schema salad)
      } catch (BindingWrongVersionException ignore) {
        wrongVersions++;
      } catch (Exception e) {
        finalException = e;
      }
    }

    if (wrongVersions == bindings.size()) {
      StringBuilder validVersions = new StringBuilder();
      boolean first = true;
      for (Bindings b : bindings) {
        if (b.getProtocolType().appVersion == null)
          continue;
        if (!first)
          validVersions.append(", ");
        validVersions.append(b.getProtocolType().appVersion);
        first = false;
      }
      throw new BindingException("Invalid cwlVersion. Allowed values are: " + validVersions);
    }

    if (finalException != null && finalException.getMessage() != null) {
      throw new BindingException(finalException.getMessage(), finalException);
    }

    throw new BindingException("Unknown error when parsing the app!");
  }

  public static synchronized Bindings create(Job job) throws BindingException {
    return create(job.getApp());
  }
  
  public static synchronized Bindings create(ProtocolType protocol) throws BindingException {
    for(Bindings binding: bindings) {
      if(binding.getProtocolType().equals(protocol)) {
        return binding;
      }
    }
    throw new BindingException("Cannot find binding for the protocol.");
  }

}

@@ -26,92 +26,8 @@
 s;%0A%0A
-import java.util.Comparator;%0Aimport java.util.SortedSet;%0Aimport java.util.TreeSet;%0A%0A
 impo
@@ -74,24 +74,24 @@
 dException;%0A
+
 import org.r
@@ -218,16 +218,100 @@
 ctory;%0A%0A
+import java.util.Comparator;%0Aimport java.util.SortedSet;%0Aimport java.util.TreeSet;%0A%0A
 public c
@@ -421,19 +421,17 @@
 class);%0A
-  
 %0A
+
   public
@@ -494,18 +494,16 @@
 tocol%22;%0A
-  
 %0A  priva
@@ -1187,37 +1187,24 @@
 blic static 
-synchronized 
 Bindings cre
@@ -2779,37 +2779,24 @@
 blic static 
-synchronized 
 Bindings cre
@@ -2871,18 +2871,16 @@
 ));%0A  %7D%0A
-  
 %0A  publi
@@ -2892,21 +2892,8 @@
 tic 
-synchronized 
 Bind
@@ -3161,11 +3161,10 @@
 %22);%0A  %7D%0A
-%0A
 %7D%0A

remove synchronization from BindingsFactory.create