package models.messaging.nibss;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManager;

import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.content.SharedPreferences;
import android.content.Context;
import android.widget.Toast;
import org.apache.cordova.*;
import com.esl.lib.PaypadFacade;
import facade.PayPadManager;
import extras.ReferenceList;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;
import java.net.Socket;
import java.io.ByteArrayInputStream;
import java.security.cert.CertificateFactory;


public class HSMClient extends IntentService {

	public static final int UPDATE_PROGRESS = 2;
	public static final int UPDATE_FAILED = 3;

	private PayPadManager Pd;

	public HSMClient() {
		super("HSMClient");
	}

	@Override
	protected void onHandleIntent(Intent intent) {

		Pd = new PayPadManager(this);

		try{
			JSONObject obj = new JSONObject();
			obj.put("operation","initialization");
			obj.put("status","downloading_keys");

			PluginResult presult = new PluginResult(PluginResult.Status.OK, obj);
			presult.setKeepCallback(true);
			PaypadFacade.callbackContext.sendPluginResult(presult);

		}catch(JSONException e){

		}

		String certificateString = "-----BEGIN CERTIFICATE-----\n" +
		"MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\n" +
		"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" +
		"DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\n" +
		"PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\n" +
		"Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" +
		"AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\n" +
		"rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\n" +
		"OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\n" +
		"xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n" +
		"7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\n" +
		"aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n" +
		"HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\n" +
		"SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\n" +
		"ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\n" +
		"AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\n" +
		"R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\n" +
		"JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\n" +
		"Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n" +
		"-----END CERTIFICATE-----";

		ResultReceiver receiver = (ResultReceiver) intent
				.getParcelableExtra("receiver");

		try {

			ByteArrayInputStream stream = new ByteArrayInputStream(certificateString.getBytes());
            CertificateFactory factory = CertificateFactory.getInstance("X509");
            java.security.cert.X509Certificate certificate = 
                    (java.security.cert.X509Certificate)factory.generateCertificate(stream);

            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(null);
                trustStore.setCertificateEntry("DSTRootCAX3", certificate);        

			SSLContext context = SSLContext.getInstance("TLS");

			KeyStore keyStore = null;

			// This is needed for non ics phones
			String nonICS = "/system/etc/security/cacerts.bks";

			try {

				keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
				FileInputStream inputStream = new FileInputStream(nonICS);
				keyStore.load(inputStream, null);

			} catch (Exception ex) {
				// PaypadFacade.showStaticToastLong("Error 1: "+ ex.getMessage());

				try {
					// Enters here if OS is ICS
					keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
					keyStore.load(null, null);
				} catch (Exception ex1) {
					PaypadFacade.showStaticToastLong("Error 2: "+ ex.getMessage());
				}

			}

			try{
				keyStore.setCertificateEntry("DSTRootCAX3", certificate);
			}catch(Exception ex){
				// PaypadFacade.showStaticToastLong(KeyStore.getDefaultType()+" Error: "+ ex.getMessage());
			}

			TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
			tmf.init(keyStore);

			MyTrustManager manager = new MyTrustManager(trustStore);

			context.init(null, new TrustManager[]{manager}, null);

			SSLSocketFactory socketFactory = context.getSocketFactory();

			SSLSocket socket = (SSLSocket) socketFactory.createSocket(
					Globals.downloadServerIP, Globals.downloadServerPort);

			socket.startHandshake();
			// Socket socket = new Socket(Globals.downloadServerIP, Globals.downloadServerPort);
			
			socket.setSoTimeout(105*1000*2);

			if (socket.isConnected()) {
				System.out.println("Client is connected successfully");
			}

			PinpadKeysRequestProto.PinpadKeysRequest.Builder builder = PinpadKeysRequestProto.PinpadKeysRequest.newBuilder();

			builder.setTerminalId(Globals.merchantTerminalID);
			builder.setSerialNo(Globals.srn);
			builder.setVas(Globals.doVas);

			// builder.setTerminalId("2011225S");
   //          builder.setVas(false);
   //          builder.setSerialNo("040098DB85040D11100E000012BF7CD6");

			OutputStream outputStream = socket.getOutputStream();
			InputStream inputStream = socket.getInputStream();

			builder.build().writeDelimitedTo(outputStream);

			PinpadKeysResponseProto.PinpadKeysResponse response = PinpadKeysResponseProto.PinpadKeysResponse
					.parseDelimitedFrom(inputStream);

			// Toast.makeText(getApplicationContext(), "Status : "+response.getStatus(), Toast.LENGTH_LONG).show();

			if (response == null || response.getStatus() == false) {

				Toast.makeText(getApplicationContext(), "Key Download failed", Toast.LENGTH_SHORT).show();
				PaypadFacade.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "90-No response"));

			}else{
				System.out.println("Data key: " + response.getBdk());
				System.out.println("Data key TR-31: " + response.getDataTr31());
				System.out.println("DUPKT key : " + response.getDupktTr31());
				System.out.println("Pin key : " + response.getTmkTr31());
				System.out.println("Pin key1 : " + response.getPinKey());
				System.out.println("TSK: " + response.getTsk());
				System.out.println("Parameter: " + response.getParameter());

				Globals.bdkKey = response.getBdk();

				String cond = response.getDupktTr31() + "\u0000";

				Globals.TR31dupktKey = cond.getBytes();

				cond = response.getTmkTr31() + "\u0000";
				Globals.TR31tmkKey = cond.getBytes();

				cond = response.getDataTr31() + "\u0000";
				Globals.TR31dataKey = cond.getBytes();

				String dupkt = response.getDupktTr31();			
				String tmk = response.getTmkTr31();
				String data = response.getDataTr31();

				String params = response.getParameter();

				String string = "";

				for(int i=0 ; i < params.length(); i++ ){
                
	                if (Character.UnicodeBlock.of(params.charAt(i)) != Character.UnicodeBlock.BASIC_LATIN) {
	                    string += " ";
	                }else{
	                    string += params.charAt(i)+"";
	                }
	            }

	            params = string;

				parseField62(params);

                String tsk = response.getTsk().replace(" ", "");
                String tpk = response.getPinKey().replace(" ", "");


                // This is for vas
                if(response.getVasPresent() == true){

                	cond = response.getTmkTr31Other() + "\u0000";
	                Globals.TR31tmkVasKey = cond.getBytes();

	                params = response.getParameterOther();

					string = "";

					for(int i=0 ; i < params.length(); i++ ){
	                
		                if (Character.UnicodeBlock.of(params.charAt(i)) != Character.UnicodeBlock.BASIC_LATIN) {
		                    string += " ";
		                }else{
		                    string += params.charAt(i)+"";
		                }
		            }

		            params = string;

	                parseField62Vas(params);
	            }


                JSONObject paramsJsonObj = new JSONObject();
                paramsJsonObj.put("tsk", tsk);
                paramsJsonObj.put("tpk", tpk);
                paramsJsonObj.put("bdk", response.getBdk());
                paramsJsonObj.put("TR31dupktKey", dupkt);
                paramsJsonObj.put("TR31tmkKey", tmk);
                paramsJsonObj.put("TR31dataKey", data);
                paramsJsonObj.put("acceptorID", Globals.acceptorID);
                paramsJsonObj.put("acceptorName", Globals.acceptorName);
                paramsJsonObj.put("merchantType", Globals.merchantType);
                paramsJsonObj.put("stan", "000005");
                paramsJsonObj.put("rrn", "000000000005");

                Context contextP = this;
                // SharedPreferences preferences = ReferenceList.config.getSharedPreferences(Globals.preference,0);
                SharedPreferences.Editor editor = ReferenceList.config.edit();
                editor.putString(Globals.TR31TmkKey,response.getTmkTr31());
                editor.putString(Globals.TPKKey,response.getPinKey());
                editor.putString(Globals.TSKKey, response.getTsk());
                editor.putString(Globals.TR31DupktKey,response.getDupktTr31());
                editor.putString(Globals.TR31DataKey, response.getDataTr31());
                editor.putString(Globals.DataKey, response.getDataKey());
                editor.putString(Globals.BDKKey,response.getBdk());
                editor.putString(Globals.acceptorIDKey,Globals.acceptorID);
                editor.putString(Globals.acceptorNameKey,Globals.acceptorName);
				editor.putString(Globals.merchantTypeKey,Globals.merchantType);
				editor.putString(Globals.terminalIdKey,Globals.merchantTerminalID);
				editor.putString(Globals.plainSRNKey, Globals.plainSRN); // This is to store the plain serial number at the back of teh PINPAD
				editor.putBoolean(Globals.keyDownloaded,true);

				// This save the VAS information at this stage

				if(response.getVasPresent() == true){

					//enters here if vas is selected

					editor.putBoolean(Globals.VAS_PRESENT_KEY,true);
					editor.putString(Globals.acceptorIDVasKey,Globals.acceptorIDVas);
					editor.putString(Globals.acceptorNameVasKey,
						Globals.acceptorNameVas);
					editor.putString(Globals.merchantTypeVasKey,
							Globals.merchantTypeVas);
					editor.putString(Globals.terminalIdVasKey,
							response.getVasTid());

					editor.putString(Globals.TR31TmkVasKey,response.getTmkTr31Other());
	                editor.putString(Globals.TPKVasKey,response.getPinKeyOther());
	                editor.putString(Globals.TSKVasKey, response.getTskOther());

				}else{

					//enters here if vas is not selected
					Globals.TR31tmkVasKey = null;
					editor.putBoolean(Globals.VAS_PRESENT_KEY,false);
				}

				editor.commit();

				Pd.doKeyLoad();

				try{
					JSONObject objR = new JSONObject();
					objR.put("operation","initialisation");
					objR.put("status","loading_keys_to_pinpad");
					objR.put("hasvas",response.getVasPresent()+"");
					objR.put("serialno", Globals.plainSRN);

					PluginResult result = new PluginResult(PluginResult.Status.OK, objR);
					result.setKeepCallback(true);
					PaypadFacade.callbackContext.sendPluginResult(result);
					
				}catch(JSONException e){

				}
				
			}
		}  catch (SocketTimeoutException e) {

			e.printStackTrace();
			// Toast.makeText(getApplicationContext(), "Key Download failed", Toast.LENGTH_SHORT).show();
			PaypadFacade.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "80-Timeout, No response"+e.getMessage()));
			
		}catch (Exception e) {

			e.printStackTrace();
			// Toast.makeText(getApplicationContext(), "Key Download failed", Toast.LENGTH_SHORT).show();
			PaypadFacade.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "90-No response"+e.getMessage()));
	
		}

	}

	public static void parseField62(String msg) {

		String message = msg;
		String[] subMsg = new String[8];

		do {

			int i = 0;

			String value = message.substring(2, 5);
			int lengthValue = Integer.parseInt(value);
			subMsg[i] = message.substring(5, 5 + lengthValue);

            String s = message.substring(0, 2);
            if (s.equals("02")) {
            // System.out.print("Time and date: ");
            // System.out.println(subMsg[i]);

            } else if (s.equals("03")) {
                Globals.acceptorID = subMsg[i];

            } else if (s.equals("04")) {
            	// This is the time for us to get response from the NIBSS server
                System.out.print("Timeout: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("05")) {
                System.out.print("Currency Code: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("06")) {
                System.out.print("Currency Code: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("07")) {
                System.out.print("Call Timeout: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("08")) {
                Globals.merchantType = subMsg[i];

            } else if (s.equals("52")) {
                Globals.acceptorName = subMsg[i];

            }
			message = message.substring(5 + lengthValue);

			i++;
		} while (!message.isEmpty());

	}

	public static void parseField62Vas(String msg) {

		String message = msg;
		String[] subMsg = new String[8];

		do {

			int i = 0;

			String value = message.substring(2, 5);
			int lengthValue = Integer.parseInt(value);
			subMsg[i] = message.substring(5, 5 + lengthValue);

            String s = message.substring(0, 2);
            if (s.equals("02")) {
            // System.out.print("Time and date: ");
            // System.out.println(subMsg[i]);

            } else if (s.equals("03")) {
                Globals.acceptorIDVas = subMsg[i];

            } else if (s.equals("04")) {
            	// This is the time for us to get response from the NIBSS server
                System.out.print("Timeout: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("05")) {
                System.out.print("Currency Code: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("06")) {
                System.out.print("Currency Code: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("07")) {
                System.out.print("Call Timeout: ");
                System.out.println(subMsg[i]);

            } else if (s.equals("08")) {
                Globals.merchantTypeVas = subMsg[i];

            } else if (s.equals("52")) {
                Globals.acceptorNameVas = subMsg[i];

            }
			message = message.substring(5 + lengthValue);

			i++;
		} while (!message.isEmpty());

	}

}
