package com.knutejohnson.pi.ipconfig;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;
import javax.swing.*;

/**
 * IPConfig is a Java GUI program to set the IP address details on a
 * RaspberryPi running Buster.  This program requires Java 11 and  must be run
 * by the root user (eg. sudo java -jar IPConfig.jar).
 *
 * @version 0.11.0 - 13 September 2019
 * @author  Knute Johnson
 */
public class IPConfig extends JFrame {
    /** Serial version UID */
    private static final long serialVersionUID = 1L;
    /** Program version */
    public static final String VERSION = "0.11.0";
    /** Program date */
    public static final String DATE = "13 September 2019";
    /** DHCPCD configuration file name */
    private static final String CONF_FILE_NAME = "/etc/dhcpcd.conf";
    /** eth0 dhcp JCheckBox */
    private final JCheckBox eth0dhcpBox;
    /** eth0 static JCheckBox */
    private final JCheckBox eth0staticBox;
    /** wlan0 dhcp JCheckBox */
    private final JCheckBox wlan0dhcpBox;
    /** wlan0 static JCheckBox */
    private final JCheckBox wlan0staticBox;
    /** eth0 ip JTextField */
    private final JTextField eth0ipField;
    /** eth0 routers JTextField */
    private final JTextField eth0routersField;
    /** eth0 dns JTextField */
    private final JTextField eth0dnsField;
    /** wlan0 ip JTextField */
    private final JTextField wlan0ipField;
    /** wlan0 routers JTextField */
    private final JTextField wlan0routersField;
    /** wlan0 dns JTextField */
    private final JTextField wlan0dnsField;
    /** List to hold lines of data from dhcpcd.conf file */
    private volatile java.util.List<String>lines = null;
    /**
     * List to hold lines of data from dhcpcd.conf file that contain
     * interface information.
     */
    private volatile java.util.List<String>interfaceLines = null;
    /** Map to hold eth0 interface data */
    private final Map<String,String> eth0Map = new LinkedHashMap<>();
    /** Map to hold wlan0 interface data */
    private final Map<String,String> wlan0Map = new LinkedHashMap<>();

