package client.gui;

import java.security.KeyPair;
import java.security.PublicKey;
import client.gui.GuiConnect.ServerInfo;
import client.gui.element.ActButton;
import client.gui.element.ButtonCallback;
import client.gui.element.Element;
import client.gui.element.Label;
import client.gui.element.NavButton;
import client.gui.element.PasswordField;
import client.gui.element.PressType;
import client.gui.element.Toggle;
import client.gui.element.FieldAction;
import client.gui.element.Field;
import client.gui.element.FieldCallback;
import client.vars.CVarCategory;
import client.vars.Variable;
import client.window.Window;
import common.network.IPlayer;
import common.util.EncryptUtil;
import common.util.Color;

public class GuiServer extends Gui implements FieldCallback {
	public static final GuiServer INSTANCE = new GuiServer(null);
	
	private final ServerInfo server;

	private Field nameBox;
	private Field addrBox;
	private Field portBox;
	private Field userBox;
	private PasswordField passBox;
	private PasswordField accBox;
	private Label nameLabel;
	private Label addrLabel;
	private Label portLabel;
	private Label rangeLabel;
	private Label userLabel;
	private Label passLabel;
	private Label accLabel;
	private Label keyLabel;
	private Label idLabel;
	private Toggle encToggle;
	private ActButton keyButton;
	private ActButton resetButton;
	private ActButton copyKeyButton;
	private ActButton copyIdButton;
	private KeyPair keypair;
	private String keyDigest;
	private PublicKey serverKey;
	private String serverDigest;
	
	public GuiServer(ServerInfo server) {
		this.server = server;
	}

    @Variable(name = "srv_last_address", category = CVarCategory.SYSTEM, min = 1, max = 128, display = "Letzte Server-Adresse")
	private String lastAddr = "";
	@Variable(name = "srv_last_port", category = CVarCategory.SYSTEM, min = 1024, max = 32767, display = "Letzter Server-Port")
	private int lastPort = -1;
    @Variable(name = "srv_last_user", category = CVarCategory.SYSTEM, max = IPlayer.MAX_USER_LENGTH, display = "Letzter Server-Nutzer", validator = IPlayer.UserValidator.class)
	private String lastUser = "";
    @Variable(name = "srv_last_password", category = CVarCategory.SYSTEM, max = IPlayer.MAX_PASS_LENGTH, display = "Letztes Server-Passwort")
	private String lastPass = "";
    @Variable(name = "srv_last_access", category = CVarCategory.SYSTEM, max = IPlayer.MAX_PASS_LENGTH, display = "Letzter Server-Zugang")
	private String lastAcc = "";
	
