package tech.nevets.dlite;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.swing.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
Commands:
 - 0x1: User connect
 - 0x2: User disconnect
 - 0x3: Message
 - 0x4: Online List

 - 0xf: End of PDU

 Usable: 0-f
 */

public class Connection {
    private Socket s;
    private InputStream is;
    private OutputStream os;
    private PrintStream out;
    private String username;

    private JTextArea chat;
    private List<String> onlineList = new ArrayList<>();


    public Connection(String username, String address) throws Exception {
        String[] addrSplit = address.split(":");
        if (addrSplit.length != 2) {
            throw new MalformedURLException("Address not formatted 'ip:port'");
        }

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream tStore = new FileInputStream("./truststore.p12");
        trustStore.load(tStore, "".toCharArray());
        tStore.close();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);

        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(null, tmf.getTrustManagers(), SecureRandom.getInstanceStrong());

        SocketFactory factory = ctx.getSocketFactory();
        s = factory.createSocket(addrSplit[0], Integer.parseInt(addrSplit[1]));

        is = s.getInputStream();
        os = s.getOutputStream();
        out = new PrintStream(os);
        this.username = username;

        os.write(0x1);
        out.println(username);
        os.write(0xf);
    }

    public void sendMessage(String message) {
        try {
            os.write(0x3);
            out.println(message);
            os.write(0xf);
            os.flush();
        } catch (IOException e) {
            Notifier.errorPopup("Error Sending Message", e.getMessage());
        }
    }

    public void setComponents(JTextArea chat, JTextArea online) {
        this.chat = chat;
        new Thread(() -> {
            byte[] buf = new byte[1024];
            ChatWriter chatWriter = new ChatWriter();

            try {
                while (!s.isClosed()) {
                    byte cmd;
                    try {
                        cmd = (byte) is.read();
                    } catch (IOException e) {
                        ChatWindow.getInstance().resetConnection(false);
                        break;
                    }
                    int i = 0;
                    for (byte b = (byte) is.read(); b != 0xf && i < 1023; b = (byte) is.read()) {
                        buf[i] = b;
                        i++;
                    }

                    switch (cmd) {
                        case 0x1 -> { // User Connect
                            addOnlineUser(new String(Arrays.copyOfRange(buf, 0, i )));
                            online.setText(getFormattedOnline());
                        }
                        case 0x2 -> { // User Disconnect
                            removeOnlineUser(new String(Arrays.copyOfRange(buf, 0, i)));
                            online.setText(getFormattedOnline());
                        }
                        case 0x3 -> {
                            byte[] msg = Arrays.copyOfRange(buf, 0, i);
                            chatWriter.write(msg); // Send Message
                        }
                        case 0x4 -> {
                            onlineList.clear();
                            String bufBuf = "";
                            for (int j = 0; j <= i; j++) {
                                if ((char) buf[j] == '\n') {
                                    onlineList.add(bufBuf);
                                    bufBuf = "";
                                }
                                bufBuf += (char) buf[j];
                            }
                            online.setText(getFormattedOnline());
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                Notifier.errorPopup("Error While Closing Connection Thread", e.getMessage());
            }
        }).start();
    }

    public void addOnlineUser(String username) {
        onlineList.add(username);
    }

    public void removeOnlineUser(String username) {
        onlineList.remove(username);
    }

    public String getFormattedOnline() {
        StringBuilder sb = new StringBuilder();
        for (String s : onlineList) {
            sb.append(s.trim().replace("\r", "").replace("\n", ""));
            sb.append("\n");
        }
        return sb.substring(0, sb.length());
    }

    public void close() {
        if (!s.isClosed()) {
            try {
                os.write(0x2);
                out.println(username);
                os.write(0xf);
                s.close();
            } catch (IOException e) {
                Notifier.errorPopup("Connection Reset By Server", e.getMessage());
                ChatWindow.getInstance().resetConnection(true);
            }
        }
        ChatWindow.getInstance().resetConnection(false);
    }

    private class ChatWriter extends OutputStream {
        String buf = "";

        @Override
        public void write(int b) {
            buf += (char) b;
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            for (int i = 0 ; i < len ; i++) {
                write(b[off + i]);
            }
            flush();
        }

        @Override
        public void flush() {
            chat.append(buf);
            Notifier.messageReceived(Main.getFrame());
            //Notifier.sendNotification(buf.substring(0, buf.indexOf(">") - 1), buf.substring(buf.indexOf(">")));
            buf = "";
        }
    }
}
