/***********************************************************
Copyright (C) 2024 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
***********************************************************/
package com.verisign.epp.framework;

import java.io.IOException;
import java.io.InterruptedIOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import com.verisign.epp.codec.gen.EPPCodec;
import com.verisign.epp.codec.gen.EPPComponentNotFoundException;
import com.verisign.epp.codec.gen.EPPDecodeException;
import com.verisign.epp.codec.gen.EPPEncodeException;
import com.verisign.epp.codec.gen.EPPMessage;
import com.verisign.epp.exception.EPPException;
import com.verisign.epp.pool.parser.EPPSchemaCachingParserPool;
import com.verisign.epp.pool.transformer.EPPTransformerPool;
import com.verisign.epp.serverstub.SessionData;
import com.verisign.epp.util.EPPEnv;
import com.verisign.epp.util.EPPSendReceiveLogger;
import com.verisign.epp.util.EPPXMLByteArray;

/**
 * The {@code HttpEPPXMLAssembler} class provides an implementation of
 * {@link EPPAssembler} that can assemble/disassemble {@code EPPMessage}s and
 * {@code EPPEventResponse}s from HTTP.
 *
 * @see EPPAssembler
 */
public class HttpEPPXMLAssembler implements EPPByteArrayAssembler {

	/** Has the XML parser been initialized? */
	private static boolean poolsInitialized = false;

	/** Category for logging */
	private static Logger LOG = LoggerFactory.getLogger(HttpEPPXMLAssembler.class);

	/**
	 * Log the packet logging and receiver.
	 */
	private static EPPSendReceiveLogger sendReceiveLogger;

	/**
	 * An EPPCodec is delegated to to do the real work.
	 * <code>HttpEPPXMLAssembler</code> just wraps it to provide the EPPAssembler
	 * interface.
	 */
	private EPPCodec codec;

	/**
	 * Construct and instance of an <code>HttpEPPXMLAssembler</code>
	 */
	public HttpEPPXMLAssembler() {
		this.codec = EPPCodec.getInstance();
		initXmlPools();

		HttpEPPXMLAssembler.sendReceiveLogger = EPPEnv.getSendReceiveLogger();
	}

	/**
	 * Initialize the XML pools, which include the parser and transformer pools.
	 */
	private void initXmlPools() {
		// Pools not initialized?
		if (!poolsInitialized) {

			// Get reference to each of the pool instances, which will initialize
			// them if they are not already initialized.
			EPPSchemaCachingParserPool.getInstance();
			EPPTransformerPool.getInstance();

			poolsInitialized = true;
		}
	}

	/**
	 * Takes an input <code>byte</code> array and reads XML from it to create an
	 * <code>EPPEvent</code>
	 * 
	 * @param aBytes
	 *           The byte array to read data from.
	 * @param aData
	 *           A data object which can be used to store context information.
	 * @return EPPEvent The <code> EPPEvent </code> that is created from the
	 *         InputStream
	 * @exception EPPAssemblerException
	 *               Error creating the <code>EPPEvent</code>
	 */
	public EPPEvent decode(byte[] aBytes, Object aData) throws EPPAssemblerException {
		LOG.debug("decode(): Enter");

		SessionData theSessionData = (SessionData) aData;

		EPPMessage theMessage = null;
		try {
			/** Declare an instance of the EPPXMLByteArray class */
			EPPXMLByteArray xmlByteArray = new EPPXMLByteArray(EPPSchemaCachingParserPool.getInstance().getPool(),
			      EPPTransformerPool.getInstance().getPool());

			// Get the DOM Document from the xml stream
			Document domDocument = xmlByteArray.decode(aBytes);
			// Convert the DOM Document to an EPPMessage using the EPPCodec
			theMessage = this.codec.decode(domDocument);
			
			theSessionData.setMessage(theMessage);
		}
		catch (EPPComponentNotFoundException e) {
			LOG.debug("decode():", e);
			switch (e.getKind()) {
				case EPPComponentNotFoundException.COMMAND:
					throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.COMMANDNOTFOUND);
				case EPPComponentNotFoundException.EXTENSION:
					throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.EXTENSIONNOTFOUND);
				case EPPComponentNotFoundException.RESPONSE:
					throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.RESPONSENOTFOUND);
			}
		}
		catch (EPPDecodeException e) {
			LOG.debug("decode():", e);
			throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.MISSINGPARAMETER);
		}
		catch (EPPAssemblerException e) {
			LOG.debug("decode():", e);
			throw e;
		}
		catch (EPPException e) {
			LOG.debug("decode():", e);
			throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.XML);
		}
		catch (InterruptedIOException e) {
			LOG.debug("decode():", e);
			throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.INTRUPTEDIO);
		}
		catch (IOException e) {
			LOG.debug("decode():", e);
			throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.CLOSECON);
		}

		LOG.debug("decode(): Return");

		return new EPPEvent(theMessage);
	}

	/**
	 * Takes an <code> EPPEventResponse </code> and serializes it to a
	 * <code>byte</code> array in XML Format.
	 * 
	 * @param aResponse
	 *           The response that will be serialized
	 * @param aData
	 *           A data object which can be used to store context information.
	 * @exception EPPAssemblerException
	 *               Error serializing the <code>EPPEventResponse</code>
	 */
	public byte[] encode(EPPEventResponse aResponse, Object aData) throws EPPAssemblerException {
		LOG.debug("encode(): Enter");

		byte[] eppPacket = null;
		try {
			// First, get the message and convert it to a DOM Document using the
			// codec
			EPPMessage response = aResponse.getResponse();
			Document domDocument = this.codec.encode(response);

			EPPXMLByteArray xmlByteArray = new EPPXMLByteArray(EPPSchemaCachingParserPool.getInstance().getPool(),
			      EPPTransformerPool.getInstance().getPool());

			// convert the DOM document to bytes
			eppPacket = xmlByteArray.encode(domDocument);
		}
		catch (EPPEncodeException e) {
			LOG.debug("encode()", e);
			throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.MISSINGPARAMETER);
		}
		catch (EPPException e) {
			LOG.debug("encode()", e);
			throw new EPPAssemblerException(e.getMessage(), EPPAssemblerException.FATAL);
		}

		LOG.debug("encode(): Return");

		return eppPacket;
	}

}