    /**
     * Creates a new IPConfig JFrame, reads the dhcpcd.conf file and populates
     * the data fields with that data.
     */
    public IPConfig() {
        super(String.format("IPConfig - Version %s - Date %s",VERSION,DATE));

        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        JMenu help = menuBar.add(new JMenu("Help"));

        JMenuItem mi;
        try (InputStream is = getClass().getResourceAsStream("ipconfig.html")) {
            if (is != null) {
                try (BufferedReader reader = new BufferedReader(
                 new InputStreamReader(is))) {
                    String html = reader.lines().collect(joining("\n"));
                    mi = help.add("Directions");
                    mi.addActionListener(event -> {
                        JOptionPane.showMessageDialog(IPConfig.this,
                         new JLabel(html,JLabel.CENTER),"IPConfig - Directions",
                         JOptionPane.INFORMATION_MESSAGE);
                    });
                    help.addSeparator();
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        mi = help.add("About");
        mi.addActionListener(event -> {
            JOptionPane.showMessageDialog(IPConfig.this,"IPConfig\n".
             concat("Version ").concat(VERSION).concat("\n").concat("Date ").
             concat(DATE),"IPConfig - About",JOptionPane.INFORMATION_MESSAGE);
        });

        setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(2,2,2,2);

        c.gridy = 0;  c.gridwidth = 2;
        JLabel label = new JLabel("eth0",JLabel.CENTER);
        label.setFont(label.getFont().deriveFont(20f));
        add(label,c);

        label = new JLabel("wlan0",JLabel.CENTER);
        label.setFont(label.getFont().deriveFont(20f));
        add(label,c);

        ButtonGroup eth0Group = new ButtonGroup();
        ButtonGroup wlan0Group = new ButtonGroup();

        ++c.gridy;  c.gridwidth = 1;
        eth0dhcpBox = new JCheckBox("dhcp");
        eth0Group.add(eth0dhcpBox);
        add(eth0dhcpBox,c);

        eth0staticBox = new JCheckBox("static");
        eth0Group.add(eth0staticBox);
        add(eth0staticBox,c);

        wlan0dhcpBox = new JCheckBox("dhcp");
        wlan0Group.add(wlan0dhcpBox);

        add(wlan0dhcpBox,c);

        wlan0staticBox = new JCheckBox("static");
        wlan0Group.add(wlan0staticBox);
        add(wlan0staticBox,c);

        ++c.gridy;  c.anchor = GridBagConstraints.WEST;
        add(new JLabel("IP Address",JLabel.LEFT),c);

        eth0ipField = new JTextField(12);
        eth0ipField.setToolTipText("IP Address must end with /24");
        add(eth0ipField,c);

        add(new JLabel("IP Address",JLabel.LEFT),c);

        wlan0ipField = new JTextField(12);
        wlan0ipField.setToolTipText("IP Address must end with /24");
        add(wlan0ipField,c);

        ++c.gridy;
        add(new JLabel("Router/Gateway",JLabel.LEFT),c);

        eth0routersField = new JTextField(12);
        add(eth0routersField,c);

        add(new JLabel("Router/Gateway",JLabel.LEFT),c);

        wlan0routersField = new JTextField(12);
        add(wlan0routersField,c);

        ++c.gridy;
        add(new JLabel("DNS Servers",JLabel.LEFT),c);

        eth0dnsField = new JTextField(12);
        add(eth0dnsField,c);

        add(new JLabel("DNS Servers",JLabel.LEFT),c);

        wlan0dnsField = new JTextField(12);
        add(wlan0dnsField,c);

        ++c.gridy;  c.gridwidth = 4;  c.anchor = GridBagConstraints.CENTER;
        JButton saveButton = new JButton("Save");
        getRootPane().setDefaultButton(saveButton);
        add(saveButton,c);
        saveButton.addActionListener(event -> {
            saveButton.setEnabled(false);
            try {
                if (eth0staticBox.isSelected()) {
                    if (!eth0ipField.getText().contains("/"))
                        throw new UnknownHostException("IP missing /");
                    String[] ipStrs = eth0ipField.getText().split("/");
                    if (Integer.parseInt(ipStrs[1]) != 24)
                        throw new UnknownHostException("IP must end with /24");
                    InetAddress.getByName(ipStrs[0]);
                    InetAddress.getByName(eth0routersField.getText());
                    String[] addrs = eth0dnsField.getText().split(" ");
                    for (String addr : addrs)
                        InetAddress.getByName(addr);
                }
                if (wlan0staticBox.isSelected()) {
                    if (!wlan0ipField.getText().contains("/"))
                        throw new UnknownHostException("IP missing /");
                    String[] ipStrs = wlan0ipField.getText().split("/");
                    InetAddress.getByName(ipStrs[0]);
                    if (Integer.parseInt(ipStrs[1]) != 24)
                        throw new UnknownHostException("IP must end with /24");
                    InetAddress.getByName(wlan0routersField.getText());
                    String[] addrs = wlan0dnsField.getText().split(" ");
                    for (String addr : addrs)
                        InetAddress.getByName(addr);
                }
            } catch (UnknownHostException|NumberFormatException ex) {
                JOptionPane.showMessageDialog(IPConfig.this,ex,
                 "Invalid IP Format",JOptionPane.ERROR_MESSAGE);
                saveButton.setEnabled(true);
                return;
            }
            // write the eth0 and wlan0 map data to /etc/dhcpcd.conf
            JOptionPane pane = new JOptionPane(new JLabel(
             "Saving ".concat(CONF_FILE_NAME).concat(" file."),
             JLabel.CENTER),JOptionPane.INFORMATION_MESSAGE);
            JDialog dialog = pane.createDialog(IPConfig.this,
             "IPConfig - Saving File");
            dialog.setModal(false);
            dialog.setVisible(true);
            new Thread(() -> {
                try (BufferedWriter writer = Files.newBufferedWriter(
                 Path.of(CONF_FILE_NAME),
                 StandardOpenOption.CREATE,
                 StandardOpenOption.WRITE,
                 StandardOpenOption.TRUNCATE_EXISTING)) {
                    if (lines != null) {
                        for (String line : lines) {
                            if (!line.startsWith("interface")) {
                                writer.write(line);
                                writer.newLine();
                            } else {
                                break;
                            }
                        }
                        if (eth0staticBox.isSelected()) {
                            writer.write("interface eth0\n");
                            writer.write("static ip_address=" +
                             eth0ipField.getText());
                            writer.newLine();
                            writer.write("static routers=" +
                             eth0routersField.getText());
                            writer.newLine();
                            writer.write("static domain_name_servers=" +
                             eth0dnsField.getText());
                            writer.newLine();
                        }

                        if (wlan0staticBox.isSelected()) {
                            writer.write("interface wlan0\n");
                            writer.write("static ip_address=" +
                             wlan0ipField.getText());
                            writer.newLine();
                            writer.write("static routers=" +
                             wlan0routersField.getText());
                            writer.newLine();
                            writer.write("static domain_name_servers=" +
                             wlan0dnsField.getText());
                            writer.newLine();
                        }
                    }
                } catch (IOException ioe) {
                    JOptionPane.showMessageDialog(IPConfig.this,ioe,
                     "Error Writing to ".concat(CONF_FILE_NAME),
                     JOptionPane.ERROR_MESSAGE);
                    ioe.printStackTrace();
                } finally {
                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException ie) { }
                    EventQueue.invokeLater(() -> {
                        if (dialog != null) {
                            dialog.setVisible(false);
                            dialog.dispose();
                        }
                    });
                }
                saveButton.setEnabled(true);
            }).start();
        });

        // action listeners
        eth0dhcpBox.addActionListener(event -> {
            eth0ipField.setEnabled(false);
            eth0routersField.setEnabled(false);
            eth0dnsField.setEnabled(false);
        });
        eth0staticBox.addActionListener(event -> {
            eth0ipField.setEnabled(true);
            eth0routersField.setEnabled(true);
            eth0dnsField.setEnabled(true);
            eth0ipField.requestFocusInWindow();
        });
        wlan0dhcpBox.addActionListener(event -> {
            wlan0ipField.setEnabled(false);
            wlan0routersField.setEnabled(false);
            wlan0dnsField.setEnabled(false);
        });
        wlan0staticBox.addActionListener(event -> {
            wlan0ipField.setEnabled(true);
            wlan0routersField.setEnabled(true);
            wlan0dnsField.setEnabled(true);
            wlan0ipField.requestFocusInWindow();
        });

        // read the /etc/dhcpcd.conf file and fill the fields
        try {
            lines = Files.readAllLines(Path.of(CONF_FILE_NAME));

            interfaceLines = lines.stream().
             filter(s1 -> !s1.startsWith("#")).
             filter(s2 -> !s2.equals("")).
             filter(s3 -> s3.startsWith("interface") || s3.contains("=")).
             collect(toList());

            Map<String,String> map = null;
            for (String line : interfaceLines) {
                if (line.matches("interface\\s+eth0")) {
                    map = eth0Map;
                    continue;
                }
                if (line.matches("interface\\s+wlan0")) {
                    map = wlan0Map;
                    continue;
                }
                if (map == null)
                    continue;
                String[] arr = line.split("=");
                map.put(arr[0],arr[1]);
            }

            if (eth0Map.size() > 0) {
                eth0staticBox.doClick();
                eth0ipField.setText(eth0Map.get("static ip_address"));
                eth0routersField.setText(eth0Map.get("static routers"));
                eth0dnsField.setText(eth0Map.get("static domain_name_servers"));
            } else {
                eth0dhcpBox.doClick();
            }
            if (wlan0Map.size() > 0) {
                wlan0staticBox.doClick();
                wlan0ipField.setText(wlan0Map.get("static ip_address"));
                wlan0routersField.setText(wlan0Map.get("static routers"));
                wlan0dnsField.setText(
                 wlan0Map.get("static domain_name_servers"));
            } else {
                wlan0dhcpBox.doClick();
            }
        } catch (IOException ioe) {
            JOptionPane.showMessageDialog(IPConfig.this,"Unable to Open ".
             concat(CONF_FILE_NAME).concat(" file.\n").concat(ioe.toString()),
             "IPConfig - FATAL ERROR!",JOptionPane.ERROR_MESSAGE);
                System.exit(-1);
        }

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    /**
     * Program entry point, checks to make sure program is being run by root
     * and creates the GUI on the event dispatch thread.
     *
     * @param   args    Command line arguments (not used)
     */
    public static void main(String... args) {
        if (!System.getProperty("user.name").equals("root"))
            JOptionPane.showMessageDialog(null,
             "Must be run as root!\nsudo java -jar IPConfig.jar",
             "IPConfig",JOptionPane.ERROR_MESSAGE);
        else
            EventQueue.invokeLater(() -> new IPConfig());
    }
}