package brn.distsim.ormapper.class2hbm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.python.core.PyArray;
import org.python.core.PyDictionary;
import org.python.core.PyFloat;
import org.python.core.PyInstance;
import org.python.core.PyInteger;
import org.python.core.PyJavaInstance;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyStringMap;
import org.python.core.PyTuple;

import brn.distsim.ormapper.util.ConstantFieldAccessor;

public class ReflectedPyClass extends ReflectedClass {

  private String name;
  private PyInstance pyInstance;
  private Object proxyName;

  public ReflectedPyClass(MapGenerator mapGenerator, PyInstance pyInstance, String name) {
    this.name = pyInstance.instclass.__name__;//((Class)pyInstance.__class__.__tojava__(Object.class)).getName();
    this.pyInstance = pyInstance;
    this.proxyName = Object.class.getName();
    this.map = mapGenerator;
    this.subs = new Vector();
    this.tableName = map.tableNameFor(this.name);
    this.persistent = true; // default to component until told otherwise
    map.rClasses.put(name, this); // this must happen to avoid
                        // infinite
    // regress...
    this.reflect(); // here
  }

  /*
   * (non-Javadoc)
   * @see brn.distsim.ormapper.class2hbm.ReflectedClass#getName()
   */
  public String getName() {
    return name;
  }

  /*
   * (non-Javadoc)
   * @see brn.distsim.ormapper.class2hbm.ReflectedClass#reflect()
   */
  protected void reflect() {
    props = new ArrayList();
    reflectClass(pyInstance, name);
  }

