package org.jfox.mx;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.net.URL;
import java.io.File;

import org.jfox.mx.util.PrimitiveHelper;

/**
 * 
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class MxServer {

  // MxServer list
  private static List mxs = new ArrayList();
  private static final String DEFAULT_DOMAIN = "Default";

  private String domain = "";
//  private ClassLoader mxLoader = null;
  private RepositoryCtrl repo = null;

  private MxServer(String domain) {
    this.domain = domain;
    repo = new RepositoryCtrl(domain);
/*
    try {
      mxLoader = new MxClassLoader(new URL[]{new File("").toURL()},Thread.currentThread().getContextClassLoader());
    }
    catch(Exception e){
      e.printStackTrace();
    }
*/
  }

  public static synchronized MxServer getInstance() {
    return getInstance(DEFAULT_DOMAIN);
  }

  public static synchronized MxServer getInstance(String domain){
    if(domain == null || domain.equals("")) {
      domain = DEFAULT_DOMAIN;
    }
    for(int i=0;i<mxs.size();i++){
      MxServer mx = (MxServer)mxs.get(i);
      if(mx.getDefaultDomain().equals(domain)) {
        return mx;
      }
    }
    MxServer mx = new MxServer(domain);
    mxs.add(mx);
    return mx;
  }

  public String getDefaultDomain(){
    return domain;
  }

  /**
   * 根据 className 和 ObjectName 生成一个 Mxable 对象,注册到 MxServer,返回生成的 Mxable 对象
   * @param className Mxable 对象类名称
   * @param name 要注册的 ObjectName
   * @return
   * @throws MxException
   */
  public Mxable createMX(String className, ObjectName name)  throws MxException {
    return createMX(className,name,null,null);
  }

  /**
   * 根据 className、ObjectName以及参数对象、参数对象类型生成一个 Mxable 对象,注册到 MxServer,返回生成的 Mxable 对象
   * @param className
   * @param name
   * @param params
   * @param signatures
   * @return
   * @throws MxException
   */
  public Mxable createMX(String className, ObjectName name, Object[] params, String[] signatures)  throws MxException {
    if(name.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + name.toString() + " is a pattern object name"));
    if (className == null || className.trim().length() == 0){
      throw new MxException("invalide className");
    }
    Class mxClass = null;
    try {
      mxClass = Thread.currentThread().getContextClassLoader().loadClass(className.trim());
    }
    catch(ClassNotFoundException e){
      throw new MxException(e);
    }

    if(!Mxable.class.isAssignableFrom(mxClass)) {
      throw new MxException(mxClass + " not implement Mxable!");
    }

    try {
      Class[] signatureClasses = loadSignatures(signatures);
      Mxable mxObject =  (Mxable)mxClass.getConstructor(signatureClasses).newInstance(params);
      registerMX(name,mxObject);
      return mxObject;
    }
    catch(Exception e){
      throw new MxException(e);
    }
  }

  public MxInfo getMxInfo(ObjectName objectName) throws MxException {
    if(objectName.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + objectName.toString() + " is a pattern object name"));
    if(!repo.contains(objectName)) throw new MxException("instance not found: " + objectName.toString());
    MxMetaData meta = repo.retrieve(objectName);
    return meta.getMxInfo();
  }

  public boolean isInstanceOf(ObjectName objectName, String className) throws MxException {
    if(objectName.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + objectName.toString() + " is a pattern object name"));
    if(!repo.contains(objectName)) throw new MxException("instance not found: " + objectName.toString());
    MxMetaData meta = repo.retrieve(objectName);

    Class cls = loadClass(className);
    return cls.isInstance(meta.getMxObject());
  }

  /**
   * 得到 MxServer 用来装载 Mx bean 的 ClassLoader
   * @return
   */
  public ClassLoader getMxClassLoader(){
    return Thread.currentThread().getContextClassLoader();
  }

  public boolean isRegistered(ObjectName objectName) throws MxException {
    if(objectName.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + objectName.toString() + " is a pattern object name"));
    return repo.contains(objectName);
  }

  public int getMxCount() {
    return repo.getRepoSize();
  }

  public Mxable getMxObject(ObjectName objectName) throws MxException {
    if(objectName.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + objectName.toString() + " is a pattern object name"));
    if(!repo.contains(objectName)) throw new MxException("instance not found: " + objectName.toString());
    return repo.retrieve(objectName).getMxObject();
  }

  private void registerMX(ObjectName objectName, Mxable mxObject) throws MxException{
    if(repo.contains(objectName)) throw new MxException( objectName + " already exists.");
    boolean isRegisterMBean = (mxObject instanceof MxRegistration) ? true : false;
    if(isRegisterMBean == true) {
      try {
        ((MxRegistration)mxObject).preRegister(this,objectName);
      }
      catch(Exception e) {
        try{
          ((MxRegistration)mxObject).postRegister(Boolean.FALSE);
        }
        catch(Exception ex){
        }
        throw new MxException(e);
      }
    }

    repo.store(objectName,new MxMetaData(objectName,mxObject));

    if(isRegisterMBean == true) {
      ((MxRegistration)mxObject).postRegister(Boolean.TRUE);
    }
  }

  public void unregisterMBean(ObjectName objectName) throws MxException {
    if(objectName.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + objectName.toString() + " is a pattern object name"));
    if(!repo.contains(objectName)) throw new MxException("instance not found: " + objectName.toString());

    MxMetaData meta = repo.retrieve(objectName);
    Mxable mxObject = meta.getMxObject();
    boolean isRegisterMBean = (mxObject instanceof MxRegistration) ? true : false;
    if(isRegisterMBean) {
      try{
        ((MxRegistration)mxObject).preDeregister();
      }
      catch(Exception e){
        try{
          ((MxRegistration)mxObject).postDeregister();
        }
        catch(Exception ex){
        }
        throw new MxException(e);
      }
    }

    repo.remove(objectName); // remove from repository

    if(isRegisterMBean){
      try{
        ((MxRegistration)mxObject).postDeregister();
      }
      catch(Exception e){
        throw new MxException(e);
      }
    }
  }

  public Object invoke(ObjectName objectName, String operationName, Object params[], String signatures[]) throws MxException {
    if(objectName.isPattern()) throw new MxException(new IllegalArgumentException("ObjectName " + objectName.toString() + " is a pattern object name"));
    if(!repo.contains(objectName)) throw new MxException("instance not found: " + objectName.toString());
    if(operationName == null || operationName.trim().length() ==0) throw new MxException(new IllegalArgumentException("operation name cannot be null or empty"));
    operationName = operationName.trim();
    if(!operationName.startsWith("mx_")) {
      throw new MxException(operationName + " is not a Management Operation, Management operation must start with \" mx_\"");
    }
    MxMetaData meta = repo.retrieve(objectName);
    Class[] signatureClasses = loadSignatures(signatures);
    return meta.invoke(operationName,params,signatureClasses);
  }

  public Object invoke(ObjectName objectName, String operationName) throws MxException {
    return invoke(objectName,operationName,null,null);
  }

  /**
   * get ObjectNames of the domain
   * @param domain
   * @return
   */
  public Iterator queryMX(String domain){
    if(domain == null || domain.equals("")) domain = "*";
    return repo.queryMBean(domain);
  }

  public String[] getDomains(){
    return repo.getDomains();
  }
  /**
   * 根据参数的字符串类装载成类对象
   * @param signatrues
   * @return
   * @throws MxException
   */
  private Class[] loadSignatures(String[] signatrues) throws MxException {
    if(signatrues == null){
      return null;
    }
    if(signatrues.length == 0) {
      return new Class[0];
    }
    Class[] signatureClasses = new Class[signatrues.length];
    for(int i=0;i<signatrues.length;i++) {
      signatureClasses[i] = loadClass(signatrues[i]);
    }

    return signatureClasses;
  }

  private Class loadClass(String className) throws MxException {
    if(className == null || className.trim().equals("")) {
      throw new MxException(className + " is a invalide class name");
    }
    className = className.trim();
    try {
      Class cls = PrimitiveHelper.getPrimitiveClass(className);
      if(cls == null) { // 不是原始类
        cls = Thread.currentThread().getContextClassLoader().loadClass(className);
      }
      return cls;
    }
    catch(ClassNotFoundException e){
      throw new MxException(e);
    }
  }


  public static void main(String[] args) {

  }
}