/*
 * JoinClass.java, 10 févr. 2006
 * 
 * This file is part of FilmZ.
 *
 * Copyright © 2006 Johan Cwiklinski
 *
 * File :               JoinClass.java
 * Author's email :     johan@x-tnd.be
 * Author's Website :   http://www.x-tnd.be/
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 */

/**
 * 
 */
package be.xtnd.films;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import be.xtnd.commons.db.Database;
import be.xtnd.films.gui.Messages;
import be.xtnd.gui.FilmzMain;


/**
 * @author Johan Cwiklinski
 *
 */
public abstract class JoinClass {
	protected static Database database;
	//protected static boolean special;
	protected Integer id;
	protected String valeur;
	/* variables bdd */
	protected static String sqlMessage;
	protected static int sqlError = 0;
	private int affectedRows;
	protected static Logger logger;
	protected static String table, /*table_specials, */pk, value, junction_table/*, junction_table_specials*//*, list_query*/;
	/** Nom de la table */
	//public static String TABLE;
	/** Nom de la table genres spéciaux */
	//public static String TABLE_SPECIALS;
	/** Nom du champ de clé primaire */
	//public static String PK;
	/** Nom du champ de valeur */
	//public static String VALUE;
	/** Nom de la table de jonction avec la table films */
	//public static String JUNCTION_TABLE; 
	/** Nom de la table de jonction avec la table films spéciaux */
	//public static String JUNCTION_TABLE_SPECIALS; 
	/** Requête de liste des éléments */
	//public static String LIST_QUERY;
	
	/** 
	 * Constructeur par défaut
	 * @param log logger à utiliser
	 * @param tbl nom de la table
	 * @param jonction table de jointure
	 * @param primary nom du champ clé primaire
	 * @param field nom du champ de données
	 * */
	public JoinClass(Logger log, String tbl, String jonction, String primary, 
			String field){
		database = FilmzMain.db;
		PropertyConfigurator.configure(ClassLoader.getSystemResource("be/xtnd/commons/log4j.properties"));
		/* initialisation de l'objet */
		logger = log;
		table = tbl;
		//table_specials = "spe_"+table;
		pk = primary;
		value = field;
		junction_table = jonction;
		//junction_table_specials = "spe_"+junction_table;
		//list_query = "SELECT * FROM "+((special)?table_specials:table)+" ORDER BY "+value+" ASC";
	}

