/***********************************************************
Copyright (C) 2018 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.interfaces.loginsecpolicy.v01;

import java.security.SecureRandom;
import java.util.Random;

import org.apache.log4j.Logger;

import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.codec.loginsecpolicy.v01.EPPLoginSecPolicyInfData;
import com.verisign.epp.codec.registry.v01.EPPRegistryInfoResp;
import com.verisign.epp.interfaces.EPPApplicationSingle;
import com.verisign.epp.interfaces.EPPCommandException;
import com.verisign.epp.interfaces.EPPSession;
import com.verisign.epp.interfaces.registry.v01.EPPRegistry;
import com.verisign.epp.util.EPPCatFactory;
import com.verisign.epp.util.Environment;
import com.verisign.epp.util.TestThread;

import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * Tests connecting to EPP SDK Stub Server, sending a registry system info
 * command and asserting that the Login Security Policy Extension is returned in
 * the response.
 */
public class EPPLoginSecPolicyRegistryTst extends TestCase {
	/**
	 * Handle to the Singleton EPP Application instance (
	 * {@code EPPApplicationSingle})
	 */
	private static EPPApplicationSingle app = EPPApplicationSingle.getInstance();

	/**
	 * Name of configuration file to use for test (default = epp.config).
	 */
	private static String configFileName = "epp.config";

	/**
	 * Logging category
	 */
	private static final Logger cat = Logger.getLogger(EPPLoginSecPolicyRegistryTst.class.getName(),
	      EPPCatFactory.getInstance().getFactory());

	/**
	 * EPP Registry associated with test
	 */
	private EPPRegistry registry = null;

	/**
	 * EPP Session associated with test
	 */
	private EPPSession session = null;

	/**
	 * Current test iteration
	 */
	private int iteration = 0;

	/**
	 * Random instance for the generation of unique objects (hosts, IP addresses,
	 * etc.).
	 */
	private Random rd = new SecureRandom();

	/**
	 * Allocates an {@code EPPLoginSecPolicyRegistryTst} with a logical name. The
	 * constructor will initialize the base class {@code TestCase} with the
	 * logical name.
	 *
	 * @param name
	 *           Logical name of the test
	 */
	public EPPLoginSecPolicyRegistryTst(String name) {
		super(name);
	}

	/**
	 * Test for the registry system info command that expects the system to
	 * include the Login Security Extension.
	 */
	public void testInfo() {
		printStart("testInfo");

		EPPRegistryInfoResp response = null;

		try {

			this.registry.setTransId("ABC-12345-XYZ");
			this.registry.setSystem(true);

			response = this.registry.sendInfo();

			assertTrue(response != null && response.isSuccess());

			System.out.println("System Info Response = " + response);

			EPPLoginSecPolicyInfData loginSecPolicyExt = (EPPLoginSecPolicyInfData) response
			      .getExtension(EPPLoginSecPolicyInfData.class);
			// Commented out since it's not the latest version supported default by
			// the default Stub Server configuration
			// assertTrue(loginSecPolicyExt != null);
			if (loginSecPolicyExt != null) {
				System.out.println("Login Security Policy: " + loginSecPolicyExt.getSystem());
			}

			printEnd("testInfo");
		}
		catch (EPPCommandException e) {
			handleException(e);
		}

	}

	/**
	 * Handle an {@code EPPCommandException}, which can be either a server
	 * generated error or a general exception. If the exception was caused by a
	 * server error, "Server Error :&lt;Response XML&gt;" will be specified. If
	 * the exception was caused by a general algorithm error, "General Error
	 * :&lt;Exception Description&gt;" will be specified.
	 *
	 * @param aException
	 *           Exception thrown during test
	 */
	public void handleException(Exception aException) {
		EPPResponse theResponse = null;
		if (aException instanceof EPPCommandException) {
			theResponse = ((EPPCommandException) aException).getResponse();
		}

		aException.printStackTrace();

		// Is a server specified error?
		if ((theResponse != null) && (!theResponse.isSuccess())) {
			Assert.fail("Server Error : " + theResponse);
		}

		else {
			Assert.fail("General Error : " + aException);
		}
	}

