/*
 * Created on May 24, 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.etixpert.evolution;

import java.util.*;
import java.sql.*;
import org.apache.log4j.*;
import org.hibernate.*;
/**
 * @author Suveges Gyorgy
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class PersistableObject {
	protected List primaryKeys;
	protected Map values = new HashMap();
	protected String table;
	protected Logger logger;
	
	
	/**
	 * 
	 */
	public PersistableObject(String table, List PK) {
		this.primaryKeys = PK;
		this.table = table;
		logger = Logger.getLogger("mainControl."+this.getClass().toString()+"."+table);
		
	}
	
	public PersistableObject(String table, String[] PK) {
	    this(table, (List) null);
	    
	    this.primaryKeys = arrayToList(PK);
	}
	
	public PersistableObject(String table, String PK) {
	    this(table, new String[]{PK});
	}
	
	private List arrayToList(String [] array) {
	    ArrayList l = new ArrayList();
	    for (int i=0;  i<array.length; i++) l.add(array[i]);
	    return l;
	}
	
	public PersistableObject(String table, List PK, Map values) {
	    this(table, PK);
	    this.values = values;
	}
	
	public PersistableObject(String table, String[] PK, Map values) {
	    this (table, (List) null, values);
	    this.primaryKeys = arrayToList(PK);
	    
	}
	
	
/*	public static String generateAttributeList(Map values) {
	    Iterator it = values.keySet().iterator();
		StringBuffer sb = new StringBuffer();
		boolean first = true;
		
		while (it.hasNext()) {
			String k = (String) it.next();
			if (first) {
				first=false;
				sb.append(k);
			} else {
				sb.append(", "+k);
			}
		}
		
		return sb.toString();
	}*/
	
