package network;

import main.RepositoryAdministrator;
import main.ConfigurationReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import org.apache.log4j.Logger;

/**
 * Objects of this class listen on TCP socket for requests for reliable
 * transmissions which can be either download or upload of file fragment or
 * checksum.
 * 
 * @author Marcel Duris
 */
public class ReliableNetworkListener extends Thread {

    private static final int AUNKNOWN = 0;
    private static final int AUPLOAD = 1;
    private static final int ADOWNLOAD = 2;
    public static final int FANIN = 0;
    public static final int BROADCAST = 1;
    private static ReliableNetworkListener factory = null;
    private ServerSocket ss = null;
    private static final int DEF_MAXPENDING = 100;

    private ReliableNetworkListener() throws IOException {
        ConfigurationReader r = ConfigurationReader.getCfg();
        ss = new ServerSocket(r.getPort());
        ss.setSoTimeout(0);

    }

    /**
     * Builds thread listening on local TCP port for incoming files. Allows
     * existence of only single class of this type.
     * 
     * @return Instance of client factory.
     * @throws IOException if no TCP socket could be started
     */
    public static ReliableNetworkListener getFactory() throws IOException {
        if (factory == null) {
            if (factory == null) {
                factory = new ReliableNetworkListener();
                factory.start();
            }
        }

        return factory;
    }

    public void close() throws IOException {
        if (ss != null) {
            ss.close();
            factory = null;
        }
    }

    /**
     * Listens for incoming TCP connections and starts appropriate instance of
     * client, which will download or upload files. Only files that have been
     * already requested to be backed up are allowed to be downloaded to
     * localhost.
     *
     */
    @Override
    public void run() {
        Logger.getLogger("qbackup").info("Reliable transfer unicast server started.");
        try {
            while (true) {
                Socket socket = ss.accept();

                String command = readLine(socket.getInputStream());////pending.get(socket.getInetAddress()).remove(0);//not ok
                String name;
                Logger.getLogger("qbackup").info("Connection accepted. Command: " + command);
                //TODO, pories viac fragmentov na jednom klientovi
                switch (action(command)) {
                    case AUPLOAD:
                        name = getBcpName(command);
                        if (RepositoryAdministrator.isRegistered(name)) {
                            new FanInClient(getFilename(command), socket);
                        } else {
                            Logger.getLogger("qbackup").warn("Unregistered backup.");
                        }
                        break;
                    case ADOWNLOAD:
                        //name = getFilename(command);
                        new UploadServer(getFilename(command), socket);
                }
            }
        } catch (IOException ex) {
            //Logger.getLogger(ReliableNetworkListener.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                ss.close();
            } catch (IOException ex) {
                //Logger.getLogger(ReliableNetworkListener.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Reads first line of input stream for purpouses of file transfer
     * management. Only messages shorter than 2000 characters are allowed.
     *
     * @param in input stream of network connection
     * @return first line of transmission
     * @throws IOException if error during reading of first line
     */
    private String readLine(InputStream in) throws IOException {
        byte[] buff = new byte[1000];
        int count = 0;
        int r;
        do {
            r = in.read();
            buff[count++] = (byte) r;
            if (count > 2000) {
                throw new IOException("Message to long.");
            }
        } while (r >= 0 && r != 10 && r != 13);
        if (r == 10 || r == 13) {
            count--;
        }

        return new String(buff, 0, count);
    }

    /**
     * Parses command line received from network host for action requestet by
     * that host.
     * 
     * @param line String received on first line of transmission.
     * @return integer representation of action
     */
    private int action(String line) {
        if (line.startsWith("upload: ")) {
            return AUPLOAD;
        }
        if (line.startsWith("download: ")) {
            return ADOWNLOAD;
        } else {
            return AUNKNOWN;
        }
    }

    /**
     * Converts filename of fragment to name of backup.
     *
     * @param name file name of fragment requested
     * @return name of backup to which file belongs
     */
    private String getBcpName(String name) {
        int pos = name.indexOf(" ") + 1;
        if (name.endsWith(".xml")) {
            return name.substring(pos, name.length() - 4);
        }
        return name.substring(pos, name.length() - 6);
    }

    /**
     * Parses command string to read filename requested to be downloaded or
     * uploaded. We don't have any library for parsing of backup names, so we
     * have to solve problem this way.
     *
     * @param command first line of transmission
     * @return String containing filename
     */
    private String getFilename(String command) {
        int pos = command.indexOf(" ") + 1;
        return command.substring(pos).trim();
    }
}

