/*
 * $Id: ConverterInfo.java,v 1.18 2007/08/16 11:10:55 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: ConverterInfo.java,v $
 * Revision 1.18  2007/08/16 11:10:55  marbobo
 * + Bugfixes
 * + Refactorings
 *
 * Revision 1.17  2006/07/07 11:22:05  lawern
 * Postgres-Bugfix
 *
 */
package vkc.db.common;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import vkc.converter.common.Status;
import vkc.db.PersistenceManager;
import vkc.db.helper.DBHelper;
import vkc.db.serializer.VKCDocumentTypeSerializer;
import vkc.exception.DBException;
import vkc.misc.VKCLogManager;
import vkc.util.VKCStringUtils;

/**
 * This class represents a bean for the converter information.
 * 
 * @created 07.01.2004
 * @author Andreas Koop
 */
public class ConverterInfo extends ServerInfo {
    private int jobsInExecution = 0;

    private int maxReiterations = 1;

    private ConverterTiming timing = new ConverterTiming();

    private Integer timingId = new Integer(0);

    /**
     * Semicolon separated list of document types
     */
    private String documentTypes;

    /**
     * Construtcs a new ConverterInfo object corresponding to
     * <code>CONVERTER</code> table.
     */
    public ConverterInfo() {
        super("CONVERTER");
    }

    public String getDocumentTypes() {
        return documentTypes.toLowerCase();
    }

    public Collection getDocumentTypesAsList() {
        Collection dTypes = new ArrayList();
        Collection exts = VKCStringUtils.stringToVector(documentTypes, ";");

        for (Iterator iter = exts.iterator(); iter.hasNext();) {
            String ext = (String) iter.next();
            dTypes.add(VKCDocumentTypeSerializer.getDocumentTypeByExtension(ext.trim().toUpperCase()));
        }

        return dTypes;
    }

    public int getJobsInExecution() {
        return jobsInExecution;
    }

    public int getMaxReiterations() {
        return maxReiterations;
    }

    public void setDocumentTypes(String string) {
        documentTypes = string;
    }

    public void setMaxReiterations(int i) {
        maxReiterations = i;
    }

    /**
     * Increases the jobs count
     * 
     * @param by
     *            this amount, can be negativ
     */
    public void incJobsCount(int amount) {
        jobsInExecution += amount;
    }

    /**
     * Returns the corresponding timing object
     * 
     * @return
     * @throws DBException
     */
    public ConverterTiming getTiming() throws DBException {
        timing.associateWithDbUrl(getAssocDbUrl());
        timing.initialize(timingId);

        return timing;
    }

    /**
     * Makes the jobs counter of this server info persistent
     * 
     * @throws DBException
     */
    public void makeJobsCountPersistent() throws DBException {
        for (Iterator it = pm.getDbUrls().iterator(); it.hasNext();) {
            associateWithDbUrl((String) it.next());

            super.makePersistent(getTable(), "JOBS_IN_EXECUTION", new Integer(jobsInExecution), "ID", getId());
        }
    }

    /**
     * Makes the jobs counter of this server info persistent
     * 
     * @throws DBException
     */
    public void makeJobsCountPersistent(Connection conn) throws DBException {
        super.makePersistent(conn, getTable(), "JOBS_IN_EXECUTION", new Integer(jobsInExecution), "ID", getId());
    }

    /**
     * Registers the converter in the DB The ID and other specific information
     * is taken from the properties
     * 
     * @param props
     * @throws DBException
     */
    public static final void register(Properties props, String dbUrl) throws DBException {
        PersistenceManager pm = PersistenceManager.getInstance();
        Connection conn = pm.getConnection(dbUrl);
        PreparedStatement pStmt = null;

        try {
            int converterId = Integer.parseInt(props.getProperty("vkc.converter.id"));

            if (DBHelper.existsEntry(conn, "CONVERTER", "ID", new Integer(converterId))) {
                pStmt = conn.prepareStatement("UPDATE CONVERTER SET HOSTNAME = ?, PORT = ?, STATUS = ?, DOCUMENTTYPES = ?, LOADFACTOR = ?, JOBS_IN_EXECUTION = 0, MAX_REITERATIONS = ? WHERE ID = ?");
            } else {
                pStmt = conn.prepareStatement("INSERT INTO CONVERTER ( HOSTNAME, PORT, STATUS, DOCUMENTTYPES, LOADFACTOR, MAX_REITERATIONS, ID ) VALUES ( ?, ?, ?, ?, ?, ?, ? )");
            }

            pStmt.setString(1, props.getProperty("vkc.converter.hostname"));
            pStmt.setInt(2, Integer.parseInt(props.getProperty("vkc.converter.port")));
            pStmt.setString(3, Status.SERVER_DOWN);
            pStmt.setString(4, props.getProperty("vkc.converter.doctypes").toLowerCase());
            pStmt.setInt(5, Integer.parseInt(props.getProperty("vkc.converter.loadfactor", "1")));
            pStmt.setInt(6, Integer.parseInt(props.getProperty("vkc.converter.reiterations", "3")));
            pStmt.setInt(7, converterId);
            pStmt.execute();
            conn.commit();
        } catch (Exception e) {
            VKCLogManager.errorLog(ConverterInfo.class, "", e.getMessage(), e);
            DBHelper.rollBack(conn);
            throw new DBException(e);
        } finally {
            DBHelper.close(pStmt);
            DBHelper.close(conn);
        }
    }