	public void init(int width, int height) {
		if(this.server != null) {
			this.nameBox = this.add(new Field(0, 0, 400, 0, 128, this, this.server.getName()));
			this.nameLabel = this.add(new Label(0, 0, 410, "Name in der Liste", true));
			this.keypair = this.server.getKeypair();
			this.keyDigest = this.keypair == null ? null : EncryptUtil.getXorSha512Hash(this.keypair.getPublic().getEncoded());
		}
		this.addrBox = this.add(new Field(0, 34, 400, 0, 128, this, this.server == null ? this.lastAddr : this.server.getAddress()));
		this.addrLabel = this.add(new Label(0, 34, 410, "Adresse / Hostname", true));
		int port = this.server == null ? this.lastPort : this.server.getPort();
		this.portBox = this.add(new Field(404, 34, 76, 0, 5, this, port < 0 ? "" : "" + port));
		this.portLabel = this.add(new Label(404, 34, 76, "Port", true));
		this.rangeLabel = this.add(new Label(370, 34 + Element.BASE_HEIGHT, 110, 0, "[1024..32767]", true));
		this.userBox = this.add(new Field(0, 68, 220, 0, IPlayer.MAX_USER_LENGTH, this, IPlayer.VALID_USER, this.server == null ? this.lastUser : this.server.getUser()));
		this.userLabel = this.add(new Label(0, 68, 220, "Benutzername", true));
		this.passBox = this.add(new PasswordField(0, this.server == null ? 102 : 136, 480, 0, IPlayer.MAX_PASS_LENGTH, this, this.server == null ? this.lastPass : this.server.getPassword()));
		this.passLabel = this.add(new Label(0, this.server == null ? 102 : 136, 480, (this.keypair == null ? "Anmelde" : "Ersatz") + "-Passwort (mind. 8 Zeichen)", true));
		this.accBox = this.add(new PasswordField(0, this.server == null ? 136 : 170, 480, 0, IPlayer.MAX_PASS_LENGTH, this, this.server == null ? this.lastAcc : this.server.getAccess()));
		this.accLabel = this.add(new Label(0, this.server == null ? 136 : 170, 480, "Server-Passwort (mind. 8 Zeichen)", true));
		this.add(new ActButton(241, this.server == null ? 166 : 254, 239, 0, new ButtonCallback() {
			public void use(ActButton elem, PressType action) {
				GuiServer.this.connect();
			}
		}, this.server == null ? "Verbinden" : (this.server.getName().isEmpty() ? "Hinzufügen" : "Übernehmen")));
		this.add(new NavButton(0, this.server == null ? 166 : 254, 239, 0, this.server != null ? GuiConnect.INSTANCE : GuiMenu.INSTANCE, "Zurück"));
		if(this.server != null) {
			this.keyButton = this.add(new ActButton(0, 102, 391, 0, new ButtonCallback() {
				public void use(ActButton elem, PressType action) {
					if(GuiServer.this.keypair == null) {
						GuiServer.this.keypair = EncryptUtil.createKeypair();
						GuiServer.this.keyDigest = EncryptUtil.getXorSha512Hash(GuiServer.this.keypair.getPublic().getEncoded());
						GuiServer.this.keyLabel.setText("Anmelde-Pubkey: " + EncryptUtil.KEY_ALGO_DISPLAY + " " + GuiServer.this.keyDigest);
						GuiServer.this.keyButton.setText("Schlüsselpaar entfernen");
						GuiServer.this.passLabel.setText("Ersatz-Passwort (mind. 8 Zeichen)");
						GuiServer.this.copyKeyButton.enabled = true;
					}
					else {
						final String name = GuiServer.this.nameBox.getText();
						final String addr = GuiServer.this.addrBox.getText();
						final String port = GuiServer.this.portBox.getText();
						final String pass = GuiServer.this.passBox.getText();
						final String acc = GuiServer.this.accBox.getText();
						final boolean reqEnc = GuiServer.this.encToggle.getValue();
						final KeyPair keys = GuiServer.this.keypair;
						final PublicKey key = GuiServer.this.serverKey;
						final String digest = GuiServer.this.keyDigest;
						final String sdigest = GuiServer.this.serverDigest;
						GuiServer.this.gm.show(new GuiConfirm(new GuiConfirm.Callback() {
							public void confirm(boolean confirmed) {
								GuiServer.this.gm.show(GuiServer.this);
								GuiServer.this.nameBox.setText(name);
								GuiServer.this.addrBox.setText(addr);
								GuiServer.this.portBox.setText(port);
								GuiServer.this.passBox.setText(pass);
								GuiServer.this.accBox.setText(acc);
								GuiServer.this.encToggle.setValue(reqEnc);
								GuiServer.this.serverKey = key;
								GuiServer.this.serverDigest = sdigest;
								GuiServer.this.idLabel.setText("Server-Pubkey: " + (key != null ? EncryptUtil.KEY_ALGO_DISPLAY + " " + GuiServer.this.serverDigest : "nicht vorhanden"));
								GuiServer.this.resetButton.enabled = key != null;
								GuiServer.this.copyIdButton.enabled = key != null;
								GuiServer.this.copyKeyButton.enabled = !confirmed;
								if(confirmed) {
									GuiServer.this.keypair = null;
									GuiServer.this.keyDigest = null;
									GuiServer.this.keyLabel.setText("Anmelde-Pubkey: nicht vorhanden");
									GuiServer.this.keyButton.setText("Neues Schlüsselpaar generieren");
									GuiServer.this.passLabel.setText("Anmelde-Passwort (mind. 8 Zeichen)");
								}
								else {
									GuiServer.this.keypair = keys;
									GuiServer.this.keyDigest = digest;
									GuiServer.this.keyLabel.setText("Anmelde-Pubkey: " + EncryptUtil.KEY_ALGO_DISPLAY + " " + GuiServer.this.keyDigest);
									GuiServer.this.keyButton.setText("Schlüsselpaar entfernen");
									GuiServer.this.passLabel.setText("Ersatz-Passwort (mind. 8 Zeichen)");
								}
							}
						}, "Schlüsselpaar wirklich entfernen?", "Wenn das Schlüsselpaar gelöscht wird, ist es nicht mehr möglich, sich damit\nauf dem Server zu identifizieren und sich anzumelden.\nDamit könnte der Zugriff auf den Server unmöglich werden.", "Schlüsselpaar löschen", "Abbrechen"));
					}
				}
			}, this.keypair == null ? "Neues Schlüsselpaar generieren" : "Schlüsselpaar entfernen"));
			this.copyKeyButton = this.add(new ActButton(395, 102, 85, 0, new ButtonCallback() {
				public void use(ActButton elem, PressType action) {
					if(GuiServer.this.keypair != null)
						Window.setClipboard(EncryptUtil.getArmoredPubkey(GuiServer.this.keypair.getPublic(), GuiServer.this.userBox.getText()));
				}
			}, "Kopieren"));
			this.copyKeyButton.enabled = this.keypair != null;
			this.keyLabel = this.add(new Label(0, 102, 480, "Anmelde-Pubkey: " + (this.keypair != null ? EncryptUtil.KEY_ALGO_DISPLAY + " " + this.keyDigest : "nicht vorhanden"), true));
			this.encToggle = this.add(new Toggle(0, 190, 480, 0, false, this.server.requiresEncryption(), null, "Nur Verschlüsselte Verbindung akzeptieren"));
			this.serverKey = this.server.getServerKey();
			this.serverDigest = this.serverKey == null ? null : EncryptUtil.getXorSha512Hash(this.serverKey.getEncoded());
			this.resetButton = this.add(new ActButton(0, 224, 391, 0, new ButtonCallback() {
				public void use(ActButton elem, PressType action) {
					if(GuiServer.this.serverKey != null) {
						GuiServer.this.serverKey = null;
						GuiServer.this.serverDigest = null;
						GuiServer.this.idLabel.setText("Server-Pubkey: nicht vorhanden");
						GuiServer.this.resetButton.enabled = false;
						GuiServer.this.copyIdButton.enabled = false;
					}
				}
			}, "Server-Identifizierung / Pubkey zurücksetzen"));
			this.resetButton.enabled = this.serverKey != null;
			this.copyIdButton = this.add(new ActButton(395, 224, 85, 0, new ButtonCallback() {
				public void use(ActButton elem, PressType action) {
					if(GuiServer.this.serverKey != null)
						Window.setClipboard(EncryptUtil.getArmoredPubkey(GuiServer.this.serverKey, GuiServer.this.nameBox.getText()));
				}
			}, "Kopieren"));
			this.copyIdButton.enabled = this.serverKey != null;
			this.idLabel = this.add(new Label(0, 224, 480, "Server-Pubkey: " + (this.serverKey != null ? EncryptUtil.KEY_ALGO_DISPLAY + " " + this.serverDigest : "nicht vorhanden"), true));
		}
		this.shift();
	}
	
