/*
 * Collection.java, 3 juin 2006
 * 
 * This file is part of FilmZ.
 *
 * Copyright © 2006 Johan Cwiklinski
 *
 * File :               Collection.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.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

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

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

/**
 * @author Johan Cwiklinski
 *
 */
public class Collection {
	private static Database db = FilmzMain.db;
	private int id;
	private String nom;
	private String table;
	private boolean prive = false;
	static Logger logger = Logger.getLogger(Collection.class.getName());
	private String[] names = {"","acteurs","genres","origines","realisateurs"};
	/** Nom de la clé primaire */
	public static final String PK = "id_collection";
	/** Nom de la table */
	public static final String TABLE = "collections";
	/** Requête pour affichage de la liste */
	public static final String LIST_QUERY = "SELECT * FROM "+TABLE;
	/* variables bdd */
	private String sqlMessage;
	private int sqlError = 0;
	private int affectedRows;


	/**
	 * 
	 *
	 */
	public Collection(){}
	
	/**
	 * 
	 * @param id
	 */
	public Collection(Integer id){
		PropertyConfigurator.configure(ClassLoader.getSystemResource("be/xtnd/films/log4j.properties"));
		String requete = "SELECT * FROM "+TABLE+" WHERE id_collection="+id;
		try {
			ResultSet rs = db.execQuery(requete);
			rs.first();
			this.id = id;
			this.nom = rs.getString("nom");
			this.table = rs.getString("table");
			this.prive = rs.getBoolean("prive");
		} catch (SQLException e) {
			logger.fatal("SQLException loading collection by id "+e.getMessage() + " (code" + e.getErrorCode() + ")");
			sqlMessage = e.getMessage();
			sqlError = e.getErrorCode();
			e.printStackTrace();
		}		
	}
	
	/**
	 * 
	 * @param name
	 */
	/*public Collection(String name){
		PropertyConfigurator.configure(ClassLoader.getSystemResource("be/xtnd/films/log4j.properties"));
		String requete = "SELECT * FROM "+TABLE+" WHERE nom='"+name+"'";
		try {
			ResultSet rs = db.execQuery(requete);
			rs.first();
			this.id = rs.getInt("id_collection");
			this.nom = rs.getString("nom");
			this.table = rs.getString("table");
			this.prive = rs.getBoolean("prive");
		} catch (SQLException e) {
			logger.fatal("SQLException loading collection by name "+e.getMessage() + " (code" + e.getErrorCode() + ")");
			e.printStackTrace();
		}		
	}*/
	
	/**
	 * 
	 * @param name
	 */
	public void changeTo(String name){
		String requete = "SELECT * FROM "+TABLE+" WHERE nom='"+name+"'";
		try {
			ResultSet rs = db.execQuery(requete);
			rs.first();
			this.id = rs.getInt("id_collection");
			this.nom = rs.getString("nom");
			this.table = rs.getString("table");
			this.prive = rs.getBoolean("prive");
		} catch (SQLException e) {
			logger.fatal("SQLException loading collection by name "+e.getMessage() + " (code" + e.getErrorCode() + ")");
			sqlMessage = e.getMessage();
			sqlError = e.getErrorCode();
			e.printStackTrace();
		}			
	}
	
	/**
	 * @param withprive selectionne les enregistrements marqués privé
	 * si le code eparental a été entré 
	 * @return liste des noms des catégories
	 */
	public static ArrayList<String> liste(boolean withprive){
		db = FilmzMain.db;
		ArrayList<String> list = new ArrayList<String>(10);
		String requete = "SELECT nom FROM " + TABLE;
		requete += (FilmzMain.isParentalActive() && withprive)?"":" WHERE prive=false";
		try {
			ResultSet rs = db.execQuery(requete);
			while(rs.next()){
				list.add(rs.getString("nom"));
			}
		} catch (SQLException e) {
			logger.fatal("SQLException listing collection "+e.getMessage() + " (code" + e.getErrorCode() + ")");
			//sqlMessage = e.getMessage();
			//sqlError = e.getErrorCode();
			e.printStackTrace();
		}
		list.trimToSize();
		return list;
	}

