import java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
import java.util.prefs.Preferences;

import javax.imageio.ImageIO;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class duckdns {

static String DuckDNSVersion = "DuckDns Updater (vs1.0.5)";
String TOOL_TIP = DuckDNSVersion;
static String StatusMsg = "";
String MESSAGE_HEADER = DuckDNSVersion;
TrayIcon trayIcon;
String ipaddress = "";
static int timercount = 0;
static TrayIcon processTrayIcon = null;
String OldIp = "";

public static void main(String[] args) {

try {
duckdns systemTrayExample = new duckdns();
systemTrayExample.createAndAddApplicationToSystemTray();
systemTrayExample.startProcess();

} catch (IOException e) {
e.printStackTrace();
}
}

private void createAndAddApplicationToSystemTray() throws IOException {
// Check the SystemTray support
if (!SystemTray.isSupported()) {
return;
}

final PopupMenu popup = new PopupMenu();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("logo.png");
Image img = ImageIO.read(inputStream);

trayIcon = new TrayIcon(img, TOOL_TIP);
duckdns.processTrayIcon = trayIcon;
final SystemTray tray = SystemTray.getSystemTray();

MenuItem aboutItem = new MenuItem("About");
MenuItem settingsItem = new MenuItem("DuckDNS Settings");
MenuItem forceupdateItem = new MenuItem("Force Update");
MenuItem whatsmyipaddress = new MenuItem("What is my IP Address?");
MenuItem exitItem = new MenuItem("Exit");

popup.add(settingsItem);
popup.add(forceupdateItem);
popup.add(whatsmyipaddress);
popup.addSeparator();
popup.add(aboutItem);
popup.add(exitItem);

trayIcon.setPopupMenu(popup);
trayIcon.setImageAutoSize(true);

try {
tray.add(trayIcon);
} catch (AWTException e) {
return;
}

trayIcon.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {
 JOptionPane.showMessageDialog(null, "Right click to choose from one of the menu options!");
 }
});

settingsItem.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {

 Preferences prefs = Preferences.userNodeForPackage(duckdns.class);

 JPanel panel = new JPanel();
 panel.setLayout(new GridLayout(5, 1));
 JLabel domain = new JLabel("Domain");
 JLabel token = new JLabel("Token");
 JLabel sett = new JLabel("Find your settings: ");
 JLabel minute = new JLabel("Refresh Interval (minutes)");
 JLabel update = new JLabel("Show Update Notifications");
 JTextField domainField = new JTextField(30);
 JTextField tokenField = new JTextField(30);

 String[] minuteStrings = { "5", "10", "15", "30", "60" };
 @SuppressWarnings({ "unchecked", "rawtypes" }) JComboBox minuteField = new JComboBox(minuteStrings);

 String[] UpdateMessages = { "YES", "NO" };
 @SuppressWarnings({ "unchecked", "rawtypes" }) JComboBox UpdateField = new JComboBox(UpdateMessages);

 domainField.setText(prefs.get("domain", ""));
 tokenField.setText(prefs.get("token", ""));
 minuteField.setSelectedItem(prefs.get("refresh", "5"));
 UpdateField.setSelectedItem(prefs.get("updatemessages", "YES"));

 // html content
 JEditorPane ep2 = new JEditorPane("text/html", "<html><a href=\"https://www.duckdns.org\">https://duckdns.org</a></html>");
 JLabel label = new JLabel();
 Font font = label.getFont();
 StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
 style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
 style.append("font-size:" + font.getSize() + "pt;");

 // handle link events
 ep2.addHyperlinkListener(new HyperlinkListener() {
  public void hyperlinkUpdate(HyperlinkEvent e) {
  if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
  launchUrl(e.getURL().toString());
  }
  }
 });
 ep2.setEditable(false);
 ep2.setBackground(label.getBackground());

 panel.add(domain);
 panel.add(domainField);
 panel.add(token);
 panel.add(tokenField);
 panel.add(minute);
 panel.add(minuteField);
 panel.add(update);
 panel.add(UpdateField);
 panel.add(sett);
 panel.add(ep2);

 //Create a window using JFrame with title ( Two text component in JOptionPane )  
 JFrame frame = new JFrame("DuckDNS Settings");

 //Set default close operation for JFrame  
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

 //Set JFrame size  
 frame.setSize(300, 300);

 //Set JFrame locate at center  
 frame.setLocationRelativeTo(null);

 //Make JFrame visible  
 frame.setVisible(false);

 //Show JOptionPane that will ask user for username and password  
 int a = JOptionPane.showConfirmDialog(frame, panel, "DuckDNS Settings", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);

 //Operation that will do when user click 'OK'  
 if (a == JOptionPane.OK_OPTION) {

 if (domainField.getText().length() > 0 && tokenField.getText().length() > 0) {
 prefs.put("domain", domainField.getText().replace(".duckdns.org", ""));
 prefs.put("token", tokenField.getText());
 prefs.put("refresh", minuteField.getSelectedItem().toString());
 String ip = getip();
 //  prefs.put("oldipaddress", ip);
 prefs.put("updatemessages", UpdateField.getSelectedItem().toString());

 String resultofipcheck = updateDuckDNS(prefs.get("domain", ""), prefs.get("token", ""), ip);

 if (resultofipcheck.equals("KO")) {
 JOptionPane.showMessageDialog(frame, "DuckDNS Error: Invalid domain or token!\nPlease correct your settings!", DuckDNSVersion, JOptionPane.INFORMATION_MESSAGE);
 }

 if (resultofipcheck.equals("OK")) {
 JOptionPane.showMessageDialog(frame, "DuckDNS settings successfully validated and saved!", DuckDNSVersion, JOptionPane.INFORMATION_MESSAGE);
 updatetraytip();
 timercount = (Integer.parseInt(prefs.get("refresh", "5")) * 60);
 }

 if ((!resultofipcheck.equals("OK")) && (!resultofipcheck.equals("KO"))) {
 JOptionPane.showMessageDialog(frame, "Unable to reach DuckDNS.org!", DuckDNSVersion, JOptionPane.INFORMATION_MESSAGE);
 }

 } else if ((domainField.getText().length() < 1) && (tokenField.getText().length() < 1)) {
 JOptionPane.showMessageDialog(frame, "Missing DuckDNS domain & token! Please try again!", "False", JOptionPane.ERROR_MESSAGE);
 prefs.put("domain", domainField.getText());
 prefs.put("token", tokenField.getText());
 prefs.put("refresh", minuteField.getSelectedItem().toString());
 } else if (domainField.getText().length() < 1) {
 JOptionPane.showMessageDialog(frame, "Missing DuckDNS domain name! Please try again!", "False", JOptionPane.ERROR_MESSAGE);
 prefs.put("domain", domainField.getText());
 prefs.put("token", tokenField.getText());
 prefs.put("refresh", minuteField.getSelectedItem().toString());

 } else if (tokenField.getText().length() < 1) {
 JOptionPane.showMessageDialog(frame, "Missing DuckDNS token! Please try again!", "False", JOptionPane.ERROR_MESSAGE);
 prefs.put("domain", domainField.getText());
 prefs.put("token", tokenField.getText());
 prefs.put("refresh", minuteField.getSelectedItem().toString());

 }
 }

 //Operation that will do when user click 'Cancel'  
 else if (a == JOptionPane.CANCEL_OPTION) {
 JOptionPane.showMessageDialog(frame, "Settings were not saved!");
 }
 }
});

