package vut.msa.transport;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.util.Date;
import javax.net.ssl.SSLSocket;
import org.apache.log4j.Logger;
import vut.msa.enums.BackupType;
import vut.msa.helpers.BackupHelper;
import vut.msa.helpers.ClientHelper;
import vut.msa.helpers.ClientInfoHelper;
import vut.msa.monitor.system.SystemInformation;
import vut.msa.pojos.Backup;
import vut.msa.pojos.Client;
import vut.msa.pojos.ClientInformation;
import vut.msa.utilities.FileUtilities;
import vut.msa.utilities.ZipUtilities;

public class TransportServerThread extends Thread {

    private static final Logger log = Logger.getLogger(TransportServerThread.class);
    
    private SSLSocket socket = null;
    private CommunicationProtocol kkp = new CommunicationProtocol();

    private FileStreamer streamer;
    private Client connectedClient;
    private ClientHelper helper;

    public TransportServerThread(SSLSocket socket) throws IOException {
        super("TransportServerThread");
        this.socket = socket;
        this.streamer = new FileStreamer(socket);

        socket.startHandshake();
        helper = new ClientHelper();
        connectedClient = new Client();
    }

    @Override
    public void run() {

        String action = null;

        try {

            log.info("Connection open with " + socket.getRemoteSocketAddress());
            action = runInStringExchangeMode();
            //ObjectInputStream objectIn = new ObjectInputStream(socket.getInputStream());
            
            int timeout = 100;
            while (true) {
                log.info("Action: " + action);
                //Klient ma zaslat udaje
                if (action.equals("SEND_DATA")) {
                    action = readFileFromClient();
                }

                if (action.equals("READ_DATA")) {
                    log.info("Sending file to client");
                    action = writeFileToClient();
                }

                if (action.equals("SEND_INFO")){           
                    try {
                        ObjectInputStream objectIn = new ObjectInputStream(socket.getInputStream());
                        Object receivedObject = objectIn.readObject();
                        if (receivedObject instanceof SystemInformation) {

                            connectedClient = kkp.getCommClient();
                            SystemInformation info = (SystemInformation) receivedObject;
                            ClientInformation clientInfo = new ClientInformation(info, connectedClient);
                            ClientInfoHelper infoHelper = new ClientInfoHelper();

                            log.info("Client " + clientInfo.getClient().getClientName()
                                    + " uptime: " + info.getFormatedUptime((int)clientInfo.getUptime()));

                            //TODO: docasne, treba namapovat klienta
                            clientInfo.setClient(connectedClient);
                            clientInfo.setId(connectedClient.getId());
                            
                            if (clientInfo != null) {
                                infoHelper.updateInformation(clientInfo);
                            } else {
                                infoHelper.enterNewInformation(clientInfo);
                            }

                        }
                        
                        break;
                    } catch (Exception ex) {
                        log.error(ex.getMessage());
                    }
                }

                if (action.equals("OK")) {
                    action = runInStringExchangeMode();
                }

                if (action == null | action.equals("CLOSE")) {
                    try {
                        socket.getInputStream().close();
                        socket.getOutputStream().close();
                    } catch (Exception ex) {}

                    break;
                }

                if (timeout-- < 0) {
                    log.warn("No communication with server. Closing socket.");
                    break;
                }
            }

            socket.close();

            log.info("Connection closed.");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public String runInStringExchangeMode() throws Exception {

        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(
                new InputStreamReader(
                socket.getInputStream()));

        String inputLine, outputLine;
        
        outputLine = kkp.processInput(null);
        out.println(outputLine);

        while ((inputLine = in.readLine()) != null) {
            outputLine = kkp.processInput(inputLine);

            if (outputLine.equals("SEND_DATA")) {
                //Nacitaj data
                out.println(outputLine);
                break;
            }

            if (outputLine.equals("READ_DATA")) {
                //Nacitaj data
                out.println(outputLine);
                break;
            }

            if (outputLine.equals("SEND_INFO")) {
                //Nacitaj data
                out.println(outputLine);
                break;
            }


            if (outputLine.equals("TAKE_ACTION")) {
                //Poziadaj klienta o zvolenie akcie
                out.println(outputLine);
            }

            if (outputLine.equals("UNAUTHORIZED")) {
                //Poziadaj klienta o zvolenie akcie
                out.println(outputLine);
                continue;
            }

            if (outputLine.equals("CLOSE")) {
                break;
            }

            //out.println(outputLine);
        }

        return outputLine;
    }

    private String readFileFromClient() throws IOException {

        log.info("Pripravujem sa na prenos suboru y klienta na server.");

        connectedClient = kkp.getCommClient();
        String backupClientPath = connectedClient.getBackupDisk().getDiskName()
                                    + "backup\\"
                                    + connectedClient.getClientId();
        File clientDir = new File(backupClientPath);
        if (!clientDir.exists()) {
            clientDir.mkdir();
        }
        log.info("Nahravam subor do adresara: " + backupClientPath);
        File received = new File(clientDir.getAbsolutePath()
                                    + System.getProperty("file.separator")
                                    + FileUtilities.getFileDate("ddMMyyyy_HHmm")
                                    + ".zip");
        streamer.readFile(received);
        log.info("Subor nahrany na server, vraciam sa do retazcoveho modu,");

        BackupHelper bkHelper = new BackupHelper();
        Backup backup =  new Backup();
        backup.setClient(connectedClient);
        backup.setSize(received.length()/(1024*1024));
        backup.setSuccess(1);
        backup.setUploadDate(new Date());
        backup.setPath(received.getAbsolutePath());
        backup.setType(BackupType.INCREMENTAL.name());
        backup.setDocumentsCount(ZipUtilities.countFilesInZip(received.getAbsolutePath()));
        
        bkHelper.addBackupDisk(backup);
        if (connectedClient != null) {
            log.info("Aktualizujem klienta s novym datumom. " + connectedClient.getClientId());
            helper.uploadSucces(connectedClient);
        }

        Client test = helper.getClientByName(connectedClient.getClientName());
        log.info("Klient ma zaloh: " + test.getBackups().size());
        return "CLOSE";
    }

    private String writeFileToClient() throws IOException {

        BufferedReader in = new BufferedReader(
                new InputStreamReader(
                socket.getInputStream()));

        log.info("Prepared for writing file from client");
        String requestedFile = in.readLine();
        log.info("Client request file:"  + requestedFile);
        if (requestedFile != null) {
            streamer.sendFile(new File("C:\\" + requestedFile));
        }
        
        log.info("File transfered, returning to StringExchangeMode");
        
        return "CLOSE";
    }
}
