package network;

import main.RepositoryAdministrator;
import main.ConfigurationReader;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import org.apache.log4j.Logger;

/** 
 * Purpose of this class is to provide services necessary for fan-in
 * implementation of Reed-Solomon code. FanInClient listens and accepts
 * data fragment, e.g. part of original file or one of its checksums.
 * In eighter case, fragment is stored in repository and is downloaded
 * or uploaded.
 *
 * Although this variant of client is more resource intensive than
 * BroadcastClient it is less prone to network outages. In case any of
 * FanIn clients comes down, other remain unaffected and it is sufficient
 * to find other suitable FanIn clients, e.g. repository hosts.
 *
 * Client uses utility wide configuration read by ConfigurationReader, which
 * must be initialized before this class is constructed.
 * 
 * @author Marcel Duris
 * @see main.ConfigurationReader
 */
public class FanInClient extends Thread {

    private Socket socket;
    private int port;
    private String fragmentName;

    /**
     * Constructs FanInClient listenning on specified port.
     * 
     * @param fragmentName filename of checksum or part of file to be received
     * @param socket to listen on
     */
    public FanInClient(String fragmentName, Socket socket) {
        Logger.getLogger("qbackup.repository").info("Client started for fragment " + fragmentName);
        this.socket = socket;
        this.fragmentName = fragmentName;
        start();
    }

    /**
     * Tries to set SO_TIMEOUT of socket waiting for connection.
     *
     * @param timeout timeout in miliseconds
     * @throws SocketException if error in underlying protocol
     */
    public void setTimeout(int timeout) throws SocketException {
        //ss.setSoTimeout(timeout);
    }

    /**
     * Returns port on which client listens.
     *
     * @return port number
     */
    public int getPort() {
        return port;
    }

    /**
     * Fires method responsible for listening for incoming traffic.
     */
    @Override
    public void run() {
        processFragment(socket);
    }

    /**
     * Listens for incoming file and stores it in repository.
     */
    private void processFragment(Socket socket) {

        try {
            InputStream in = socket.getInputStream();
            OutputStream out = RepositoryAdministrator.getOutputStream(fragmentName);

            writeToFile(in, out);
            socket.close();
        } catch (IOException ex) {
            //error accepting connection
            //error getting input stream
            //ex.printStackTrace();
            Logger.getLogger("qbackup.repository").warn("Error receiving fragment.");
        }
    }

    /**
     * Reads input stream and writes it to desired output file specified by
     * argument.
     * 
     * @param input stream to read
     * @param outputFile file to write to
     */
    private void writeToFile(InputStream input, OutputStream output) {
        InputStream in = new BufferedInputStream(input);
        OutputStream out = new BufferedOutputStream(output);

        int n = 0;
        try {
            while ((n = in.read()) > -1) {
                out.write(n);
            }
        } catch (IOException ex) {
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                Logger.getLogger("qbackup.repository").warn("Error receiving fragment. Error closing network input.");
            }
            try {
                out.close();
            } catch (IOException ex) {
                Logger.getLogger("qbackup.repository").warn("Error receiving fragment. Error closing file output.");
            }
        }
    }
}