/*	public static String generateValueList(Map values) {
	    Iterator it = values.keySet().iterator();
		StringBuffer sb = new StringBuffer();
		boolean first = true;
		while (it.hasNext()) {
			String k = (String) it.next();
			Object v = values.get(k);
			if (v==null) v="NULL"; else v="'"+v+"'";
			if (first) {
				first=false;
				sb.append(v);
			} else {
				sb.append(", "+v);
			}
		}
		
		return sb.toString();
	}*/
	
	private  String toString(Object[] keys) {
	    Iterator it = primaryKeys.iterator();
		StringBuffer sb = new StringBuffer();
		boolean first = true;
		int i = 0;
		while (it.hasNext()) {
			String k = (String) it.next();
			Object v = keys[i++];
			if (v==null) v="NULL"; else v="'"+v+"'";
			if (first) {
				first=false;
				sb.append(k+"="+v);
			} else {
				sb.append(", "+k+"="+v);
			}
		}
		
		return sb.toString();
	}
	
	
	protected static void fillInValues(ResultSet rs, Map values) throws SQLException {
	   ResultSetMetaData md = rs.getMetaData();
	   for (int i=1; i<=md.getColumnCount(); i++) {
	   		values.put(md.getColumnName(i), rs.getObject(i));
	   }
	   
	}
	
	public Set columnNames() {
	    return values.keySet();
	}
	
	public String getString(String column, String ifNull) {
	    return values.get(column)==null?ifNull:values.get(column).toString();
	}
	public Object get(String column) {
	    return values.get(column);
	}
	
	public void set(String column, Object value) {
	    
	    values.put(column, value);
	    
	}
	
	public void setLogger(Logger logger) {
		this.logger = logger;
	}
	
	public void delete(Connection conn) throws SQLException {
	    StringBuffer sb = new StringBuffer("delete from "+table+" where ");
	    for (int i=0; i<primaryKeys.size(); i++) {
		    sb.append(primaryKeys.get(i)).append(" = ?");
		    if (i<primaryKeys.size()-1) sb.append(" and ");
		}
	    String sql= sb.toString();
	    PreparedStatement ps = conn.prepareStatement(sql);
	    parameterize(ps, true, false);
	    logger.info("Deleting object: "+sql);
	    
	    int ar = ps.executeUpdate();
	    logger.info("affected rows: "+ar);
	    ps.close();
	}
	
	public void delete() throws SQLException {
	    
	    Transaction tx = null;
	   
	    Session session = HibernateUtil.currentSession();
	    tx = session.beginTransaction();
	    Connection conn = session.connection();
	    delete(conn);
	    logger.info("itt meg nem null");
	    tx.commit();
	   
	    logger.info("itt meg nem null");
	    HibernateUtil.closeSession();
	    logger.info("itt meg nem null");
	}

	
	public void update(Connection conn, String where) throws SQLException {
	    StringBuffer sb = new StringBuffer("update "+table+" set ");
	    Iterator it = values.keySet().iterator();
	    boolean first = true;
	    while (it.hasNext()) {
	        String c = (String) it.next();
	        
	        if (first) 
	            first = false;
	        else
	            sb.append(", ");
	           
	        sb.append(c+" = ?");
	    }
	    sb.append(" where ");
	    for (int i=0; i<primaryKeys.size(); i++) 
	    {
	    	if (i == 0)
	    		sb.append(primaryKeys.get(i)).append(" = ?");
	    	else { 
	    		sb.append(" AND ");
	    		sb.append(primaryKeys.get(i)).append(" = ?");
	    	}
	    }
	    
	    if (where.length() > 0) {
	    	sb.append(" AND ");
	    	sb.append(where);
	    }
	    
	    String sql= sb.toString();
	    logger.info("updating object: "+sql);
	    PreparedStatement ps = conn.prepareStatement(sql);
	    parameterize(ps, true, true);
	    int ar = ps.executeUpdate();
	    
	    logger.info("affected rows: " + ar);
	    ps.close();
	    
	}

	public void update(Connection conn) throws SQLException {
		update(conn, "");
	}

	public void update() throws SQLException {
	    Session session = HibernateUtil.currentSession();
	    Transaction tx = null;
	    
		 tx = session.beginTransaction();
		 Connection conn = session.connection();
		    
		 update(conn);
		 tx.commit();
	  
	 
	    HibernateUtil.closeSession();
	    
	}
	
	public void insert(Connection conn) throws EvolutionException, SQLException {
	    StringBuffer sb = new StringBuffer("insert into "+table+ " (");
	    Iterator it = values.keySet().iterator();
	    boolean first = true;
	    while (it.hasNext()) {
	        
	        String c = (String) it.next();
	        
	        if (first) first = false; else sb.append(", ");
	        sb.append(c);
	    }
	    sb.append(") VALUES("); for (int i=0; i<values.keySet().size(); i++) sb.append(" ?").append(i<values.keySet().size()-1?", ":"");
	    sb.append(")");
	    String sql = sb.toString();
	    
	    logger.info("Inserting row: "+ sql);
	    PreparedStatement ps = conn.prepareStatement(sql);
	    parameterize(ps, false, true);
	    ps.executeUpdate();
	    ps.close();
	}
	
	public void insert(Connection conn, boolean update) throws EvolutionException, SQLException {
	    StringBuffer sb = new StringBuffer("insert into "+table+ " (");
	    Iterator it = values.keySet().iterator();
	    boolean first = true;
	    while (it.hasNext()) {
	        String c = (String) it.next();
	        if (first) first = false; else sb.append(", ");
	        sb.append(c);
	    }
	    sb.append(") VALUES("); for (int i=0; i<values.keySet().size(); i++) sb.append(" ?").append(i<values.keySet().size()-1?", ":"");
	    sb.append(")");
	    String sql = sb.toString();
	    
	    logger.info("Inserting row: "+ sql);
	    PreparedStatement ps = conn.prepareStatement(sql);
	    parameterize(ps, false, true);
	    ps.executeUpdate();
	    
	    if (update) {
		    Statement st = conn.createStatement();
		    ResultSet rs = st.executeQuery("Select last_value from seq_" + table);
		    if (rs.next()) {
		    	for (Iterator it2 = primaryKeys.iterator(); it2.hasNext(); ) {
		    		String key = (String) it2.next();
		    		set(key, rs.getObject(1));
		    	}
		    }
	    }
	    
	    ps.close();
	}

	public void insert() throws EvolutionException, SQLException {
	    Session session = HibernateUtil.currentSession();
	    Transaction tx = null;
		
		 tx = session.beginTransaction();
		 Connection conn = session.connection();
		 insert(conn);
		    
		  tx.commit();
	  
	    HibernateUtil.closeSession();
	}
	
	protected void parameterize(PreparedStatement ps, boolean key, boolean values) throws SQLException {
	    int i = 1;
	    if (values) {
	        Iterator it = this.values.keySet().iterator();
	        
	        while (it.hasNext()) {
	            Object o = this.values.get(it.next());
	            logger.info("setting parameter "+i+" : "+o);
	           
	            ps.setObject(i, o);
	            i++;
	        }
	    }
	    
	    if (key) {
	        Iterator it = primaryKeys.iterator();
	        while (it.hasNext()) {
	            Object o = this.values.get(it.next());
	            logger.info("Adding "+i+". parameter: "+o);
	            ps.setObject(i, o);
	            i++;
	        }
	    }
	    
	    
	}
	
	
	public void load(Connection conn) throws SQLException, EvolutionException {
	    Object[] pkValues = new Object[primaryKeys.size()];
		for (int i=0; i<primaryKeys.size(); i++) {
		    Object o = values.get(primaryKeys.get(i));
		    if (o==null)
		        throw new EvolutionException("primary key must be given to load");
		    pkValues[i]=o;
		}
	    
	    if (pkValues.length != primaryKeys.size())
		    throw new EvolutionException("The number of primary attributes must match for the table "+table);
	   
		
		StringBuffer sb = new StringBuffer("select * from "+table+" where "); 
		
		for (int i=0; i<pkValues.length; i++) {
		    sb.append(primaryKeys.get(i)).append(" = ?");
		    if (i<pkValues.length-1) sb.append(" and ");
		}
		
		String sql = sb.toString(); 
		PreparedStatement stat = conn.prepareStatement(sql);
		parameterize(stat, true, false);
		logger.info("loading object: "+sql);
		ResultSet rs = stat.executeQuery();
		values = new HashMap();
		if (!rs.next()) 
		    throw new EvolutionException("PersistableObject on the key: "+toString(pkValues)+ " not found");
		    
		fillInValues(rs, values);
		
		rs.close();
		stat.close();	
	}
	
	public void load() throws SQLException, EvolutionException {
	    Session session = HibernateUtil.currentSession();
	    Transaction tx = session.beginTransaction();
	    Connection conn = session.connection();
	    load(conn);
		tx.commit();
		HibernateUtil.closeSession();
	}

	
	
	public PersistableObject getReferencedObject(Connection conn, List attr, String table, List refPK) throws SQLException, EvolutionException {
	    if (attr.size()!=refPK.size())
	        throw new EvolutionException("Foreign key doesn't match to the referenced primary key");
	    PersistableObject po = new PersistableObject(table, refPK);
	    
	    for (int i=0; i<attr.size(); i++)
	        po.set((String) refPK.get(i),get((String) attr.get(i)));
	    try {
	        po.load(conn);
	    } catch (EvolutionException e) {
	        return null;
	    }
	    return po;
	    
	    
	}
	
	public PersistableObject getReferencedObject(List attr, String table, List refPK) throws SQLException, EvolutionException {
	    Session session = HibernateUtil.currentSession();
	    Transaction tx = session.beginTransaction();
	    Connection conn = session.connection();
	    PersistableObject po = getReferencedObject(conn, attr, table, refPK);
	    tx.commit();
		HibernateUtil.closeSession();
		return po;
	    
	    
	}
	
	public PersistableObject getReferencedObject(String[] attr, String table, String[] refPK) throws SQLException, EvolutionException {
	    List attr1 = arrayToList(attr);
	    List refPK1 = arrayToList(refPK);
	    return getReferencedObject(attr1, table, refPK1);
	}
	
	public PersistableObject getReferencedObject(Connection conn, String[] attr, String table, String[] refPK) throws SQLException, EvolutionException {
	    List attr1 = arrayToList(attr);
	    List refPK1 = arrayToList(refPK);
	    return getReferencedObject(conn, attr1, table, refPK1);
	}
	
	public PersistableObject getReferencedObject(String attr, String table, String refPK) throws SQLException, EvolutionException {
	    return getReferencedObject(new String[]{attr}, table, new String[]{refPK});
	}
	
	public PersistableObject getReferencedObject(Connection conn, String attr, String table, String refPK) throws SQLException, EvolutionException {
	    return getReferencedObject(conn, new String[]{attr}, table, new String[]{refPK});
	}
	
	
	public List getMNObjects(Connection conn, String table, List refPK, Map map) throws SQLException {
	    List refAttr = mapKeys(primaryKeys, map); 
		
		StringBuffer sb = new StringBuffer("select * from "+table+" where ");
		Iterator it = refAttr.iterator();
		while (it.hasNext()) 
		    sb.append(it.next()).append(" = ?");
		
		
		String sql = sb.toString();
		logger.info("Getting referenced objects from "+table +"(m:n): "+sql);
		PreparedStatement ps = conn.prepareStatement(sql);
		parameterize(ps, true, false);
		
		ResultSet rs = ps.executeQuery();
		
		ArrayList al = new ArrayList();
		while (rs.next()) {
		    Map values = new HashMap();
		    fillInValues(rs, values);
		    al.add(new PersistableObject(table, refPK, values));
		}
		
		rs.close();
		ps.close(); 
		return al;
	}
	
	public List getMNObjects(String table, List refPK, Map map) throws SQLException {
	    Session session = HibernateUtil.currentSession();
	    Transaction tx = session.beginTransaction();
	    Connection conn = session.connection();
		
		List al = getMNObjects(conn, table, refPK, map);
		tx.commit();
		HibernateUtil.closeSession();
		return al;
	}
	
	public List getMNObjects(Connection conn, String table, String[] refPK, Map map) throws SQLException {
	    List refPK1 = arrayToList(refPK);
	    return getMNObjects(conn, table, refPK1, map);
	}
	
	public List getMNObjects(String table, String[] refPK, Map map) throws SQLException {
	    List refPK1 = arrayToList(refPK);
	    return getMNObjects(table, refPK1, map);
	}
	
	public List getMNObjects(Connection conn, String table, String refPK, Map map) throws SQLException {
	    return getMNObjects(conn, table, new String[]{refPK}, map);
	}
	
	public List getMNObjects(String table, String refPK, Map map) throws SQLException {
	    return getMNObjects(table, new String[]{refPK}, map);
	}
	
	
	protected List mapKeys(List PK, Map map) {
	    if (map==null) return primaryKeys; 
	    ArrayList l = new ArrayList();
	    for (int i=0; i<PK.size(); i++)
	        l.add(map.get(PK.get(i)));
	    
	    return l;
	}
	
	public List primaryKey() {
	    return primaryKeys;
	}
	
	public Map getValues() {
	    return values;
	}
	
	public void setValues(Map values) {
	    this.values = values;
	}
}