    /**
     * (non-Javadoc)
     * 
     * @see vkc.db.common.PersistentObject#initialize(java.lang.Integer,
     *      java.sql.Connection)
     */
    public void initialize(Integer id, Connection conn) throws DBException {
        PreparedStatement pStmt = null;
        ResultSet rs = null;

        try {
            pStmt = conn.prepareStatement("SELECT * FROM CONVERTER WHERE ID = ?");
            pStmt.setInt(1, id.intValue());
            rs = pStmt.executeQuery();

            if (rs.next()) {
                setId(id);
                setHostname(rs.getString("hostname"));
                setPort(rs.getInt("port"));
                setDocumentTypes(rs.getString("documenttypes"));
                setStatus(rs.getString("STATUS"));
                setLoadFactor(rs.getInt("LOADFACTOR"));

                jobsInExecution = rs.getInt("JOBS_IN_EXECUTION");
                maxReiterations = rs.getInt("MAX_REITERATIONS");
                timingId = new Integer(rs.getInt("TIMINGID"));
            } else {
                throw new DBException("CONVERTERINFO for id = " + id + " not found!");
            }
        } catch (Exception e) {
            throw new DBException(e);
        } finally {
            DBHelper.close(rs);
            DBHelper.close(pStmt);
        }
    }

    /**
     * Returns all configurations of registered converter servers
     * 
     * @return list of all registered converter servers
     * @throws DBException
     */
    public static final List getAllRegisteredServers() throws DBException {
        PersistenceManager pm = PersistenceManager.getInstance();
        Connection conn = pm.getConnection();
        PreparedStatement pStmt = null;
        ResultSet rs = null;
        List serverConfigurations = new ArrayList();

        try {
            pStmt = conn.prepareStatement("SELECT ID FROM CONVERTER");
            rs = pStmt.executeQuery();

            while (rs.next()) {
                ConverterInfo c = new ConverterInfo();
                c.initialize(new Integer(rs.getInt("ID")), conn);
                c.checkAndUpdateStatus();
                serverConfigurations.add(c);
            }

            return serverConfigurations;
        } catch (Exception e) {
            VKCLogManager.errorLog(ConverterInfo.class, "", e.getMessage(), e);
            throw new DBException(e);
        } finally {
            DBHelper.close(rs);
            DBHelper.close(pStmt);
            DBHelper.close(conn);
        }
    }

    /**
     * Returns server convfiguration for the most under-worked Converter-Server
     * supporting the given docType
     * 
     * @param docTypeId
     * @return ConverterInfo
     * @throws DBException
     */
    public static final ConverterInfo getMostUnderWorkedServer(int docTypeId) throws DBException {
        PersistenceManager pm = PersistenceManager.getInstance();
        Connection conn = pm.getConnection();
        PreparedStatement pStmt = null;
        ResultSet rs = null;

        try {
            pStmt = conn.prepareStatement("SELECT EXTENSION FROM DOCUMENTTYPES WHERE DOCTYPEID = ?");
            pStmt.setInt(1, docTypeId);
            rs = pStmt.executeQuery();

            if (rs.next()) {
                return ConverterInfo.getMostUnderWorkedServer(rs.getString("EXTENSION"), conn);
            }

            return null;
        } catch (Exception e) {
            VKCLogManager.errorLog(ConverterInfo.class, "", e.getMessage(), e);
            throw new DBException(e);
        } finally {
            DBHelper.close(rs);
            DBHelper.close(pStmt);
            DBHelper.close(conn);
        }
    }

    /**
     * Returns server convfiguration for the most under-worked Converter-Server
     * supporting the given docType
     * 
     * @param docType
     * @param conn
     *            the connection to be used
     * @return ConverterInfo
     * @throws DBException
     */
    private static final ConverterInfo getMostUnderWorkedServer(String docType, Connection conn) throws DBException {
        PreparedStatement pStmt = null;
        ResultSet rs = null;
        ConverterInfo s = null;

        try {
            pStmt = conn.prepareStatement("SELECT * FROM CONVERTER WHERE STATUS = ? AND DOCUMENTTYPES LIKE ? ORDER BY JOBS_IN_EXECUTION ASC, LOADFACTOR DESC");
            pStmt.setString(1, Status.SERVER_UP);
            pStmt.setString(2, "%" + docType.toLowerCase() + "%");
            rs = pStmt.executeQuery();

            if (rs.next()) {
                s = new ConverterInfo();
                s.setId(new Integer(rs.getInt("ID")));
                s.setHostname(rs.getString("HOSTNAME"));
                s.setLoadFactor(rs.getInt("LOADFACTOR"));
                s.setPort(rs.getInt("PORT"));
                s.setStatus(rs.getString("STATUS"));
                s.setDocumentTypes(rs.getString("DOCUMENTTYPES"));
                s.jobsInExecution = rs.getInt("JOBS_IN_EXECUTION");
                s.setStatus(rs.getString("STATUS"));
            }

            return s;
        } catch (Exception e) {
            throw new DBException(e);
        } finally {
            DBHelper.close(rs);
            DBHelper.close(pStmt);
        }
    }

    /**
     * Returns the name of this server info
     * 
     * @return name of this server info
     */
    public String getName() {
        return "Converter Server";
    }

    /**
     * (non-Javadoc)
     * 
     * @see vkc.db.common.ServerInfo#getJobsCount()
     */
    public int getJobsCount() {
        return getJobsInExecution();
    }

    public void setJobsInExecution(int i) {
        jobsInExecution = i;
    }
}