	public String getTitle() {
		return this.server == null ? "Mit Server verbinden" : (this.server.getName().isEmpty() ? "Server hinzufügen" : "Server bearbeiten");
	}
	
	private void connect() {
		if(this.gm.world != null)
			return;
		String name = null;
		if(this.server != null) {
			name = this.nameBox.getText();
			if(name.isEmpty()) {
				this.nameLabel.setText(Color.RED + "Name in der Liste");
				return;
			}
			if(GuiConnect.INSTANCE.isNameTaken(name)) {
				this.nameLabel.setText("Name in der Liste - " + Color.RED + "Bereits vorhanden");
				return;
			}
		}
		String addr = this.addrBox.getText();
		if(addr.isEmpty()) {
			this.addrLabel.setText(Color.RED + "Adresse / Hostname");
			return;
		}
		int port = -1;
		if(this.portBox.getText().isEmpty()) {
			this.portLabel.setText(Color.RED + "Port");
			return;
		}
		else {
			try {
				port = Integer.parseInt(this.portBox.getText());
			}
			catch(NumberFormatException e) {
				this.rangeLabel.setText("[" + Color.RED + "1024..32767" + Color.RESET + "]");
				return;
			}
			if(port < 1024 || port > 32767) {
				this.rangeLabel.setText("[" + (port < 1024 ? Color.RED + "1024" + Color.RESET + "..32767" : "1024.." + Color.RED + "32767" + Color.RESET) + "]");
				return;
			}
		}
		String user = this.userBox.getText();
		if(user.isEmpty()) {
			this.userLabel.setText(Color.RED + "Benutzername");
			return;
		}
		String pass = this.passBox.getText();
		if(!pass.isEmpty() && pass.length() < 8) {
			this.passLabel.setText((this.keypair == null ? "Anmelde" : "Ersatz") + "-Passwort (" + Color.RED + "mind. 8 Zeichen" + Color.RESET + ")");
			return;
		}
		String acc = this.accBox.getText();
		if(!acc.isEmpty() && acc.length() < 8) {
			this.accLabel.setText("Zugang (" + Color.RED + "mind. 8 Zeichen" + Color.RESET + ")");
			return;
		}
		if(this.server == null) {
			this.lastAddr = addr;
			this.lastPort = port;
			this.lastUser = user;
			this.lastPass = pass;
			this.lastAcc = acc;
			this.gm.setDirty();
			GuiConnect.INSTANCE.connect(addr, port, user, pass, acc);
		}
		else {
			this.server.setData(name, addr, port, user, pass, acc, this.keypair, this.serverKey, this.encToggle.getValue());
			GuiConnect.INSTANCE.applyServer(this.server);
		}
	}
	
	public void use(Field elem, FieldAction value) {
		if(value == FieldAction.SEND) {
			elem.setDeselected();
			this.connect();
		}
		else if(value == FieldAction.FOCUS) {
			if(elem == this.addrBox)
				this.addrLabel.setText("Adresse / Hostname");
			else if(elem == this.portBox) {
				this.portLabel.setText("Port");
				this.rangeLabel.setText("[1024..32767]");
			}
			else if(elem == this.userBox)
				this.userLabel.setText("Benutzername");
			else if(elem == this.passBox)
				this.passLabel.setText((this.keypair == null ? "Anmelde" : "Ersatz") + "-Passwort (mind. 8 Zeichen)");
			else if(elem == this.accBox)
				this.accLabel.setText("Server-Passwort (mind. 8 Zeichen)");
			else if(this.server != null && elem == this.nameBox)
				this.nameLabel.setText("Name in der Liste");
		}
	}
}
