import java.lang.management.ManagementFactory;

import language.BnmxProtocol;
import tcpipcomm.JavaSocketServer;
import utils.BuildAnswer;
import utils.BuildAnswer.CommonAnswer;
import utils.CommandLineInterpreter;
import utils.FileLogger;
import utils.IngenicoLogger;
import utils.SimpleLogger;
import embosser.EmbosserCPX;

/**
 * Clase ejecutable para la comunicacion entre el pinpad y el sistema
 *
 * @author diego
 *
 */
//marco antonio

public class EMV2000Communication {

	/**
	 * Metodo main
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		JavaSocketServer myServer = null; // Socket Servidor.- Permite las comunicaciones por red
		CommandLineInterpreter myCommandLine = new CommandLineInterpreter(); // Interprete de la linea de comandos.
		BnmxProtocol bnmxProtocol = null; // Protocolo de comunicaciones de Banamex
		String incommingOrder = ""; // String con el comando de entrada
		String fullOrder = ""; // String con la instruccion completa.
		FileLogger logTrace; // = new FileLogger(); //Archivo de Log Hexadecimal

		SimpleLogger simpleLogger; // = new SimpleLogger(); //Archivo de Log Simple
		IngenicoLogger ingenicoLogger; // Archivo de Logg con las especificaciones de Ingenico

		boolean useUserSerialPort = false; // Define el puerto por el que se haran las comunicaciones
		String preferredPort = ""; // Usa el Puerto predefinido
		boolean printInteraction = false; // Muestra la interaccion
		boolean printLogFile = false; // Escribe en el Log
		boolean useUserServerPort = false; // Usa el puerto definido por el usuario
		boolean streamFullRecvd = false; // Bandera que determina si se ha recibido todo el buffer
		boolean streamLengthRcvd = false; // Usa la longitud definida por el usuario
		int streamLenght = 0; // Longitud del paquete
		int serverSocketPort = 0; // Socket de comunicaciones del puerto serial

		String emv2000Pid = ManagementFactory.getRuntimeMXBean().getName();
		emv2000Pid = emv2000Pid.substring(0, emv2000Pid.indexOf('@'));

		EmbosserCPX embCPX; // Clase para la comunicacion con la emposadora

		/**
		 * Verifica si el comando introducido es el de "mostrar la ayuda"
		 */
		if (myCommandLine.checkIfHelp(args)) {
			myCommandLine.printUsage();
			System.exit(0);
		}

		/**
		 * Verifica la sintaxis en la linea de comandos.
		 */
		if (!myCommandLine.evalCommandLine(args)) {
			myCommandLine.printUsage();
			System.exit(1);
		}

		/**
		 * Checa si se ha definido usar el puerto serial.
		 */
		useUserSerialPort = myCommandLine.checkIfPort(args);
		if (useUserSerialPort) {
			preferredPort = myCommandLine.getPortName(args);
		}

		printInteraction = myCommandLine.checkIfPrintInteraction(args); // Verifica si se mostraró el proceso en la línea de comandos
		printLogFile = myCommandLine.checkIfPrintLogFile(args); // Verifica si se guardaró un archivo Log

		/**
		 * Checa si se ha definido el puerto para el socket
		 */
		useUserServerPort = myCommandLine.checkIfServerSocket(args);
		if (useUserServerPort) {
			serverSocketPort = myCommandLine.getServerSocketPort(args);
		}

		/**
		 * Instancía el protocolo Banamex y sus archivos de Log
		 */
		logTrace = new FileLogger();
		simpleLogger = new SimpleLogger();
		ingenicoLogger = new IngenicoLogger(Boolean.TRUE);

  		bnmxProtocol = new BnmxProtocol(useUserSerialPort, preferredPort, printInteraction, printLogFile, logTrace, simpleLogger, ingenicoLogger);
		embCPX = new EmbosserCPX(simpleLogger, logTrace);

		// Si se ha definido el puerto de comunicaciones
		if (useUserServerPort) {
			myServer = new JavaSocketServer(serverSocketPort, simpleLogger); // Instancía un nuevo socket con dicho puerto
		} else {
			myServer = new JavaSocketServer(simpleLogger); // Instancía un socket con el puerto por default
		}