	public static Test suite() {
		TestSuite suite = new TestSuite(EPPLoginSecPolicyRegistryTst.class);

		String theConfigFileName = System.getProperty("EPP.ConfigFile");
		if (theConfigFileName != null) {
			configFileName = theConfigFileName;
		}

		try {
			app.initialize(configFileName);
		}

		catch (EPPCommandException e) {
			e.printStackTrace();

			Assert.fail("Error initializing the EPP Application: " + e);
		}

		return suite;
	}

	@Override
	protected void setUp() {
		try {
			String theSessionClassName = System.getProperty("EPP.SessionClass");

			if (theSessionClassName != null) {
				try {
					Class theSessionClass = Class.forName(theSessionClassName);

					if (!EPPSession.class.isAssignableFrom((theSessionClass))) {
						Assert.fail(theSessionClassName + " is not a subclass of EPPSession");
					}

					this.session = (EPPSession) theSessionClass.getDeclaredConstructor().newInstance();
				}
				catch (Exception ex) {
					Assert.fail("Exception instantiating EPP.SessionClass value " + theSessionClassName + ": " + ex);
				}
			}
			else {
				this.session = new EPPSession();
			}

			this.session.setClientID(Environment.getProperty("EPP.Test.clientId", "ClientX"));
			this.session.setPassword(Environment.getProperty("EPP.Test.password", "foo-BAR2"));
		}

		catch (Exception e) {
			e.printStackTrace();

			Assert.fail("Error initializing the session: " + e);
		}

		initSession();

		// System.out.println("out init");
		this.registry = new EPPRegistry(this.session);
	}

	/**
	 * JUNIT {@code tearDown}, which currently does nothing.
	 */
	@Override
	protected void tearDown() {
		endSession();
	}

	/**
	 * Unit test of {@code EPPSession.initSession}. The session attribute is
	 * initialized with the attributes defined in the EPP sample files.
	 */
	private void initSession() {
		printStart("initSession");

		// Set attributes for initSession
		this.session.setTransId("ABC-12345-XYZ");

		this.session.setVersion("1.0");

		this.session.setLang("en");

		// Initialize the session
		try {
			this.session.initSession();
		}

		catch (EPPCommandException e) {
			EPPResponse response = this.session.getResponse();

			// Is a server specified error?
			if ((response != null) && (!response.isSuccess())) {
				Assert.fail("Server Error : " + response);
			}
			else {
				e.printStackTrace();

				Assert.fail("initSession Error : " + e);
			}
		}

		printEnd("initSession");
	}

	/**
	 * Unit test of {@code EPPSession.endSession}. The session with the EPP
	 * Server will be terminated.
	 */
	private void endSession() {
		printStart("endSession");

		this.session.setTransId("ABC-12345-XYZ");

		// End the session
		try {
			this.session.endSession();
		}

		catch (EPPCommandException e) {
			EPPResponse response = this.session.getResponse();

			// Is a server specified error?
			if ((response != null) && (!response.isSuccess())) {
				Assert.fail("Server Error : " + response);
			}

			else // Other error
			{
				e.printStackTrace();

				Assert.fail("initSession Error : " + e);
			}
		}

		printEnd("endSession");
	}

	/**
	 * Print the start of a test with the {@code Thread} name if the current
	 * thread is a {@code TestThread}.
	 *
	 * @param aTest
	 *           name for the test
	 */
	private void printStart(String aTest) {
		if (Thread.currentThread() instanceof TestThread) {
			System.out.print(Thread.currentThread().getName() + ", iteration " + this.iteration + ": ");

			cat.info(Thread.currentThread().getName() + ", iteration " + this.iteration + ": " + aTest + " Start");
		}

		System.out.println("Start of " + aTest);

		System.out.println("****************************************************************\n");
	}

	/**
	 * Print the end of a test with the {@code Thread} name if the current thread
	 * is a {@code TestThread}.
	 *
	 * @param aTest
	 *           name for the test
	 */
	private void printEnd(String aTest) {
		System.out.println("****************************************************************");

		if (Thread.currentThread() instanceof TestThread) {
			System.out.print(Thread.currentThread().getName() + ", iteration " + this.iteration + ": ");

			cat.info(Thread.currentThread().getName() + ", iteration " + this.iteration + ": " + aTest + " End");
		}

		System.out.println("End of " + aTest);

		System.out.println("\n");
	}

}