// Add listener to aboutItem.
aboutItem.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {

 // for copying style
 JLabel label = new JLabel();
 Font font = label.getFont();

 // create some css from the label's font
 StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
 style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
 style.append("font-size:" + font.getSize() + "pt;");

 // html content
 JEditorPane ep = new JEditorPane("text/html", DuckDNSVersion + "<br>Developed by: ETX Software Inc.<br><html><a href=\"http://www.ETX.ca\">www.ETX.ca</a></html>");

 // handle link events
 ep.addHyperlinkListener(new HyperlinkListener() {
  public void hyperlinkUpdate(HyperlinkEvent e) {
  if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
  launchUrl(e.getURL().toString()); // roll your own link launcher or use Desktop if J6+
  }
  }
 });
 ep.setEditable(false);
 ep.setBackground(label.getBackground());

 // show
 JOptionPane.showMessageDialog(null, ep);
 }
});

// Add listener to forceupdateItem.
forceupdateItem.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {

 Preferences prefs = Preferences.userNodeForPackage(duckdns.class);
 timercount = (Integer.parseInt(prefs.get("refresh", "5")) * 60);
 updatetraytip();

 }
});

// Add listener to whatsmyipaddress.
whatsmyipaddress.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {

 String ip = getip();

 // for copying style
 JLabel label = new JLabel();
 Font font = label.getFont();

 // create some css from the label's font
 StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
 style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
 style.append("font-size:" + font.getSize() + "pt;");

 // html content
 JEditorPane ep = new JEditorPane("text/html", "Your External IP Address: \n<html><a href=\"http://checkip.amazonaws.com\">" + ip + "</a></html>");

 // handle link events
 ep.addHyperlinkListener(new HyperlinkListener() {
  public void hyperlinkUpdate(HyperlinkEvent e) {
  if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
  launchUrl(e.getURL().toString()); // roll your own link launcher or use Desktop if J6+
  }
  }
 });
 ep.setEditable(false);
 ep.setBackground(label.getBackground());

 // show
 JOptionPane.showMessageDialog(null, ep);

 }
});

exitItem.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {
 tray.remove(trayIcon);
 System.exit(0);
 }
});
}

private static String getip() {
Document doc = null;
String url = "http://checkip.amazonaws.com";
String ip = "";

try {
doc = Jsoup.connect(url).header("Cache-Control", "no-cache").ignoreHttpErrors(true).ignoreContentType(true).timeout(10 * 1000).get();
ip = doc.text();
} catch (IOException e) {
e.printStackTrace();
}
if (doc.text().length() < 7) {
// can't get ip address, let DuckDNS to resolve it
ip = "";
}
return ip;
}