	/**
	 * Initialise un objet joint en fonction de son id
	 * @param id id de l'objet à initialiser
	 */
	public void loadFromId(Integer id) {
		this.id = id;
		try {
			String query = "SELECT * FROM "+table+" WHERE "+pk+"="+id;
			//String query = "SELECT * FROM "+((special)?table_specials:table)+" WHERE "+pk+"="+id;
			ResultSet rs = database.execQuery(query);
			rs.first();
				this.valeur = rs.getString(value);
		} catch (SQLException e) {
			e.printStackTrace();
			logger.fatal("SQLException initializing "+value+" by id : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}
	}	

	/**
	 * Initialise un objet en fonction de sa valeur
	 * @param valeur valeur de l'objet à initialiser
	 */
	public void loadFromValue(String valeur) {
		this.valeur=valeur;
		try {
			ResultSet rs = database.execQuery(
					"SELECT * FROM "+table+" WHERE "+value+"='"+
					//"SELECT * FROM "+((special)?table_specials:table)+" WHERE "+value+"='"+
					Database.replace(valeur,"\'","\\\'")+"'");
			while(rs.next())
				this.id = rs.getInt(pk);
		} catch (SQLException e) {
			logger.fatal("SQLException initializing "+value+" by name : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}
	}
	
	/**
	 * Supprime l'objet 
	 * @param id identifiant de l'objet à supprimer
	 * @return entier nombre de lignes affectées 
	 */
	public int delete(Integer id){
		int result=0;
		try {
			result = database.execUpdate("DELETE FROM "+table+" WHERE "+pk+"="+id);
			//result = database.execUpdate("DELETE FROM "+((special)?table_specials:table)+" WHERE "+pk+"="+id);
		} catch (SQLException e) {
			if(e.getErrorCode()==1217 || e.getErrorCode()==1451) return -2; //mysql fk fails
			logger.fatal("SQLException deleting "+value+" : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}
		return result;
	}
	
	/**
	 * Enregistre un objet 
	 * @param nouveau true s'il s'agit d'un nouvel enregistrement,
	 * false s'il sagit d'une modification 
	 */
	public void enregistrer(boolean nouveau){
		String query = new String();
		if(nouveau){
			query = "INSERT INTO "+table+" VALUES(null, '"+
			//query = "INSERT INTO "+((special)?table_specials:table)+" VALUES(null, '"+
				Database.replace(valeur,"\'","\\\'")+"')";
		}else{
			query = "UPDATE "+table+" SET "+value+"='"+
			//query = "UPDATE "+((special)?table_specials:table)+" SET "+value+"='"+
				Database.replace(valeur,"\'","\\\'")+
				"' WHERE "+pk+"="+this.id;
		}
		try {
			affectedRows = database.execUpdate(query);
			if(nouveau){
				ResultSet rs = database.execQuery("SELECT LAST_INSERT_ID() as id");
				while(rs.next())
					this.id=rs.getInt("id");	
			}
		} catch (SQLException e) {
			logger.fatal("SQLException saving "+value+" (new="+nouveau+") : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}
	}

	/**
	 * Tableau d'objets par film
	 * @param idFilm : l'identifiant du film
	 * @return Renvoie liste des références des objets joints
	 */
	public static ArrayList<Integer> jonctionFilm(Integer idFilm){
		database = FilmzMain.db;
		ArrayList<Integer> array = new ArrayList<Integer>(5);
		
		String strSql = "SELECT * FROM "+junction_table+" WHERE "+Film.PK+"="+idFilm;
		//String strSql = "SELECT * FROM "+((special)?junction_table_specials:junction_table)+" WHERE "+Film.PK+"="+idFilm;
		try {
			ResultSet rs = database.execQuery(strSql);
			rs.beforeFirst();
			
			while(rs.next()){
				array.add(rs.getInt(pk));
			}	
			array.trimToSize();
		} catch (SQLException e) {
			logger.fatal("SQLException getting movie's "+value+" : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}
		array.trimToSize();
		return array;
	}
	
	/**
	 * Liste des films dans lesquels joue l'acteur
	 * 
	 * @return chaîne liste des titres de films
	 */
	public String fkTitre(){
		StringBuffer liste = new StringBuffer();
		try {
			String query = "SELECT titre FROM "+Film.TABLE+" INNER JOIN "
					+junction_table+" ON("+Film.TABLE+"."+Film.PK+"="+junction_table+"."+Film.PK+")"
					+" INNER JOIN "+table+" ON("+table+"."+pk+"="+junction_table+"."+pk+
					") WHERE "+table+"."+pk+"="+id;
			/*String query = "SELECT titre FROM "+((special)?Film.TABLE_SPECIALS:Film.TABLE)+" INNER JOIN "
					+((special)?junction_table_specials:junction_table)+" ON("+((special)?Film.TABLE_SPECIALS:Film.TABLE)+"."+Film.PK+"="+((special)?junction_table_specials:junction_table)+"."+Film.PK+")"
					+" INNER JOIN "+((special)?table_specials:table)+" ON("+((special)?table_specials:table)+"."+pk+"="+((special)?junction_table_specials:junction_table)+"."+pk+
					") WHERE "+((special)?table_specials:table)+"."+pk+"="+id;*/

			ResultSet rs = database.execQuery(query);
			int i = 0;
			while(rs.next()){
				if(i>0 && i<=15) liste.append("\n");
				if(i<=15) liste.append("- "+rs.getString("titre"));
				i++;
			}
			if(i>15){
				int more = i-15;
				liste.append("\n- ("+more+") "+Messages.getString("window.fk.moretitles"));
			}
			
			
		} catch (SQLException e) {
			logger.fatal("SQLException getting "+value+"'s movies titles : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}		
		return liste.toString();
	}

	/**
	 * Liste des objets joins par ordre alphabétique
	 * @return ArrayList des objets
	 */
	public ArrayList<String> listing(){
		ArrayList<String> list = new ArrayList<String>(5);
		String query = "SELECT "+value+" FROM "+table+" ORDER BY "+value+" ASC";
		//String query = "SELECT "+value+" FROM "+((special)?table_specials:table)+" ORDER BY "+value+" ASC";
		try{
			ResultSet rs = database.execQuery(query);
			rs.beforeFirst();
			while(rs.next()){
				list.add(rs.getString(value));
			}
		}catch(SQLException e){
			logger.fatal("SQLException listing "+value+" : "+e.getMessage()+" (code "+e.getErrorCode()+")");
			sqlMessage = "Message was : "+e.getMessage();
			sqlError = e.getErrorCode();
		}
		list.trimToSize();
		return list;
	}
	
	/**
	 * Enregistre les acteurs associés au film dans la base de données
	 * @param idFilm identifiant du film sur lequel intervenir
	 * @param clauseFrom clause from pour la requête
	 * @param deleted tableau des identifiants des objets à supprimer
	 * @param array tableau des objets à enregistrer
	 * @return entier résultat
	 */
	protected int enregistrerRefs(
			Integer idFilm, 
			String clauseFrom, 
			ArrayList<Integer> deleted,
			ArrayList<?> array){
		int result = -1;
		StringBuffer query;
		for(int i = 0 ; i < deleted.size() ; i++){
			query = new StringBuffer();
			query.append("DELETE FROM ");
			query.append(clauseFrom);
			query.append(" WHERE "+Film.PK+"="+idFilm+" AND "+pk+"="+deleted.get(i));
			try{
				result = database.execUpdate(query.toString());				
			}catch(SQLException e){
				logger.fatal("SQLException storing movie's "+value+" : "+e.getMessage()+" (code "+e.getErrorCode()+")");
				sqlMessage = "Message was : "+e.getMessage();
				sqlError = e.getErrorCode();
			}
		}
		
		JoinClass joined;
		for(int i = 0 ; i < array.size() ; i++){
			joined = (JoinClass) array.get(i);
			query = new StringBuffer();
			query.append("INSERT INTO ");
			query.append(clauseFrom);
			query.append(" VALUES("+idFilm+", "+joined.getId()+")");
			try{
				result = database.execUpdate(query.toString());
			}catch(SQLException e){
				if(e.getErrorCode()!=1062){ //acteur déjà attaché au film
					logger.fatal("SQLException storing movie's "+clauseFrom+" : "+e.getMessage()+" (code "+e.getErrorCode()+")");
					sqlMessage = "Message was : "+e.getMessage();
					sqlError = e.getErrorCode();
				}
			}
		}
		
		return result;
	}
	
	/**
	 * @return nombre d'enregistrements dans la base
	 */
	public abstract Integer getCount();
	
	/**
	 * @return Renvoie sqlMessage.
	 */
	public String getSqlMessage() {
		return sqlMessage;
	}

	/**
	 * @param sqlMessage sqlMessage à définir.
	 */
	public void setSqlMessage(String sqlMessage) {
		JoinClass.sqlMessage = sqlMessage;
	}

	/**
	 * @return Renvoie id.
	 */
	public Integer getId() {
		return id;
	}

	/**
	 * @param id id à définir.
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	/**
	 * @return Renvoie special.
	 */
	/*public boolean isSpecial() {
		return special;
	}*/

	/**
	 * @param spe special à définir.
	 */
	/*public void setSpecial(boolean spe) {
		special = spe;
	}*/

	/**
	 * @return Renvoie valeur.
	 */
	public String getValeur() {
		return valeur;
	}

	/**
	 * @param valeur valeur à définir.
	 */
	public void setValeur(String valeur) {
		this.valeur = valeur;
	}

	/**
	 * @return Renvoie sqlError.
	 */
	public static int getSqlError() {
		return sqlError;
	}

	/**
	 * @return Renvoie affectedRows.
	 */
	public int getAffectedRows() {
		return affectedRows;
	}

	/**
	 * @return Renvoie pk.
	 */
	public String getPk() {
		return pk;
	}

	/**
	 * @return Renvoie table.
	 */
	public String getTable() {
		return table;
	}

	/**
	 * @return Renvoie value.
	 */
	public String getValue() {
		return value;
	}
}
