/*
 * $Id: ConverterProxy.java,v 1.13 2007/08/16 11:10:59 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: ConverterProxy.java,v $
 * Revision 1.13  2007/08/16 11:10:59  marbobo
 * + Bugfixes
 * + Refactorings
 *
 * Revision 1.12  2005/04/01 14:31:20  lawern
 * openSocket mit Timeout versehen
 *
 */
package vkc.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Iterator;

import vkc.converter.common.ConversionRequest;
import vkc.converter.common.ConversionResponse;
import vkc.converter.common.Status;
import vkc.converter.common.StreamConversionRequest;
import vkc.converter.common.UrlConversionRequest;
import vkc.db.common.ConverterInfo;
import vkc.db.entities.VKCAdditionalDocumentFormat;
import vkc.db.entities.VKCDocument;
import vkc.db.serializer.VKCDocumentSerializer;
import vkc.exception.ConverterException;
import vkc.exception.DBException;
import vkc.misc.SocketSetter;
import vkc.misc.VKCLogManager;
import vkc.server.archive.DataStoreException;
import vkc.util.FileUtils;
import vkc.util.SocketUtils;
import vkc.util.VKCInputStream;
import vkc.view.WebShare;

/**
 * This class is used for sending converter request to a converter server.
 * 
 * @created 09.01.2004
 * @author Andreas Koop
 */
public class ConverterProxy {
    private SocketSetter socketSetter = null;

    /**
     * Sends the a conversion request to the converter server for the given
     * document (and waits for the response)
     * 
     * @param document
     *            (synchroneously)
     */
    public ConversionResponse convert(VKCDocument document) throws ConverterException {
        ConversionRequest request = null;

        try {
            request = ConverterProxy.createConversionRequest(document);
        } catch (DataStoreException e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);

            return null;
        }

        return this.convert(request);
    }

    /**
     * Sends the a conversion request to the converter server for the given
     * document (and waits for the response)
     * 
     * (synchroneously)
     */
    public ConversionResponse convert(ConversionRequest request) throws ConverterException {
        if (request == null) {
            throw new ConverterException("No converter reqeust specified!");
        }
        
        Socket socket = null;
        
        try {
            ConverterInfo ci = null;
            
            while (true) {
                ci = ConverterInfo.getMostUnderWorkedServer(request.getDocTypeId());
                
                if (ci == null) {
                    VKCLogManager.infoLog(this, "Conversion request can not be send.\n" + "There is no Converter-Server running at all!");
                    throw new ConverterException("No converter server is running for docTypeId = " + request.getDocTypeId(), "vkc.converter.error.no_server_running");
                }

                VKCLogManager.debug(this, "Connecting to " + ci.getHostname() + ":" + ci.getPort());
                
                try {
                    socket = ci.openSocket(0);
                    break;
                } catch (UnknownHostException e1) {
                    VKCLogManager.errorLog(this, "", "Unknown host " + ci.getHostname(), e1);

                } catch (IOException e1) {
                    VKCLogManager.errorLog(this, "", "I/O Error while opening socket " + ci.getHostname() + ":" + ci.getPort(), e1);
                }

                ci.setStatus(Status.SERVER_DOWN);
                ci.setJobsInExecution(0);
                ci.makeStatusPersistent();
                ci.makeJobsCountPersistent();
            }

            if (socketSetter != null) {
                socketSetter.setProperties(socket);
            }
            
            ObjectOutputStream out = SocketUtils.getObjectOutputStream(socket);
            out.flush();
            ObjectInputStream in = SocketUtils.getObjectInputStream(socket);

            // Send the converter request
            out.writeObject(request);

            if (request instanceof StreamConversionRequest) {
                // Send the inputstream to be converted
                VKCInputStream is = ((StreamConversionRequest) request).getInputStream();
                FileUtils.write(is, out);
                is.close();
            }

            out.flush();
            socket.shutdownOutput();

            VKCLogManager.debug(this, "Request sent: " + request);

            // Awaiting response
            Object readObject = null;
            
            while (!((readObject = in.readObject()) instanceof ConversionResponse)) {
                // Leer
            }

            ConversionResponse response = (ConversionResponse) readObject;

            // Konvertierte Streams auslesen und im VKCArchiv ablegen
            Collection addDocFormats = response.getAdditionalDocFormats();
            
            if (addDocFormats != null) {
                for (Iterator it = addDocFormats.iterator(); it.hasNext();) {
                    VKCAdditionalDocumentFormat f = (VKCAdditionalDocumentFormat) it.next();
                    String archiveId = WebShare.archive.store(new VKCInputStream(in, f.getDocSize().longValue()));
                    f.setArchiveId(archiveId);
                }
            }

            return response;
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);

            if (e instanceof ConverterException) {
                throw (ConverterException) e;
            }
            
            throw new ConverterException(e.getMessage(), "vkc.converter.error.general");
        } finally {
            if (socket != null) {
                SocketUtils.close(socket);
            }
        }
    }

    /**
     * Creates a conversion request for a VKCdocument
     * 
     * @param document
     * @return the corresponding conversion request object
     */
    public static ConversionRequest createConversionRequest(VKCDocument document) throws DataStoreException {
        if (!document.getDocumentType().getExtension().equals("LINK") && !document.getDocumentType().getExtension().equals("NOTE")) {
            StreamConversionRequest req = new StreamConversionRequest((VKCInputStream) WebShare.archive.getInputStream(document.getArchiveId().toString()), document.getDocTypeId().intValue());

            req.setZipped((document.getZipped() == null) ? false : document.getZipped().intValue() == 1);

            if (req.isZipped()) {
                req.setRootFilename(document.getDisplayName());
            }
            
            return req;
        } else if (document.getDocumentType().getExtension().equals("LINK")) {
            return new UrlConversionRequest(document.getName());
        }

        return null;
    }

    /**
     * Creates a conversion request for a document Id
     * 
     * @param documentId
     * @return the corresponding conversion request object
     */
    public static ConversionRequest createConversionRequest(Integer documentId) throws DataStoreException, DBException {
        return ConverterProxy.createConversionRequest(VKCDocumentSerializer.retrieveDocument(documentId));
    }

    public void setSocketSetter(SocketSetter setter) {
        socketSetter = setter;
    }
}