		// Levanta el socket y comienza a escuchar por una petición de conexión
		myServer.startListening();
		try {
			do {
				myServer.acceptConection(); // Acepta la conexion entrante (Abre el socket)
				myServer.prepareBuffers(); // ...y prepara los buffers

				streamFullRecvd = false; // Setea en falso que ha sido recibido todo el buffer
				streamLengthRcvd = false; // Setea en falso que ha recibido la longitud del mensaje

				fullOrder = ""; // Vacía la cadena de la instrucción completa
				do {
					incommingOrder = myServer.readFromConnection(); // Carga el mensaje enviado por el cliente (ECR)
					fullOrder = fullOrder + incommingOrder; // Concatena el mensaje entrante con la instrucción la cadena.

					/**
					 * Si no se ha leido la longitud de la cadena
					 */
					if (!streamLengthRcvd) {
						streamLenght = Integer.parseInt(fullOrder.substring(0, 4));
						streamLengthRcvd = true;
					}

					/**
					 * Si la longitud de la orden completa es igual a la longitud registrada por el ECR
					 */
					if (fullOrder.length() == streamLenght) {
						streamFullRecvd = true;
					}
				} while (!streamFullRecvd); // *--- Haz lo mismo, mientras no se haya llegado al final de la instrucción.
				simpleLogger.printStream("Mensaje entrante del ECR: '" + fullOrder + "'");
				bnmxProtocol.getClass();

				/**
				 * Si la subcadena del mensaje entrante no es "ENDTCPCOMM"
				 */
				if (!incommingOrder.substring(4).equalsIgnoreCase(BnmxProtocol.END_TCP_COMM)) {
					/**
					 * Si es válida la orden recibida en el mensaje entrante del ECR, en el BnmxProtocol ejecutará la instrucción que será enviada al PinPad 
					 * (Obtiene la subcadena a partir de la posición 4, ya que los primeros cuatro bytes es donde se almacena la longitud del mensaje)
					 */
					String answerToECR = "";

					if (incommingOrder.substring(4).equalsIgnoreCase(BnmxProtocol.RESTART_PINPAD)) {
						try {
							answerToECR = bnmxProtocol.restartPinpad();
						} catch (Exception e) {
							simpleLogger.printStream("Excepción en BnmxProtocol: " + e.getMessage());
							simpleLogger.printStream("ErrorDesc: ", e);
						}
					} else if (incommingOrder.substring(4).equalsIgnoreCase(BnmxProtocol.GET_EMV_PID)) {

						answerToECR = new BuildAnswer(CommonAnswer.GET_EMV_PID, emv2000Pid).getECRMessage();
					}
					if (bnmxProtocol.isValidOrder(fullOrder.substring(4))) {
						/**
						 * Entra cuando se detecta un comando que se enviará al PinPad
						 */
						try {
							answerToECR = bnmxProtocol.getAnswerOfLastOrder();
						} catch (Exception e) {
							simpleLogger.printStream("Excepción en BnmxProtocol: " + e.getMessage());
							simpleLogger.printStream("ErrorDesc: ", e);
						}
					} else if (embCPX.isValidOrder(fullOrder)) {
						/**
						 * Entra cuando se detecta un comando que se enviará a la Embosadora
						 */
						try {
							simpleLogger.printStream("Entró a EmbosserCPX");
							simpleLogger.printStream("Es una órden válida");
							answerToECR = embCPX.getAnswerOfLastOrder();
						} catch (Exception e) {
							simpleLogger.printStream("Excepción en EmbCPX: " + e.getMessage());
							simpleLogger.printStream("X EPU ErrorDesc: ", e);
						}
					} else {
						/**
						 * En caso de que no sea reconocida manda el siguiente token
						 */
						simpleLogger.printStream("No es una órden válida: ");
						bnmxProtocol.getClass();
						answerToECR = new BuildAnswer(CommonAnswer.ORDER_NOT_RECOGNIZED, "").getECRMessage();

					}
					myServer.writeToConnection(answerToECR);
					simpleLogger.printStream("Mensaje enviado al ECR: '" + answerToECR + "'");
				}

				// Cierra la conexión con el ECR.
				myServer.closeClient();
				bnmxProtocol.getClass();
			} while (!incommingOrder.substring(4).equalsIgnoreCase(BnmxProtocol.END_TCP_COMM)); // *--- Mientras la subcadena no sea "ENDTCPCOMM"
		} catch (Exception e) {
			simpleLogger.printStream("X EPU Error: " + e.getMessage());
			simpleLogger.printStream("X EPU ErrorDesc: ", e);
		} finally {
			myServer.closeAll();
			bnmxProtocol.closeOpenResources();
		}
	}
}