package brn.distsim.jmx;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.StandardMBean;

@SuppressWarnings("unchecked")
public class DescriptionMBean extends StandardMBean {

  private boolean isLowercaseAttributeNames = true;
  private static Map nameToPrimitiveClass = new HashMap();
  static {
    nameToPrimitiveClass.put("boolean",Boolean.TYPE);
    nameToPrimitiveClass.put("byte",Byte.TYPE);
    nameToPrimitiveClass.put("char",Character.TYPE);
    nameToPrimitiveClass.put("short",Short.TYPE);
    nameToPrimitiveClass.put("int",Integer.TYPE);
    nameToPrimitiveClass.put("long",Long.TYPE);
    nameToPrimitiveClass.put("float",Float.TYPE);
    nameToPrimitiveClass.put("double",Double.TYPE);
  }
  public DescriptionMBean(Class<?> mbeanInterface, boolean isMXBean) {
    super(mbeanInterface, isMXBean);
    // TODO Auto-generated constructor stub
  }

  public DescriptionMBean(Class<?> mbeanInterface)
      throws NotCompliantMBeanException {
    super(mbeanInterface);
    // TODO Auto-generated constructor stub
  }

  public <T> DescriptionMBean(T implementation, Class<T> mbeanInterface,
      boolean isMXBean) {
    super(implementation, mbeanInterface, isMXBean);
  }

  public <T> DescriptionMBean(T implementation, Class<T> mbeanInterface)
      throws NotCompliantMBeanException {
    super(implementation, mbeanInterface);
  }

  private Class findClass(String name) {
    Class c = (Class)nameToPrimitiveClass.get(name);
    if (null != c)
      return c;
    try {
      return Class.forName(name);
    } catch (Throwable e) {
      return null;
    }
  }

  /**
   * Returns the class's description.
   */
  private String getDescription(Class cl) {
    if (null == cl)
      return null;
    try {
      Description desc = (Description) cl.getAnnotation(Description.class);

      if (desc != null)
        return desc.value();
      else
        return "";
    } catch (Throwable e) {
      return "";
    }
  }