	/**
	 * 
	 * @param nouveau
	 * @return résultat
	 */
	public int enregistrer(boolean nouveau){
		//this.table = "testing";
		int result = 0;
		boolean tablesOk = true;
		Connection c = db.getConnection();
		Statement stmt = null;
		StringBuilder requete = new StringBuilder();
		requete.append((nouveau)?"INSERT INTO " + TABLE + " SET ":"UPDATE " + TABLE + " SET ");
		requete.append("`nom`='" + Database.replace(this.nom,"\'","\\\'") + "', ");
		requete.append("`table`='" + this.table + "', ");
		requete.append("`prive`=" + this.prive);
		requete.append((nouveau)?"":" WHERE " + PK + "=" + this.id);
		
		System.err.println(requete.toString());
		
		try {
			c.setAutoCommit(false);
			stmt = c.createStatement();
			//Savepoint sp = c.setSavepoint();
			result = stmt.executeUpdate(requete.toString());
			affectedRows = result;
			if(nouveau) tablesOk = createTables(this.table, this.prive);
			if(!tablesOk) return -10;
			c.commit();
		} catch (SQLException e) {
			try {c.rollback();} catch (SQLException e1) {e1.printStackTrace();}
			logger.fatal("SQLException storing collection (new="+nouveau+") " + e.getMessage() + " (code was " + e.getErrorCode() +")");
			sqlMessage = e.getMessage();
			sqlError = e.getErrorCode();
			e.printStackTrace();
		} finally {
			try {c.setAutoCommit(true);} catch (SQLException e) {e.printStackTrace();}
		}
		
		return result;
	}
	
	private boolean createTables(String table, boolean prive){
		Connection c = db.getConnection();
		Statement stmt = null;
	    StringBuilder sb = new StringBuilder();
		try {
			for(String n : names){
				if(tableExists(c, table + n)){
					logger.warn("Table " + table + n + " already exists !");
					return false;					
				}
			}
			stmt = c.createStatement();
			BufferedReader in = new BufferedReader(new FileReader(".config/create_collection.sql"));
			String str;
			while ((str = in.readLine()) != null) {
				str = str.replaceAll("\\{TABLE_NAME\\}", table);
				str = str.replaceAll("\\{ISRESTRICT\\}", (prive)?"r_":"");
				if(str.startsWith("CREATE TABLE") && sb.length()>0){
					stmt.addBatch(sb.toString());
					sb = new StringBuilder();
				}
				if(!str.startsWith("--")) sb.append(str);
				sb.trimToSize();
			}
			if(sb.length() > 0) stmt.addBatch(sb.toString()); //for the last query
			in.close();
			stmt.executeBatch();
			return true;
		} catch (BatchUpdateException be){
			logger.fatal("SQLException while creating collection " + be.getMessage() + " (code was " + be.getErrorCode() + ")");
			sqlMessage = be.getMessage();
			sqlError = be.getErrorCode();
			be.printStackTrace();
			//this replaces the failing rollback for mysql
			int[] results = be.getUpdateCounts();
			logger.debug("Application will now try to delete newly created tables since MySQL innoDB doesn't support rollback on create table");
			for(int i = 0 ; i < results.length ; i++){
				if(results[i] == 0){
					try {
						String qry = "DROP TABLE " + table + names[i];
						db.execUpdate(qry);
					} catch (SQLException e) {
						logger.warn("Error while deleting table " + table + names[i] + " after collections tables crate failed");
						e.printStackTrace();
					}
				}
			}
			//the following line is unused since InnoDB never rollback on create table (@see mysql documentation)
			//try {c.rollback();} catch (SQLException e) {e.printStackTrace();}
			return false;
		} catch (SQLException e1) {
			try{c.rollback();} catch (SQLException e){e.printStackTrace();}
			logger.fatal("SQLException while apptemting to create collection " + e1.getMessage() + " (code was " + e1.getErrorCode() + ")");
			sqlMessage = e1.getMessage();
			sqlError = e1.getErrorCode();
			e1.printStackTrace();
			return false;
		} catch (IOException e) {			
		} finally {
			try {
				if(stmt != null) stmt.close();
			} catch (SQLException e) {e.printStackTrace();}
		}
		
		return false;
	}
	
	private boolean tableExists(Connection connection, String nomTable) throws SQLException{
		   boolean existe;
		   DatabaseMetaData dmd = connection.getMetaData();
		   ResultSet tables = dmd.getTables(connection.getCatalog(),null,nomTable,null);
		   existe = tables.next();
		   tables.close();
		   return existe;
		}

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

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

	/**
	 * @return Renvoie nom.
	 */
	public String getNom() {
		return nom;
	}

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

	/**
	 * @return Renvoie prive.
	 */
	public boolean isPrive() {
		return prive;
	}

	/**
	 * @param prive prive à définir.
	 */
	public void setPrive(boolean prive) {
		this.prive = prive;
	}

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

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

	/**
	 * @return Renvoie sqlMessage.
	 */
	public String getSqlMessage() {
		return this.sqlMessage;
	}

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

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

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