private static String updateDuckDNS(String domain, String token, String ipaddress) {
String url = "http://www.duckdns.org/update?domains=" + domain + "&token=" + token + "&ip=" + ipaddress;
Document doc = null;
String ua = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.120 Safari/535.2";
try {
//	doc = Jsoup.connect(url).ignoreHttpErrors(true).timeout(10 * 1000).get();
doc = Jsoup.connect(url).userAgent(ua).ignoreHttpErrors(true).ignoreContentType(true).timeout(10 * 1000).get();

} catch (IOException e) {
e.printStackTrace();
}

return doc.text();
}

private static void updatetraytip() {
Preferences prefs = Preferences.userNodeForPackage(duckdns.class);

DateFormat dateFormat = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss aa");
Calendar cal2 = Calendar.getInstance();
cal2.add(Calendar.SECOND, ((Integer.parseInt(prefs.get("refresh", "5")) * 60) - timercount));

processTrayIcon.setToolTip(DuckDNSVersion + StatusMsg + "\nNext Update: " + dateFormat.format(cal2.getTime()) + "\nRefresh: " + prefs.get("refresh", "5") + " minutes.");

}

private static void launchUrl(String urlToLaunch) {
try {
if (!Desktop.isDesktopSupported() || !Desktop.getDesktop().isSupported(java.awt.Desktop.Action.BROWSE)) {
JOptionPane.showMessageDialog(null, "On this computer java cannot open automatically url in browser, you have to copy/paste it manually.");
return;
}

Desktop desktop = Desktop.getDesktop();
URI uri = new URI(urlToLaunch);

desktop.browse(uri);
} catch (URISyntaxException ex) {
JOptionPane.showMessageDialog(null, "Url [" + urlToLaunch + "] seems to be invalid ");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "There was some error opening the url. \n Details:\n" + ex.getMessage());
}

}

private void startProcess() {

Thread thread = new Thread(new Runnable() {

 @Override public void run() {
 Preferences prefs = Preferences.userNodeForPackage(duckdns.class);

 // first start message (on empty settings)
 if ((prefs.get("domain", "").length() < 1) || (prefs.get("token", "").length() < 1)) {
 processTrayIcon.displayMessage(DuckDNSVersion, "Right click on the tray icon to change the settings!", TrayIcon.MessageType.INFO);
 }

 Timer timer = new Timer("Repeater");
 MyTask t = new MyTask();
 timer.schedule(t, 0, 1000);

 timercount = (Integer.parseInt(prefs.get("refresh", "5")) * 60);

 }
});

thread.start();
}

class MyTask extends TimerTask {

@Override public void run() {

Preferences prefs = Preferences.userNodeForPackage(duckdns.class);
timercount++;

if (timercount > (Integer.parseInt(prefs.get("refresh", "5")) * 60)) {

// repeat on interval
try {

//Thread.sleep(Integer.parseInt(prefs.get("refresh", "5"))*60000);

String resultofipcheck;
String ip;

if ((prefs.get("domain", "").length() > 0) || (prefs.get("token", "").length() > 0)) {
ip = getip();
OldIp = InetAddress.getByName(prefs.get("domain", "") + ".duckdns.org").toString().replace(prefs.get("domain", "") + ".duckdns.org/", "");

// check if IP address is different from the last one. Only update is it differs (saving calls to DuckDNS).
if (!ip.equals(OldIp)) {

resultofipcheck = updateDuckDNS(prefs.get("domain", ""), prefs.get("token", ""), ip);
if (resultofipcheck.equals("KO")) {
processTrayIcon.displayMessage(DuckDNSVersion, "DuckDNS Error: Missing or invalid domain or token!\nRight click for settings!", TrayIcon.MessageType.INFO);
}

if (resultofipcheck.equals("OK")) {

if (prefs.get("updatemessages", "YES").equals("YES")) {
processTrayIcon.displayMessage(DuckDNSVersion, "DuckDNS successfully updated!\nCurrent IP Address: " + ip + "!", TrayIcon.MessageType.INFO);
StatusMsg = "\nOld IP: " + OldIp + "\nNew IP: " + ip;
// prefs.put("oldipaddress", ip);
}

}

if ((!resultofipcheck.equals("OK")) && (!resultofipcheck.equals("KO"))) {
processTrayIcon.displayMessage(DuckDNSVersion, "Unable to reach DuckDNS.org!", TrayIcon.MessageType.INFO);
}

} else {
if (prefs.get("updatemessages", "YES").equals("YES")) {
//processTrayIcon.displayMessage(DuckDNSVersion,"Update not necessary!\nOld IP Address: "+prefs.get("oldipaddress", "")+"!\nNew IP Address: "+ip+"!", TrayIcon.MessageType.INFO);

StatusMsg = "\nIP unchanged: " + OldIp + "";
}

}
updatetraytip();

}

} catch (Exception e) {
e.printStackTrace();
}

//after ran on schedule reset back to 0
timercount = 0;

}
}

}

}