  /**
   * called to get the properties of a class, or add properties from a
   * superclass
   */
  private void reflectClass(PyInstance pyInstance, String pyInstName) {
    
    // empty getprops and setprops if a getter and setter pair
    // should come from one class and not a class and its superclass
    try {
      PyStringMap fields = (PyStringMap) pyInstance.__dict__;
      PyList list = fields.items();
      
      for (int i = 0; i < list.__len__(); ++i) {
        PyTuple tuple = (PyTuple) list.__getitem__(i);
        PyString pyName = (PyString) tuple.__finditem__(0);
        PyObject value = tuple.__finditem__(1);
        
//        // static fields aren't mapped
//        if (Modifier.isStatic(field.getModifiers()))
//          continue;
        String name = (String) pyName.__tojava__(String.class);
        if (name.equals("simulationId")
            ||name.equals("subclass")
            ||name.equals("entity")
            ||name.equals("primary_key"))
          throw new RuntimeException("overwriting existing property in jython code: "+name);
          
        Class type = null; //Object.class;
        if (value instanceof PyInteger)
          type = Integer.class;
        else if (value instanceof PyFloat)
          type = Double.class;
        // Not mapped because the pypersister is not able to map longs
//        else if (value instanceof PyLong)
//          type = Long.class;
        else if (value instanceof PyString)
          type = String.class;
        else if (value instanceof PyJavaInstance) {
          type = (Class) value.fastGetClass().__tojava__(Object.class);
          if (List.class.isAssignableFrom(type))
            type = List.class;
          else if (Map.class.isAssignableFrom(type))
            type = Map.class;
          else if (Set.class.isAssignableFrom(type))
            type = Set.class;
          else if (Collection.class.isAssignableFrom(type))
            type = Collection.class;
        } else if (value instanceof PyInstance) {
          type = (Class) Object.class;
        } else if (value instanceof PyArray) {
          Object array = ((PyArray) value).getArray();
          type = array.getClass();
        } else if (value instanceof PyList) {
          type = List.class;
        } else if (value instanceof PyDictionary) {
          type = Map.class;
        }
          
        
        if (type == null)
          System.err.println("type not mapped: " + value.getClass().getName());
        
        
        ReflectedProperty prop = map.makeProperty(pyInstName, name, type);
        if (prop != null)
          props.add(prop);
      }
    } catch (SecurityException e) {
      // ignore!?
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.distsim.ormapper.class2hbm.ReflectedClass#addSuperclassProps()
   */
  protected void addSuperclassProps() {
    // add properties from superclasses
//    if (null != clazz)
//      addSuperclassProps(clazz.getSuperclass());
  } // with helper...

  /**
   * dump the OR-Mapping XML for a root class and all of its subclasses that
   * share a table and UID
   */
  protected void getXML(int level) {
    // dumps in map.buf
    this.buf = map.buf;

    // debug...
    map.emitPrefix(level);
    buf.append("<!-- ").append(name).append(" root -->\n");
    // a root class -- polymorphic style
    map.emitPrefix(level);
    buf.append("<class entity-name=\"").append(name).append("\" name=\"").append(
        proxyName).append("\" proxy=\"").append(
            proxyName).append("\" table=\"").append(
            tableName).append("\">\n");

    map.emitPrefix(level + 1);
    buf.append("<tuplizer entity-mode=\"pojo\" class=\"").append(PyPersister.class.getName()).append("\" />\n");
    buf.append("<id ").append("type=\"").append("long").append(
        "\" column=\"").append("primary_key").append("\">\n");
    map.emitPrefix(level + 2);
    buf.append("<generator class=\""+IdGenerator.class.getName()+"\">\n");
//    map.emitPrefix(level + 3);
//    buf.append("<param name=\"simulationId\">").append(simulationId).append("</param>\n");
    map.emitPrefix(level + 2);
    buf.append("</generator>\n");
    map.emitPrefix(level + 1);
    buf.append("</id>\n");

//    map.emitPrefix(level + 1);
//    buf.append("<composite-id>");
//    map.emitPrefix(level + 2);
//    buf.append("<key-property name=\"simulationId\" type=\"int\" access=\""+ConstantFieldAccessor.class.getName()+"r\"/>");
//    map.emitPrefix(level + 2);
//    buf.append("<key-property name=\"id\" type=\"int\"/>");
//    map.emitPrefix(level + 1);
//    buf.append("</composite-id>");

//    map.emitPrefix(level + 1);
//    buf.append("<id ").append("type=\"").append("long").append(
//        "\" column=\"").append("primary_key").append("\">\n");
//    map.emitPrefix(level + 2);
//    buf.append("<generator class=\"native\"/>\n");
//    map.emitPrefix(level + 1);
//    buf.append("</id>\n");
    // special properties for identifying simulation, origin and class of
    // the result
    map.emitPrefix(level + 1);
    buf.append("<property name=\"subclass\" type=\"string\" access=\""+ConstantFieldAccessor.class.getName()+"\"/>\n");
    map.emitPrefix(level + 1);
    buf.append("<property name=\"simulationId\" type=\"int\" access=\""+ConstantFieldAccessor.class.getName()+"\"/>\n");
    map.emitPrefix(level + 1);
    buf.append("<property name=\"entity\" type=\"string\" access=\""+ConstantFieldAccessor.class.getName()+"\"/>\n");

    // now the properties already classified by heuristic
    Iterator iter = props.iterator();
    while (iter.hasNext()) {
      ReflectedProperty prop = (ReflectedProperty) iter.next();
      if (prop.isUid)
        continue; // done above
      // we're root and top component level
      prop.getXML(level + 1, buf);
      map.cycleBuster = new HashSet();
    }
    map.emitPrefix(level);
    buf.append("</class>\n");
    // now the subclasses
    int len = subs.size();
    for (int i = 0; i < len; i++) {
      ReflectedClass rsc = (ReflectedClass) subs.get(i);
      rsc.getXMLasSubclass(level);
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.distsim.ormapper.class2hbm.ReflectedClass#getXMLasSubclass(int)
   */
  protected void getXMLasSubclass(int level) {
    // dumps in map.buf
    this.buf = map.buf;
    // TODO superclass  
    assert (pyInstance.instclass.__bases__.size() <= 1);
    assert (((Class) pyInstance.instclass.__bases__.get(0)).getName().equals(Object.class.getName()));
    String superclass = Object.class.getName(); 
    // debug...
    map.emitPrefix(level);
    ;
    buf.append("<!-- ").append(name).append(" -->\n");
    // a sub class -- polymorphic style
    map.emitPrefix(level);
    buf.append("<joined-subclass entity-name=\"").append(name).append("\" name=\"").
      append(proxyName).append("\" proxy=\"").
      append(proxyName).append("\" table=\"").append(tableName).append("\" extends=\"")
        .append(superclass).append("\">\n");
    map.emitPrefix(level + 1);
    buf.append("<tuplizer entity-mode=\"pojo\" class=\"").append(PyPersister.class.getName()).append("\" />\n");
    buf.append("<key column=\"primary_key\" on-delete=\"cascade\"/>\n");
    // now the properties already classified by heuristic
    Iterator iter = props.iterator();
    while (iter.hasNext()) {
      ReflectedProperty prop = (ReflectedProperty) iter.next();
      if (prop.isUid)
        continue; // done above
      prop.getXML(level + 1, buf);
    }
    map.emitPrefix(level);
    buf.append("</joined-subclass>\n");
    // now the subclasses
    int len = subs.size();
    for (int i = 0; i < len; i++) {
      ReflectedClass rsc = (ReflectedClass) subs.get(i);
      rsc.getXMLasSubclass(level);
    }
  }

  public String getSuperclass() {
    return Object.class.getName();
  }


}
