package com.k_int.aggregator.dpp.timers;


import com.k_int.aggregator.dpp.timers.datamodel.CollectionUpdateTimerHDO;
import com.k_int.aggregator.dpp.timers.datamodel.UpdateTimerStatusEnum;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * Service to automatically run some queries against each CG collection and update the 
 * datamodel to include information such as top subjects, number of records, etc.
 * @author rpb rich@k-int.com
 * @version 1.0 20.03.12
 */
public class CollectionAutoUpdateService implements ApplicationContextAware {

    private int thread_pool_size = 1;
    private long execution_interval = 300000; // 5 mins by default - how often the thread looks to see if it should run the update task
    private ApplicationContext ctx;
    private SessionFactory factory;

    private static Log log   = LogFactory.getLog(CollectionAutoUpdateService.class);

    public CollectionAutoUpdateService() {
    }

    public void setApplicationContext(ApplicationContext ctx) {
        this.ctx = ctx;
    }

    public void setThreadPoolSize(int thread_pool_size) {
        this.thread_pool_size = thread_pool_size;
    }

    public int getThreadPoolSize() {
        return thread_pool_size;
    }

    public void setExecutionInterval(int execution_interval) {
        this.execution_interval = execution_interval;
    }

    public long getExecutionInterval() {
        return execution_interval;
    }
    
    public void setFactory(SessionFactory factory) {
        this.factory = factory;
    }
    
    public SessionFactory getFactory() {
        return this.factory;
    }

    public void init() {
        log.debug("Initialising CollectionAutoUpdateService, create thread pool - size="+thread_pool_size);
        ExecutorService thread_pool = Executors.newFixedThreadPool(thread_pool_size,
            new ThreadFactory() {
                public Thread newThread(Runnable r) {
                    Thread result = new Thread(r);
                    result.setDaemon(true);
                    result.setName("Collection Auto Update Thread "+result.hashCode());
                    return result;
                }
            }
        );
        
        log.debug("About to reset the timer data");
        resetUpdateTimer();
        
        log.debug("Setting collection auto update timer");
        Timer timer = new Timer(true);
        
        
        CollectionAutoUpdateTask task = new CollectionAutoUpdateTask(thread_pool, ctx);

        // Set the update thread to start 5 minutes from initial execution
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.MINUTE, 2);
        log.debug("Setting the first collection update check to occur at: " + cal.getTime().toString());
        timer.scheduleAtFixedRate(task, cal.getTime(), execution_interval);
    }

  /**
   * Reset the timer so that the system can start from scratch, etc.
   */
  private void resetUpdateTimer() {
    log.debug("reset update timer called");

    // Get a session to use..
    
    Session sess = null;
    Transaction tx = null;
    
    try {

        // Go and get the HDO and set the status to idle
        sess = factory.openSession();
        
        CollectionUpdateTimerHDO timer = CollectionUpdateTimerHDO.getTimer(sess);
        if ( timer == null ) {
            // No timer - create one
            log.debug("No timer to reset - creating one!");
            tx = sess.beginTransaction();
            
            timer = new CollectionUpdateTimerHDO();
            timer.setLastRunTime(new Date());
            timer.setNextDueTime(new Date());
            timer.setTimerStatus(UpdateTimerStatusEnum.IDLE);
            
            sess.save(timer);
            tx.commit();
        } else {
            // Set the status to idle so it can be picked up again
            log.debug("Timer found - resetting status");
            tx = sess.beginTransaction();
            timer.setTimerStatus(UpdateTimerStatusEnum.IDLE);
            sess.update(timer);
            tx.commit();        
        }
    } catch (HibernateException he) {
        log.error("HibernateException thrown when resetting the collection auto update timer: "+ he.getMessage());
        he.printStackTrace();
        
        if ( tx != null ) {
            tx.rollback();
        }
        
    } finally {
        if ( sess != null && sess.isOpen() ) {
            try {
                sess.close();
            } catch (Exception e) {
                log.error("Exception thrown when closing the session: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }
    
  }

}
