package hh.dejsem.net;

import android.content.res.Resources;

import java.io.InputStream;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;

import hh.dejsem.K;
import hh.dejsem.Prefs;
import hh.dejsem.R;
import hh.lib.D;

/**
 * network SSL node
 */
public class NetNode {
	D d;
	public int portBase;
	public SSLContext ctx;
	public SSLSocketFactory sf;
	public SSLServerSocketFactory ssf;
	/* ----------------------------------------------------------*/

	public NetNode(D d) throws Exception {
		this.d = d.klon(this);
		d.logPrefix += "[" + (Prefs.ssl ? "" : "non") + "SSL]";
		portBase = K.BASE_PORT_NUM + 10 * Integer.valueOf(d.getContext().getResources().getString(R.string.channel));
		prepareSsl();
		if(d.ll(3)) d.l("initalized");
	}

	private class X509KM extends X509ExtendedKeyManager {
		X509KeyManager standardKM;

	    X509KM(KeyManager standardKM) { this.standardKM = (X509KeyManager)standardKM; }
	   
		@Override 
	    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
			String valentine = standardKM.chooseClientAlias(keyType, issuers, socket);
			if(d.ll(4)) d.l("KM: chooseClientAlias: " + valentine);
			return valentine; }
	   
		@Override 
	    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
			if(d.ll(4)) d.l("KM: chooseServerAlias");
			return standardKM.chooseServerAlias(keyType, issuers, socket); }
	   
		@Override 
	    public X509Certificate[] getCertificateChain(String alias) {
			if(d.ll(4)) d.l("KM: getCertificateChain: alias=" + alias);
			return standardKM.getCertificateChain(alias); }

		@Override
	    public String[] getClientAliases(String keyType, Principal[] issuers) {
			if(d.ll(4)) d.l("KM: getClientAliases: key type=" + keyType);
			return standardKM.getClientAliases(keyType, issuers); }

		@Override 
	    public PrivateKey getPrivateKey(String alias) {
			if(d.ll(4)) d.l("KM: getPrivateKey: alias=" + alias);
			return standardKM.getPrivateKey(alias); }

		@Override
	    public String[] getServerAliases (String keyType, Principal[] issuers) {
			if(d.ll(4)) d.l("KM: getServerAliases");
			return standardKM.getServerAliases(keyType, issuers); }
	}

	private class X509TM implements X509TrustManager {
		X509TrustManager standardTM;
		X509Certificate trusted = null;
		X509TM(X509TrustManager standardTM) { this.standardTM = standardTM; }
	   
		@Override 
	    public X509Certificate[] getAcceptedIssuers() {
			X509Certificate[] accepted = standardTM.getAcceptedIssuers();
			if(d.ll(4)) d.l("TM: getAcceptedIssuers");
			return accepted; }

		@Override 
	    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			if(d.ll(4)) d.l("TM: checkClientTrusted");
			standardTM.checkClientTrusted(chain, authType); }

		@Override 
	    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			String cns = "";
			for(X509Certificate crt: chain) cns += (crt.getSubjectDN() + " ");
			if(d.ll(4)) d.l("TM: checkServerTrusted: server chain=" + cns);
			for(X509Certificate c: chain) if(d.ll(4)) d.l("\tDN=" + c.getSubjectDN()); }
	}

	void prepareSsl() throws Exception {
		if(K.word.length() == 0) throw new Exception("KeyStore integrity check failed.");   // tuhle exc generuje bcast při neplatném hesle
		KeyManager[] standardKMs;
		TrustManager[] standardTMs;

		if(Prefs.sslDebug) System.setProperty("javax.net.debug", "ssl,handshake");
		final char[] passphrase = K.word.toCharArray();
		Resources r = d.getContext().getResources();
		String ksName = String.format("bks%02d", Integer.valueOf(r.getString(R.string.channel)));
		int ksId = r.getIdentifier(ksName, "raw", d.getContext().getPackageName());
		ctx = SSLContext.getInstance("TLSv1");

		KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
		final KeyStore ks = KeyStore.getInstance("bks");
		InputStream kfs = r.openRawResource(ksId);
		ks.load(kfs, passphrase);
		kmf.init(ks, passphrase);
		standardKMs = kmf.getKeyManagers();
		X509KM x509km = new X509KM((X509KeyManager)standardKMs[0]);

		TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
		final KeyStore ts = KeyStore.getInstance("bks");
		InputStream tfs = r.openRawResource(ksId);
		ts.load(tfs, passphrase);
		tmf.init(ts);
		standardTMs = tmf.getTrustManagers();
		X509TM x509tm = new X509TM((X509TrustManager)standardTMs[0]);

		ctx.init(standardKMs, standardTMs, null);

		sf = ctx.getSocketFactory();
		ssf = ctx.getServerSocketFactory();
	}
}


