/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sun.jini.example.browser;

import java.lang.reflect.Field;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.Icon;

/**
 *
 * @version 0.2 06/04/98
 *
 */
class ObjectNode extends DefaultMutableTreeNode implements java.io.Serializable {

  // "classname", "classname fieldName" or "classname fieldName=value"
  /**
   * @serial
   */
  private String name;

  /**
   * @serial
   */
  private Object obj;

  /**
   * @serial
   */
  private Class clazz;

  /**
   * @serial
   */
  private String fieldName;

  /**
   * @serial
   */
  private int arrayIndex = -1;

  /**
   * @serial
   */
  private boolean editable = false;

  /**
   * @serial
   */
  private boolean isLeaf;

  /**
   * @serial
   */
  private boolean isAdministrable;	// root level

  /**
   * @serial
   */
  private boolean isControllable;	// entry level

  /**
   * @serial
   */
  private boolean isRoot = false;

  /**
   * @serial
   */
  private boolean isEntryTop = false;

  // icons
  private static Icon[] icons = new Icon[6];
  static {
    icons[0] = MetalIcons.getBlueFolderIcon();	// Administrable Service, Controllable Attribute
    icons[1] = MetalIcons.getGrayFolderIcon();	// Non-administrable Service
    icons[2] = MetalIcons.getOrangeFolderIcon();	// Uncontrollable Attribute
    icons[3] = MetalIcons.getBlueFileIcon();	// Administrable Service, Controllable Attribute
    icons[4] = MetalIcons.getGrayFileIcon();	// Non-administrable Service
    icons[5] = MetalIcons.getOrangeFileIcon();	// Uncontrollable Attribute
  }

  /**
     Constructor for a root node.
  */
  public ObjectNode(boolean isAdministrable) {
    this("Root node", "".getClass(), null, -1, false);

    this.isAdministrable = isAdministrable;
    this.isRoot = true;
    this.isEntryTop = true;
  }

  /**
     Constructor for an entry (attribute) top nodes.
  */
  public ObjectNode(Object obj, boolean isControllable) {
    this(obj, obj.getClass(), null, -1, false);

    this.isControllable = isControllable;
    this.isEntryTop = true;
  }

  /**
     Constructor for an ordinary field.
  */
  public ObjectNode(Object obj, Class clazz, String fieldName, boolean isLeaf) {
    this(obj, clazz, fieldName, -1, isLeaf);
  }

  /**
     Constructor for an array element.
  */
  public ObjectNode(Object obj, Class clazz, String fieldName, int arrayIndex, boolean isLeaf) {
    this.obj = obj;
    this.clazz = clazz;
    this.fieldName = fieldName;
    this.arrayIndex = arrayIndex;
    this.isLeaf = isLeaf;

    super.setAllowsChildren(! isLeaf);
    setNodeName();
  }

  private void setNodeName() {
    name = Introspector.getTypename(clazz, false);
    if(fieldName != null)
      name += " " + fieldName;

    if(isLeaf) {
      //Class clazz = obj.getClass();
      String value = "";
      if(arrayIndex >= 0)
	value += ("[" + arrayIndex + "]");
      value += "=";
      if(clazz.isPrimitive()){
	value += "" + obj;
	editable = false;
      } else if(Introspector.isWrapper(clazz)){
	// Wrapper objects
	value += "" + obj;
	editable = true;
      } else if(Introspector.isString(clazz)) {
	value += "\"" + obj + "\"";
	editable = true;
      } else {
	value += (obj == null ? "null" : obj.toString());
      }
      name += value;
    } else if(obj == null) {
      name += "=null";
    }

    super.setUserObject(name);
  }

  public void add(ObjectNode child){
    child.setAdministrable(isAdministrable);
    if(! isRoot){
      child.setControllable(isControllable);
    }

    super.add(child);
  }

  public Object getEntryTop() {
    ObjectNode snode = this;
    do {
      snode = (ObjectNode) snode.getParent();
    } while(! snode.isEntryTop());

    return snode.getObject();
  }

  protected boolean isEntryTop() {
    return isEntryTop;
  }

  public void setObjectRecursive() throws NoSuchFieldException, IllegalAccessException {
    ObjectNode pnode = this;
    do {
      pnode = (ObjectNode) pnode.getParent();
      Object pobj = pnode.getObject();
      // Needs to think about array modifications
      Field f = pobj.getClass().getField(fieldName);
      f.set(pobj, obj);
    } while(! pnode.isEntryTop());
  }

  public String getTitle() {
    return name;
  }

  public Icon getIcon() {
    /*
      if(isAdministrable){
      if(isControllable){
      if(isLeaf)	    return icons[3];
      else		    return icons[0];
      } else {
      if(isLeaf)	    return icons[5];
      else		    return icons[2];
      }
      } else {
      if(isLeaf)	    return icons[4];
      else		    return icons[1];
      }
    */
    if(isAdministrable && isControllable)
      if(isLeaf)	return icons[3];
      else		return icons[0];
    else
      if(isLeaf)	return icons[4];
      else		return icons[1];
  }


  // Overwrite
  public void setUserObject(Object obj){
    if(obj instanceof String)
      name = (String) obj;

    super.setUserObject(obj);
  }

  public Object getUserObject(){
    return name;
  }

  public String toString() {
    return name;
  }

  public String getFieldName() {
    return fieldName;
  }

  public Object getObject() {
    return obj;
  }

  public boolean isEditable() {
    return editable;
  }

  public boolean isAdministrable() {
    return isAdministrable;
  }

  public void setAdministrable(boolean val) {
    isAdministrable = val;
  }

  public boolean isControllable() {
    return isControllable;
  }

  public void setControllable(boolean val) {
    isControllable = val;
  }

  public Object setValue(Object val) throws NumberFormatException {
    String clazzName = clazz.getName();
    Object newObj = null;

    if(val instanceof String || val == null) {
      String sval = (String) val;
      if(clazzName.equals("java.lang.Integer"))
	newObj = Integer.valueOf(sval);
      else if(clazzName.equals("java.lang.Boolean"))
	newObj = Boolean.valueOf(sval);
      else if(clazzName.equals("java.lang.Byte"))
	newObj = Byte.valueOf(sval);
      else if(clazzName.equals("java.lang.Character"))
	newObj = Character.valueOf(sval.charAt(0));
      else if(clazzName.equals("java.lang.Double"))
	newObj = Double.valueOf(sval);
      else if(clazzName.equals("java.lang.Float"))
	newObj = Float.valueOf(sval);
      else if(clazzName.equals("java.lang.Long"))
	newObj = Long.valueOf(sval);
      else if(clazzName.equals("java.lang.String"))
	newObj = sval;	// clone WHY?
    } else if(val.getClass().equals(obj.getClass())) {
      // same class type
      newObj = val;
    }

    Object oldObj = obj;
    obj = newObj;

    setNodeName();

    return oldObj;
  }
}
