/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.dgfip.xemelios.batch;

import fr.gouv.finances.dgfip.xemelios.data.impl.pool.PoolManager;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;

/**
 * Classe regroupant les elements communs aux traitements batch
 * @author CBO
 */
public abstract class Batch extends AbstractBatch {
    private Properties props;
    private List<String> postProcessors;
    private boolean started = false;

    private static final String EXECUTION_FORCEE = "-force";
//    private static final String TRACE_DEBUT_TRAITEMENT = "Démarrage du traitement le {0} à {1}";
//    private static final String TRACE_FIN_TRAITEMENT = "Fin du traitement le {0} à {1}";
//    private static final String TRACE_INTERRUPTION_ERREUR_INATTENDUE = "Une erreur inattendue à interrompu le traitement";
//  private TraitementBO traitement;

    /**
     * @param args Arguments éventuels de la ligne de commande
     */
    public Batch(String args[]) {
        super(args);
        BatchRunner.getInstance().registerBatch(this);
    }

    @Override
    protected void finalize() throws Throwable {
        BatchRunner.getInstance().unregisterBatch(this);
        super.finalize();
    }


    @Override
    protected void initialize() throws Exception {
        super.initialize();
        checkDirectories();
    }

    protected void checkDirectories() throws Exception {
//      DirectoryUtils.checkDirectories();
    }

    @Override
    protected void processTerminaison() throws Exception {
        Connection connection = null;
        try {
            connection = PoolManager.getInstance().getConnection();
//      JdbcUtils.executeUpdate(connection, "call pck_demat_tache_utils.termineTache(?)", new SimplePStmtBinderBuilder().add(typeTraitementRefCode()).toPStmtBinder());
        } finally {
            PoolManager.getInstance().releaseConnection(connection);
        }
    }

    @Override
    protected boolean canStart() throws Exception {
//    Connection connection = null;
//    CallableStatement callableStatement = null;
//    String executionForcee = "0";
//    try {
//      connection = PoolManager.getInstance().getConnection();
//      callableStatement = connection.prepareCall("{ ?  = call pck_demat_tache_utils.lanceTache(?, ?)}");
//      callableStatement.registerOutParameter(1, Types.INTEGER);
//      callableStatement.setString(2, typeTraitementRefCode());
//      if(isForced()) {
//        executionForcee="1";
//      }
//      callableStatement.setString(3, executionForcee);
//
//      callableStatement.execute();
//      return callableStatement.getInt(1) == 1;
//    } finally {
//      try {
//        if (callableStatement != null) {
//          callableStatement.close();
//        }
//      } finally {
//        PoolManager.getInstance().releaseConnection(connection);
//      }
//    }
        return true;
    }

    public boolean isForced() {
        if (getArgs() != null) {
            for (String arg : getArgs()) {
                if (arg.equalsIgnoreCase(EXECUTION_FORCEE)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Implémentation du traitement
     */
    protected abstract void doProcess() throws Exception;

    /**
     * @return Resumé du traitement exemple: 35 fichiers traites avec succès, 3 dossiers en erreur
     */
    abstract public String getResumeTraitement();

    /**
     * @return Le refCode du type de traitement
     */
    abstract public String typeTraitementRefCode();

//  protected TraitementBO getTraitement() {
//    return this.traitement;
//  }
    @Override
    protected void process() throws Exception {
        startRapportTraitements();
        try {
            doProcess();
            started = true;
        } catch (Throwable e) {
//      TraitementManager.traceMessage(traitement, Gravite.ERREUR_BLOQUANTE, TRACE_INTERRUPTION_ERREUR_INATTENDUE, e);
        } finally {
            endRapportTraitements();
        }
    }

    public boolean isStarted() { return started; }

    /**
     * Démarre le rapport du traitement
     */
    private void startRapportTraitements() throws SQLException {
//    traitement = TraitementManager.debuteTraitement(typeTraitementRefCode());
//    Date now = new Date();
//    String messageDeDemarrage = StringUtils.formatPattern(TRACE_DEBUT_TRAITEMENT, DateUtils.formatDate(now), DateUtils.formatTime(now));
//    TraitementManager.traceMessage(traitement, Gravite.INFORMATION, messageDeDemarrage);
    }

    /**
     *  Met fin au rapport traitememt
     */
    private void endRapportTraitements() throws SQLException {
//    Date now = new Date();
//    String messageDeDemarrage = StringUtils.formatPattern(TRACE_FIN_TRAITEMENT, DateUtils.formatDate(now), DateUtils.formatTime(now));
//    TraitementManager.traceMessage(traitement, Gravite.INFORMATION, messageDeDemarrage);
//    TraitementManager.termineTraitement(traitement, getResumeTraitement());
    }

    public Properties getProps() {
        return props;
    }

    public void setProps(Properties props) {
        this.props = props;
    }

    public List<String> getPostProcessors() {
        return postProcessors;
    }

    public void setPostProcessors(List<String> postProcessors) {
        this.postProcessors = postProcessors;
    }

    public abstract String getInformations();

    public abstract class RunnableBatch extends Thread {
        private ThreadPool notifier = null;
        public void setNotifier(ThreadPool th) {
            this.notifier=th;
        }
        public ThreadPool getNotifier() { return notifier; }
        public abstract String getInformations();
    }
    public static class ThreadPool<T extends RunnableBatch> {
        private static final Logger logger = Logger.getLogger(ThreadPool.class);
        private List<T> threads;
        private int maxConcurrentThreads;
        private int runningThreads = 0;
        public ThreadPool(List<T> threads) {
            super();
            this.threads=threads;
        }
        public void notifyThreadStop(T thread) {
            threads.remove(thread);
            runningThreads--;
            synchronized(this) {
                for(RunnableBatch aThread:threads) {
                    if(!aThread.isAlive()) {
                        aThread.setNotifier(this);
                        aThread.start();
                    }
                }
            }
        }
        public void start(int maxConcurrentThreads) {
            logger.debug("starting with "+maxConcurrentThreads+" max threads");
            this.maxConcurrentThreads=maxConcurrentThreads;
            while(runningThreads<maxConcurrentThreads && threads.size()>0) {
                for(int i=0;i<threads.size();i++) {
                    if(!threads.get(i).isAlive()) {
                        threads.get(i).setNotifier(this);
                        threads.get(i).start();
                        runningThreads++;
                    }
                }
            }
        }
        public int getRunningThreadCount() { return runningThreads; }
        public List<T> getThreads() { return threads; }
    }

}