  private String getDescription(Method method, int i) {
    if (null == method)
      return null;
    try {
      for (Annotation ann : method.getParameterAnnotations()[i]) {
        if (ann instanceof Description)
          return ((Description) ann).value();
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }

    return "";
  }
  
  private String getName(Method method, int i) {
    if (null == method)
      return null;
    try {
      for (Annotation ann : method.getParameterAnnotations()[i]) {
        if (ann instanceof Name)
          return ((Name) ann).value();
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }

    return "";
  }

  /**
   * Returns the method's description.
   */
  private String getDescription(Method method) {
    if (null == method)
      return null;
    try {
      Description desc = (Description) method.getAnnotation(Description.class);

      if (desc != null)
        return desc.value();
      else
        return "";
    } catch (Throwable e) {
      return "";
    }
  }

  @Override
  protected String getDescription(MBeanInfo info) {
    if (info == null) return null;
    String ret = getDescription(getMBeanInterface());
    if (null != ret && ret.length() > 0)
      return ret;
    return super.getDescription(info);
  }

  @Override
  protected String getDescription(MBeanAttributeInfo info) {
    if (info == null) return null;
    
    String attributeName = info.getName();
    if (isLowercaseAttributeNames )  {
      StringBuilder builder = new StringBuilder(attributeName);
      builder.setCharAt(0, Character.toLowerCase(builder.charAt(0)));
      attributeName = builder.toString();
    }

    BeanInfo binfo;
    try {
      binfo = Introspector.getBeanInfo(getMBeanInterface());
      PropertyDescriptor[] desc = binfo.getPropertyDescriptors();
      for (int i = 0; i < desc.length; i++)  {
        if (desc[i].getName().equals(attributeName)) {
          String ret = getDescription(desc[i].getReadMethod());
          if (null != ret && ret.length() > 0)
            return ret;
          ret = getDescription(desc[i].getWriteMethod());
          if (null != ret && ret.length() > 0)
            return ret;
        }  
      }
    } catch (IntrospectionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return super.getDescription(info);
  }

  @Override
  protected String getDescription(MBeanOperationInfo info) {
    if (info == null) return null;
    Class clazz = this.getMBeanInterface();

    MBeanParameterInfo[] pinfo = info.getSignature();
    Class[] parameterClazz = new Class[pinfo.length];
    for (int i = 0; i < pinfo.length; i++) {
      parameterClazz[i] = findClass(pinfo[i].getType());
    }
    Method method = null;
    try {
//      BeanInfo binfo = Introspector.getBeanInfo(getMBeanInterface());
//      MethodDescriptor[] methods = binfo.getMethodDescriptors();
//      methods[i].
      method = clazz.getMethod(info.getName(), parameterClazz);
      String ret = getDescription(method);
      if (null != ret && ret.length() > 0)
        return ret;
    } catch (SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    return super.getDescription(info);
  }

  @Override
  protected String getParameterName(MBeanOperationInfo op,
      MBeanParameterInfo param, int sequence) {
    if (param == null) return null;
    Class clazz = this.getMBeanInterface();

    MBeanParameterInfo[] pinfo = op.getSignature();
    Class[] parameterClazz = new Class[pinfo.length];
    for (int i = 0; i < pinfo.length; i++) {
      parameterClazz[i] = findClass(pinfo[i].getType());
    }
    Method method = null;
    try {
      method = clazz.getMethod(op.getName(), parameterClazz);
      String ret = getName(method, sequence);
      if (null != ret && ret.length() > 0)
        return ret;
    } catch (SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    return super.getDescription(op, param, sequence);
  }

  @Override
  protected String getDescription(MBeanOperationInfo op,
      MBeanParameterInfo param, int sequence) {
    if (param == null) return null;
    Class clazz = this.getMBeanInterface();

    MBeanParameterInfo[] pinfo = op.getSignature();
    Class[] parameterClazz = new Class[pinfo.length];
    for (int i = 0; i < pinfo.length; i++) {
      parameterClazz[i] = findClass(pinfo[i].getType());
    }
    Method method = null;
    try {
      method = clazz.getMethod(op.getName(), parameterClazz);
      String ret = getDescription(method, sequence);
      if (null != ret && ret.length() > 0)
        return ret;
    } catch (SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return super.getDescription(op, param, sequence);
  }


  // static MBeanInfo introspect(Object obj, Class cl,
  // boolean isLowercaseAttributeNames) throws NotCompliantMBeanException {
  // try {
  // SoftReference<MBeanInfo> infoRef = _cachedInfo.get(cl);
  // MBeanInfo info = null;
  //
  // if (infoRef != null && (info = infoRef.get()) != null)
  // return info;
  //
  // String className = cl.getName();
  //
  // HashMap<String, MBeanAttributeInfo> attributes = new HashMap<String,
  // MBeanAttributeInfo>();
  //
  // ArrayList<MBeanConstructorInfo> constructors = new
  // ArrayList<MBeanConstructorInfo>();
  //
  // ArrayList<MBeanOperationInfo> operations = new
  // ArrayList<MBeanOperationInfo>();
  //
  // Method[] methods = cl.getMethods();
  // for (int i = 0; i < methods.length; i++) {
  // Method method = methods[i];
  //
  // if (method.getDeclaringClass() == Object.class)
  // continue;
  //
  // if (Modifier.isStatic(method.getModifiers()))
  // continue;
  //
  // String methodName = method.getName();
  // Class[] args = method.getParameterTypes();
  // Class retType = method.getReturnType();
  //
  // if (methodName.startsWith("get") && args.length == 0
  // && !retType.equals(void.class)) {
  // Method getter = method;
  // String name = methodName.substring(3);
  //
  // Method setter = getSetter(methods, name, retType);
  //
  // String attributeName;
  //
  // if (isLowercaseAttributeNames) {
  // StringBuilder builder = new StringBuilder(name);
  // builder.setCharAt(0, Character.toLowerCase(builder.charAt(0)));
  // attributeName = builder.toString();
  // } else
  // attributeName = name;
  //
  // Class type = method.getReturnType();
  //
  // MBeanAttributeInfo attr;
  //
  // attr = new MBeanAttributeInfo(attributeName, getDescription(method),
  // getter, setter);
  //
  // /*
  // * Descriptor descriptor = attr.getDescriptor();
  // *
  // * if (descriptor != null) { Object openType = getOpenType(type);
  // *
  // * if (openType != null) descriptor.setField("openType", openType);
  // *
  // * descriptor.setField("originalType", getTypeName(type));
  // *
  // * attr.setDescriptor(descriptor); }
  // */
  //
  // if (attributes.get(attributeName) == null)
  // attributes.put(attributeName, attr);
  // } else if (methodName.startsWith("is") && args.length == 0
  // && (retType.equals(boolean.class) || retType.equals(Boolean.class))) {
  // Method getter = method;
  // String name = methodName.substring(2);
  //
  // Method setter = getSetter(methods, name, retType);
  //
  // String attributeName;
  //
  // if (isLowercaseAttributeNames) {
  // StringBuilder builder = new StringBuilder(name);
  // builder.setCharAt(0, Character.toLowerCase(builder.charAt(0)));
  // attributeName = builder.toString();
  // } else
  // attributeName = name;
  //
  // if (attributes.get(attributeName) == null) {
  // attributes.put(attributeName, new MBeanAttributeInfo(attributeName,
  // getDescription(method), getter, setter));
  // }
  // } else if (methodName.startsWith("set") && args.length == 1) {
  // Method setter = method;
  // String name = methodName.substring(3);
  //
  // Method getter = getGetter(methods, name, args[0]);
  //
  // if (getter == null) {
  // String attributeName;
  //
  // if (isLowercaseAttributeNames) {
  // StringBuilder builder = new StringBuilder(name);
  // builder.setCharAt(0, Character.toLowerCase(builder.charAt(0)));
  // attributeName = builder.toString();
  // } else
  // attributeName = name;
  //
  // if (attributes.get(attributeName) == null) {
  // attributes.put(attributeName, new MBeanAttributeInfo(
  // attributeName, getDescription(method), null, setter));
  // }
  // }
  // } else {
  // operations.add(new MBeanOperationInfo(getName(method),
  // getDescription(method), getSignature(method), method
  // .getReturnType().getName(), MBeanOperationInfo.UNKNOWN));
  // }
  // }
  //
  // ArrayList<MBeanNotificationInfo> notifications = new
  // ArrayList<MBeanNotificationInfo>();
  //
  // if (obj instanceof NotificationBroadcaster) {
  // NotificationBroadcaster broadcaster;
  // broadcaster = (NotificationBroadcaster) obj;
  //
  // MBeanNotificationInfo[] notifs = broadcaster.getNotificationInfo();
  //
  // if (notifs != null) {
  // for (int i = 0; i < notifs.length; i++) {
  // MBeanNotificationInfo notif = notifs[i];
  //
  // notifications.add((MBeanNotificationInfo) notifs[i].clone());
  // }
  // }
  // }
  //
  // Collections.sort(notifications, MBEAN_FEATURE_INFO_COMPARATOR);
  //
  // MBeanAttributeInfo[] attrArray = new MBeanAttributeInfo[attributes.size()];
  // attributes.values().toArray(attrArray);
  // Arrays.sort(attrArray, MBEAN_FEATURE_INFO_COMPARATOR);
  //
  // MBeanConstructorInfo[] conArray = new MBeanConstructorInfo[constructors
  // .size()];
  // constructors.toArray(conArray);
  //
  // MBeanOperationInfo[] opArray = new MBeanOperationInfo[operations.size()];
  // operations.toArray(opArray);
  // Arrays.sort(opArray, MBEAN_FEATURE_INFO_COMPARATOR);
  // MBeanNotificationInfo[] notifArray = new
  // MBeanNotificationInfo[notifications
  // .size()];
  // notifications.toArray(notifArray);
  // Arrays.sort(notifArray, MBEAN_FEATURE_INFO_COMPARATOR);
  //
  // MBeanInfo modelInfo;
  //
  // modelInfo = new MBeanInfo(cl.getName(), getDescription(cl), attrArray,
  // conArray, opArray, notifArray);
  // /*
  // * Descriptor descriptor = modelInfo.getMBeanDescriptor(); if (descriptor !=
  // * null) { descriptor.setField("mxbean", "true");
  // * modelInfo.setMBeanDescriptor(descriptor); }
  // */
  //
  // info = modelInfo;
  //
  // _cachedInfo.put(cl, new SoftReference<MBeanInfo>(info));
  //
  // return info;
  // } catch (Exception e) {
  // NotCompliantMBeanException exn;
  // exn = new NotCompliantMBeanException(String.valueOf(e));
  //
  // exn.initCause(e);
  //
  // throw exn;
  // }
  // }

}
