/***********************************************************
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.launchpolicy.v01;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.apache.log4j.Logger;

import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyCreate;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyInfData;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyInfoPhase;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyPhase;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyPhaseStatus;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyPollPolicy;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyUpdate;
import com.verisign.epp.codec.launchpolicy.v01.EPPLaunchPolicyZone;
import com.verisign.epp.codec.registry.v01.EPPRegistryCheckResp;
import com.verisign.epp.codec.registry.v01.EPPRegistryCheckResult;
import com.verisign.epp.codec.registry.v01.EPPRegistryContact;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactAddress;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactCity;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactName;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactOrg;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactPostalCode;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactStateProvince;
import com.verisign.epp.codec.registry.v01.EPPRegistryContactStreet;
import com.verisign.epp.codec.registry.v01.EPPRegistryCreateResp;
import com.verisign.epp.codec.registry.v01.EPPRegistryDNSSEC;
import com.verisign.epp.codec.registry.v01.EPPRegistryDomain;
import com.verisign.epp.codec.registry.v01.EPPRegistryDomainContact;
import com.verisign.epp.codec.registry.v01.EPPRegistryDomainHostLimit;
import com.verisign.epp.codec.registry.v01.EPPRegistryDomainNSLimit;
import com.verisign.epp.codec.registry.v01.EPPRegistryDomainName;
import com.verisign.epp.codec.registry.v01.EPPRegistryDomainPeriod;
import com.verisign.epp.codec.registry.v01.EPPRegistryExternalHost;
import com.verisign.epp.codec.registry.v01.EPPRegistryGracePeriod;
import com.verisign.epp.codec.registry.v01.EPPRegistryHost;
import com.verisign.epp.codec.registry.v01.EPPRegistryIDN;
import com.verisign.epp.codec.registry.v01.EPPRegistryInfoResp;
import com.verisign.epp.codec.registry.v01.EPPRegistryInternalHost;
import com.verisign.epp.codec.registry.v01.EPPRegistryKey;
import com.verisign.epp.codec.registry.v01.EPPRegistryLanguage;
import com.verisign.epp.codec.registry.v01.EPPRegistryMaxSig;
import com.verisign.epp.codec.registry.v01.EPPRegistryMinMaxLength;
import com.verisign.epp.codec.registry.v01.EPPRegistryPendingDeletePeriodType;
import com.verisign.epp.codec.registry.v01.EPPRegistryPendingRestorePeriodType;
import com.verisign.epp.codec.registry.v01.EPPRegistryPeriodType;
import com.verisign.epp.codec.registry.v01.EPPRegistryPostal;
import com.verisign.epp.codec.registry.v01.EPPRegistryRGP;
import com.verisign.epp.codec.registry.v01.EPPRegistryRedemptionPeriodType;
import com.verisign.epp.codec.registry.v01.EPPRegistryRegex;
import com.verisign.epp.codec.registry.v01.EPPRegistryReservedNames;
import com.verisign.epp.codec.registry.v01.EPPRegistryServices;
import com.verisign.epp.codec.registry.v01.EPPRegistryServices.EPPRegistryObjURI;
import com.verisign.epp.codec.registry.v01.EPPRegistryServicesExt;
import com.verisign.epp.codec.registry.v01.EPPRegistryServicesExt.EPPRegistryExtURI;
import com.verisign.epp.codec.registry.v01.EPPRegistrySupportedStatus;
import com.verisign.epp.codec.registry.v01.EPPRegistrySupportedStatus.Status;
import com.verisign.epp.codec.registry.v01.EPPRegistryTransferHoldPeriodType;
import com.verisign.epp.codec.registry.v01.EPPRegistryZoneSummary;
import com.verisign.epp.codec.registry.v01.EPPRegistryZone;
import com.verisign.epp.codec.registry.v01.EPPRegistryZoneName;
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;

public class EPPLaunchPolicyRegistryTst 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(EPPLaunchPolicyRegistryTst.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 EPPLaunchPolicyRegistryTst} 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 EPPLaunchPolicyRegistryTst(String name) {
		super(name);
	}

	/**
	 * Test the Registry Mapping by doing the following:
	 * <ol>
	 * <li>Create a set of zones.</li>
	 * <li>Update a zone.</li>
	 * <li>Check the availability (existence) of the zones.</li>
	 * <li>Get all summary information for the zones.</li>
	 * <li>Get detailed zone information for some zones.</li>
	 * <li>Get registry system information.</li>
	 * <li>Delete a set of zones.</li>
	 * <li>
	 *
	 * </ol>
	 */
	public void testRegistry() {
		int numIterations = 1;

		String iterationsStr = System.getProperty("iterations");

		if (iterationsStr != null) {
			try {
				numIterations = Integer.parseInt(iterationsStr);
				numIterations = (numIterations < 1) ? 1 : numIterations;
			}
			catch (Exception e) {
				numIterations = 1;
			}
		}

		printStart("Test Suite");

		registryCreate("launchpolicytst", true);

		registryUpdate("launchpolicytst", true);

		registryInfo("launchpolicytst", false, false);

		registryDelete("launchpolicytst", true);

		printEnd("Test Suite");
	}

	/**
	 * Unit test of {@code EPPRegistry.sendCheck}.
	 * 
	 * @param aZoneNames
	 *           Names of the zones to check
	 */
	public void registryCheck(Map aZoneNames) {
		printStart("registryCheck");

		EPPRegistryCheckResp response;
		try {
			System.out.println("\n----------------------------------------------------------------");
			System.out.print("registryCheck:");
			this.registry.setTransId("ABC-12345-XYZ");

			for (Iterator it = aZoneNames.entrySet().iterator(); it.hasNext();) {
				String name = (String) ((Entry) it.next()).getKey();
				System.out.print(" " + name + " ");
				this.registry.addZone(name);
			}
			System.out.println("");

			response = this.registry.sendCheck();

			System.out.println("registryCheck: Response = [" + response + "]\n\n");

			for (Iterator it = aZoneNames.entrySet().iterator(); it.hasNext();) {
				Entry entry = (Entry) it.next();
				String name = (String) entry.getKey();
				Boolean available = (Boolean) entry.getValue();
				inner: for (Object element : response.getCheckResults()) {
					EPPRegistryCheckResult result = (EPPRegistryCheckResult) element;
					if (result.getName().equals(name)) {
						if (result.isAvailable().booleanValue() == available.booleanValue()) {
							break inner;
						}
						else {
							fail("Expected availability for tld \"" + name + "\": " + available.booleanValue() + ", but got: "
							      + result.isAvailable());
						}
					}
				}
			}

			assertTrue(response != null && response.isSuccess());
		}
		catch (EPPCommandException e) {
			handleException(e);
		}

		printEnd("registryCheck");
	}

	/**
	 * Unit test of {@code EPPRegistry.sendCreate}.
	 * 
	 * @param aZoneName
	 *           Zone to create
	 * @param aShouldSucceed
	 *           {@code true} if the zone create should succeed; {@code false}
	 *           otherwise.
	 */
	public void registryCreate(String aZoneName, boolean aShouldSucceed) {
		printStart("registryCreate");

		EPPRegistryCreateResp response = null;
		try {
			System.out.println("\n----------------------------------------------------------------");
			System.out.println("registryCreate: " + aZoneName);
			this.registry.setTransId("ABC-12345-XYZ");
			this.registry.setZone(this.buildZoneInfo(new EPPRegistryZoneName(aZoneName)));

			// Add the Launch Phase Policy Extension
			EPPLaunchPolicyCreate launchPolicyExt = new EPPLaunchPolicyCreate(buildLaunchPolicyZone());
			this.registry.addExtension(launchPolicyExt);

			response = this.registry.sendCreate();
			System.out.println("registryCreate: Response = [" + response + "]\n\n");

			if (aShouldSucceed) {
				assertTrue(response != null && response.isSuccess());
			}
			else {
				fail("Expecting error in response");
			}
		}
		catch (EPPCommandException e) {
			if (aShouldSucceed) {
				handleException(e);
			}
		}

		printEnd("registryCreate");
	}

	/**
	 * Unit test of {@code EPPRegistry.sendDelete}.
	 * 
	 * @param aZoneName
	 *           Zone to delete
	 * @param aShouldSucceed
	 *           {@code true} if the zone delete should succeed; {@code false}
	 *           otherwise.
	 */
	public void registryDelete(String aZoneName, boolean aShouldSucceed) {
		printStart("registryDelete");

		EPPResponse response = null;
		try {
			System.out.println("\n----------------------------------------------------------------");
			System.out.println("registryDelete: " + aZoneName);
			this.registry.setTransId("ABC-12345-XYZ");
			this.registry.addZone(aZoneName);

			response = this.registry.sendDelete();
			System.out.println("registryDelete: Response = [" + response + "]\n\n");

			if (aShouldSucceed) {
				assertTrue(response != null && response.isSuccess());
			}
			else {
				fail("Expecting error in response");
			}
		}
		catch (EPPCommandException e) {
			if (aShouldSucceed) {
				handleException(e);
			}
		}

		printEnd("registryDelete");
	}

	/**
	 * Unit test of {@code EPPRegistry.sendUpdate}.
	 * 
	 * @param aZoneName
	 *           Zone to update
	 * @param aShouldSucceed
	 *           {@code true} if the zone update should succeed; {@code false}
	 *           otherwise.
	 */
	public void registryUpdate(String aZoneName, boolean aShouldSucceed) {
		printStart("registryUpdate");

		EPPResponse response = null;
		try {
			System.out.println("\n----------------------------------------------------------------");
			System.out.println("registryUpdate: " + aZoneName);
			this.registry.setTransId("ABC-12345-XYZ");
			this.registry.setZone(this.buildZoneInfo(new EPPRegistryZoneName(aZoneName)));

			// Add the Launch Phase Policy Extension
			EPPLaunchPolicyUpdate launchPolicyExt = new EPPLaunchPolicyUpdate(buildLaunchPolicyZone());
			this.registry.addExtension(launchPolicyExt);

			response = this.registry.sendUpdate();
			System.out.println("registryUpdate: Response = [" + response + "]\n\n");

			if (aShouldSucceed) {
				assertTrue(response != null && response.isSuccess());
			}
			else {
				fail("Expecting error in response");
			}
		}
		catch (EPPCommandException e) {
			if (aShouldSucceed) {
				handleException(e);
			}
		}

		printEnd("registryUpdate");
	}

	/**
	 * Test for the registry info command that supports the three forms of
	 * getting detailed information for an individual zone by setting the
	 * {@code aZoneName} parameter to a non-{@code null} value, getting a summary
	 * of all zones by setting the {@code aAll} parameter to true, and getting
	 * the system information by setting the {@code aSystem} parameter to true.
	 * There can be only one form used as a time.
	 *
	 * @param aZoneName
	 *           Alabel zone name to get the detailed information for
	 * @param aAll
	 *           Set to {@code true} to get a summary information for all zones
	 * @param aSystem
	 *           Set to {@code true} to get the registry system information
	 */
	public void registryInfo(String aZoneName, boolean aAll, boolean aSystem) {
		printStart("registryInfo");

		EPPRegistryInfoResp response = null;

		try {

			// Zone detailed information form?
			if (aZoneName != null) {
				if (aAll || aSystem) {
					Assert.fail("[1} Only one form of info command can be used at a time");
				}

				this.registry.setTransId("ABC-12345-XYZ");
				this.registry.addZone(aZoneName);
				response = this.registry.sendInfo();

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

				assertTrue(response.getZoneList() == null && response.getZoneInfo() != null);
				EPPRegistryZone info = response.getZoneInfo();
				System.out.println("Zone name: " + info.getName());
				if (info.getServices() != null) {
					System.out.println("Services:");
					for (Object element : info.getServices().getObjURIs()) {
						EPPRegistryObjURI objUri = (EPPRegistryObjURI) element;
						System.out.println("\tobjURI: " + objUri.getUri() + ", required: " + objUri.getRequired());
					}
				}
				if (info.getServices() != null && info.getServices().getExtension() != null) {
					System.out.println("Services extension:");
					for (Iterator it = info.getServices().getExtension().getExtURIs().iterator(); it.hasNext();) {
						EPPRegistryExtURI extUri = (EPPRegistryExtURI) it.next();
						System.out.println("\textURI: " + extUri.getUri() + ", required: " + extUri.getRequired());
					}
				}
				System.out.println("crId: " + info.getCreatedBy());
				System.out.println("crDate: " + info.getCreatedDate());
				System.out.println("upId: " + info.getLastUpdatedBy());
				System.out.println("upDate: " + info.getLastUpdatedDate());

				EPPRegistryDomain domain = info.getDomain();
				assertTrue(domain != null);
				System.out.println("Domain: " + domain);
				EPPRegistryHost host = info.getHost();
				assertTrue(host != null);
				System.out.println("Host: " + host);
				EPPRegistryContact contact = info.getContact();
				assertTrue(contact != null);
				System.out.println("Contact: " + contact);

				EPPLaunchPolicyInfData launchPolicyExt = (EPPLaunchPolicyInfData) response
				      .getExtension(EPPLaunchPolicyInfData.class);
				assertTrue(launchPolicyExt != null);
				if (launchPolicyExt != null) {
					System.out.println("Launch Policy Zone: " + launchPolicyExt.getZone());
				}
			}
			else if (aAll) {
				if (aSystem) {
					Assert.fail("[2] Only one form of info command can be used at a time");
				}

				this.registry.setTransId("ABC-12345-XYZ");
				this.registry.setAllZones(true);
				response = this.registry.sendInfo();

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

				assertTrue(response.getZoneList() != null && response.getZoneList().getZoneList() != null
				      && response.getZoneInfo() == null && response.getZoneList().getZoneList().size() > 0);
				System.out.println("All TLDs: ");
				for (Iterator it = response.getZoneList().getZoneList().iterator(); it.hasNext();) {
					EPPRegistryZoneSummary tld = (EPPRegistryZoneSummary) it.next();
					assertTrue(tld.getName().getName() != null && tld.getName().getName().length() > 0);
					assertTrue(tld.getCreateDate() != null);
					System.out.print(tld.getName() + "\tcreated on " + tld.getCreateDate());
					if (tld.getUpdateDate() != null) {
						System.out.println("\tupdated on " + tld.getUpdateDate());
					}
					else {
						System.out.println();
					}
				}
			}
			else if (aSystem) {
				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);
			}
			else {
				Assert.fail("At least one info command form must be specified");
			}

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

	/**
	 * Make a pseudo random zone name.
	 *
	 * @return pseudo random zone name
	 */
	public String makeZoneName() {
		int len = this.rd.nextInt(15);
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < len; i++) {
			sb.append('a' + this.rd.nextInt(26));
		}
		return sb.toString();
	}

	/**
	 * 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(EPPLaunchPolicyRegistryTst.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");
	}

	/**
	 * Creates a populated {@link EPPRegistryZone} instance given a zone name.
	 *
	 * @param aZone
	 *           Zone name to create a populated {@link EPPRegistryZone} instance
	 *           for.
	 *
	 * @return Populated {@link EPPRegistryZone} instance
	 */
	private EPPRegistryZone buildZoneInfo(EPPRegistryZoneName aZone) {

		EPPRegistryZone theZoneInfo = new EPPRegistryZone(aZone);

		theZoneInfo.setDomain(this.buildDomain());
		theZoneInfo.setHost(this.buildHost());
		theZoneInfo.setContact(this.buildContact());

		EPPRegistryServices services = new EPPRegistryServices();
		services.addObjURI(new EPPRegistryObjURI("urn:ietf:params:xml:ns:registry-1.2", Boolean.TRUE));
		services.addObjURI(new EPPRegistryObjURI("urn:ietf:params:xml:ns:registry-1.1", Boolean.FALSE));
		EPPRegistryServicesExt svcExt = new EPPRegistryServicesExt();
		services.setExtension(svcExt);
		svcExt.addExtURI(new EPPRegistryExtURI("urn:ietf:params:xml:ns:registry-1.2", Boolean.TRUE));
		svcExt.addExtURI(new EPPRegistryExtURI("urn:ietf:params:xml:ns:registry-1.1", Boolean.FALSE));
		theZoneInfo.setServices(services);

		theZoneInfo.setCreatedBy("crId");
		theZoneInfo.setCreatedDate(new Date());

		return theZoneInfo;
	}

	/**
	 * Build a populated domain object policy object to include in a zone.
	 *
	 * @return A populated {@link EPPRegistryDomain} instance
	 */
	private EPPRegistryDomain buildDomain() {
		EPPRegistryDomain domain = new EPPRegistryDomain();

		List domainNames = new ArrayList();
		EPPRegistryDomainName domainName = new EPPRegistryDomainName();
		domainName.setLevel(Integer.valueOf(2));
		domainName.setMinLength(Integer.valueOf(5));
		domainName.setMaxLength(Integer.valueOf(50));
		domainName.setAlphaNumStart(Boolean.valueOf(true));
		domainName.setAlphaNumEnd(Boolean.valueOf(false));
		domainName.setALabelSupported(Boolean.valueOf(true));

		List regex = new ArrayList();
		EPPRegistryRegex r = new EPPRegistryRegex("^\\w+.*$", "test regex");
		regex.add(r);

		r = new EPPRegistryRegex("^\\d+.*$");
		regex.add(r);
		domainName.setRegex(regex);

		EPPRegistryReservedNames reservedNames = new EPPRegistryReservedNames();
		List rNames = new ArrayList();
		reservedNames.setReservedNames(rNames);
		rNames.add("reserved1");
		rNames.add("reserved2");
		// reservedNames.setReservedNameURI("http://example.com/reservedNames");

		domainName.setReservedNames(reservedNames);
		domainNames.add(domainName);

		try {
			domainName = (EPPRegistryDomainName) domainName.clone();
			domainName.setLevel(Integer.valueOf(3));
			domainName.getReservedNames().setReservedNames(new ArrayList());
			domainName.getReservedNames().setReservedNameURI("http://testrn.vrsn.com");
			domainNames.add(domainName);
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}

		domain.setDomainNames(domainNames);

		EPPRegistryIDN idn = new EPPRegistryIDN();
		idn.setIdnVersion("1.1");
		idn.setIdnaVersion("2008");
		idn.setUnicodeVersion("6.0");
		idn.addLanguage(new EPPRegistryLanguage("CHI", "http://www.iana.org/domains/idn-tables/tables/com_zh_1.1.txt",
		      EPPRegistryLanguage.VariantStrategy.restricted));
		idn.addLanguage(new EPPRegistryLanguage("LATN", "http://www.iana.org/domains/idn-tables/tables/eu_latn_1.0.html",
		      EPPRegistryLanguage.VariantStrategy.blocked));
		idn.setCommingleAllowed(Boolean.TRUE);
		domain.setIdn(idn);

		domain.setPremiumSupport(Boolean.valueOf(true));
		domain.setContactsSupported(Boolean.valueOf(false));

		domain.addContact(new EPPRegistryDomainContact(EPPRegistryDomainContact.Type.admin, 1, 4));
		domain.addContact(new EPPRegistryDomainContact(EPPRegistryDomainContact.Type.billing, 2, 5));
		domain.addContact(new EPPRegistryDomainContact(EPPRegistryDomainContact.Type.tech, 3, 6));

		domain.setNameServerLimit(new EPPRegistryDomainNSLimit(1, 16));

		domain.setChildHostLimit(new EPPRegistryDomainHostLimit(2, 32));

		domain.addPeriod(new EPPRegistryDomainPeriod("create", Boolean.TRUE));
		domain.addPeriod(new EPPRegistryDomainPeriod("renew", 1, EPPRegistryPeriodType.Unit.y, 10,
		      EPPRegistryPeriodType.Unit.y, 2, EPPRegistryPeriodType.Unit.y));
		domain.addPeriod(new EPPRegistryDomainPeriod("transfer", 1, EPPRegistryPeriodType.Unit.y, 8,
		      EPPRegistryPeriodType.Unit.y, 3, EPPRegistryPeriodType.Unit.y));

		domain.setTransferHoldPeriod(new EPPRegistryTransferHoldPeriodType(1, EPPRegistryPeriodType.Unit.y));

		domain.addGracePeriod(new EPPRegistryGracePeriod("create", 1, EPPRegistryPeriodType.Unit.m));
		domain.addGracePeriod(new EPPRegistryGracePeriod("renew", 2, EPPRegistryPeriodType.Unit.h));
		domain.addGracePeriod(new EPPRegistryGracePeriod("transfer", 3, EPPRegistryPeriodType.Unit.d));

		EPPRegistryRGP rgp = new EPPRegistryRGP();
		rgp.setPendingDeletePeriod(new EPPRegistryPendingDeletePeriodType(1, EPPRegistryPeriodType.Unit.m));
		rgp.setRedemptionPeriod(new EPPRegistryRedemptionPeriodType(1, EPPRegistryPeriodType.Unit.m));
		rgp.setPendingRestorePeriod(new EPPRegistryPendingRestorePeriodType(1, EPPRegistryPeriodType.Unit.m));
		domain.setRgp(rgp);

		EPPRegistryDNSSEC dnssec = new EPPRegistryDNSSEC();
		// EPPRegistryDS ds = new EPPRegistryDS(1, 3, null, null);
		// ds.addAlgorithm("algDS1");
		// ds.addAlgorithm("algDS2");
		// ds.addDigestType("digest1");
		// ds.addDigestType("digest2");
		// dnssec.setDs(ds);

		EPPRegistryKey key = new EPPRegistryKey(2, 4, null);
		key.addAlgorithm("algKey1");
		key.addAlgorithm("algKey2");
		key.addAlgorithm("algKey3");
		dnssec.setKey(key);

		dnssec.setMaxSigLife(new EPPRegistryMaxSig(true, 1, 2, 3));
		dnssec.setUrgent(Boolean.TRUE);

		domain.setDnssec(dnssec);
		domain.setMaxCheckDomain(Integer.valueOf(12));
		domain.setAuthInfoRegex(new EPPRegistryRegex("^.*$", "exp"));

		EPPRegistrySupportedStatus supportedStatus = new EPPRegistrySupportedStatus();
		supportedStatus.addStatus(Status.DOMAIN_CLIENTDELETEPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_SERVERDELETEPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_CLIENTHOLD);
		supportedStatus.addStatus(Status.DOMAIN_SERVERHOLD);
		supportedStatus.addStatus(Status.DOMAIN_CLIENTRENEWPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_SERVERRENEWPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_CLIENTTRANSFERPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_SERVERTRANSFERPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_CLIENTUPDATEPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_SERVERUPDATEPROHIBITED);
		supportedStatus.addStatus(Status.DOMAIN_INACTIVE);
		supportedStatus.addStatus(Status.DOMAIN_OK);
		supportedStatus.addStatus(Status.DOMAIN_PENDINGCREATE);
		supportedStatus.addStatus(Status.DOMAIN_PENDINGDELETE);
		supportedStatus.addStatus(Status.DOMAIN_PENDINGRENEW);
		supportedStatus.addStatus(Status.DOMAIN_PENDINGTRANSFER);
		supportedStatus.addStatus(Status.DOMAIN_PENDINGUPDATE);
		domain.setSupportedStatus(supportedStatus);

		return domain;
	}

	/**
	 * Build a populated contact object policy object to include in a zone.
	 *
	 * @return A populated {@link EPPRegistryContact} instance
	 */
	private EPPRegistryContact buildContact() {
		EPPRegistryContact contact = new EPPRegistryContact();

		contact.setContactIdRegex(new EPPRegistryRegex("^.*$"));
		contact.setSharePolicy(EPPRegistryContact.SharePolicy.perZone);

		contact.setIntPostalInfoTypeSupport(EPPRegistryContact.PostalInfoTypeSupport.locSupport);

		contact.setAuthInfoRegex(new EPPRegistryRegex("^.*$", "exp"));

		contact.setMaxCheckContact(Integer.valueOf(15));

		EPPRegistryPostal postalInfo = new EPPRegistryPostal();
		postalInfo.setName(new EPPRegistryContactName(5, 15));
		postalInfo.setOrg(new EPPRegistryContactOrg(2, 12));
		postalInfo.setVoiceRequired(Boolean.TRUE);
		postalInfo.setEmailRegex(new EPPRegistryRegex("^.+\\..+$"));

		EPPRegistryContactAddress address = new EPPRegistryContactAddress();
		address.setStreet(new EPPRegistryContactStreet(2, 12, 0, 3));
		address.setCity(new EPPRegistryContactCity(5, 15));
		address.setStateProvince(new EPPRegistryContactStateProvince(1, 11));
		address.setPostalCode(new EPPRegistryContactPostalCode(2, 12));

		postalInfo.setAddress(address);
		postalInfo.setVoiceRequired(Boolean.TRUE);
		postalInfo.setVoiceExt(new EPPRegistryMinMaxLength(5, 15));
		postalInfo.setFaxExt(new EPPRegistryMinMaxLength(5, 15));

		contact.setMaxCheckContact(Integer.valueOf(5));

		contact.setPostalInfo(postalInfo);

		EPPRegistrySupportedStatus supportedStatus = new EPPRegistrySupportedStatus();
		supportedStatus.addStatus(Status.CONTACT_CLIENTDELETEPROHIBITED);
		supportedStatus.addStatus(Status.CONTACT_SERVERDELETEPROHIBITED);
		supportedStatus.addStatus(Status.CONTACT_CLIENTTRANSFERPROHIBITED);
		supportedStatus.addStatus(Status.CONTACT_SERVERTRANSFERPROHIBITED);
		supportedStatus.addStatus(Status.CONTACT_CLIENTUPDATEPROHIBITED);
		supportedStatus.addStatus(Status.CONTACT_SERVERUPDATEPROHIBITED);
		supportedStatus.addStatus(Status.CONTACT_LINKED);
		supportedStatus.addStatus(Status.CONTACT_OK);
		supportedStatus.addStatus(Status.CONTACT_PENDINGCREATE);
		supportedStatus.addStatus(Status.CONTACT_PENDINGDELETE);
		supportedStatus.addStatus(Status.CONTACT_PENDINGTRANSFER);
		supportedStatus.addStatus(Status.CONTACT_PENDINGUPDATE);
		contact.setSupportedStatus(supportedStatus);

		return contact;
	}

	/**
	 * Build a populated host object policy object to include in a zone.
	 *
	 * @return A populated {@link EPPRegistryHost} instance
	 */
	private EPPRegistryHost buildHost() {
		EPPRegistryHost host = new EPPRegistryHost();

		host.setInternal(new EPPRegistryInternalHost(5, 15, EPPRegistryInternalHost.SharePolicy.perZone, Boolean.FALSE));
		host.setExternal(new EPPRegistryExternalHost(2, 12, EPPRegistryExternalHost.SharePolicy.perZone, Boolean.FALSE));
		host.addNameRegex(new EPPRegistryRegex("^.*$", "exp1"));
		host.addNameRegex(new EPPRegistryRegex("^.*$", "exp2"));
		host.setMaxCheckHost(Integer.valueOf(15));

		EPPRegistrySupportedStatus supportedStatus = new EPPRegistrySupportedStatus();
		supportedStatus.addStatus(Status.HOST_CLIENTDELETEPROHIBITED);
		supportedStatus.addStatus(Status.HOST_SERVERDELETEPROHIBITED);
		supportedStatus.addStatus(Status.HOST_CLIENTUPDATEPROHIBITED);
		supportedStatus.addStatus(Status.HOST_SERVERUPDATEPROHIBITED);
		supportedStatus.addStatus(Status.HOST_LINKED);
		supportedStatus.addStatus(Status.HOST_OK);
		supportedStatus.addStatus(Status.HOST_PENDINGCREATE);
		supportedStatus.addStatus(Status.HOST_PENDINGDELETE);
		supportedStatus.addStatus(Status.HOST_PENDINGTRANSFER);
		supportedStatus.addStatus(Status.HOST_PENDINGUPDATE);
		host.setSupportedStatus(supportedStatus);

		return host;
	}

	/**
	 * Build a populated {@link EPPLaunchPolicyZone} instance that can be
	 * included in one of the Launch Policy Extensions. The populated attributes
	 * are based on the sample included in draft-gould-regext-launch-policy. The
	 * following phases are added to the populated {@link EPPLaunchPolicyZone}
	 * instance:<br>
	 * <ol>
	 * <li>Sunrise</li>
	 * <li>Claims for Limited Registration Period #1 (LRP1)</li>
	 * <li>Claims for Landrush</li>
	 * <li>Claims (Open)</li>
	 * <li>Custom for Limited Registration Period #2 (LRP2)</li>
	 * <li>Open</li>
	 * </ol>
	 * 
	 * @return Populated {@link EPPLaunchPolicyZone} based on the sample included
	 *         in draft-gould-regext-launch-policy.
	 */
	public static EPPLaunchPolicyZone buildLaunchPolicyZone() {
		EPPLaunchPolicyZone zone = new EPPLaunchPolicyZone();

		// Sunrise
		EPPLaunchPolicyPhase sunrise = new EPPLaunchPolicyPhase(EPPLaunchPolicyPhase.Phase.sunrise,
		      new GregorianCalendar(2017, 11, 01, 0, 0).getTime());
		sunrise.setMode(EPPLaunchPolicyPhase.Mode.pendingApplication);
		sunrise.setEndDate(new GregorianCalendar(2017, 12, 01, 0, 0).getTime());
		sunrise.setValidatePhase(true);
		sunrise.addValidatorId("tmch");
		sunrise.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.pendingAllocation));
		sunrise.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.allocated));
		sunrise.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.rejected));
		sunrise.addStatus(
		      new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.custom, "test", null, "test status"));
		sunrise.addMarkValidation(EPPLaunchPolicyPhase.MarkValidation.signedMark);
		sunrise.setMaxMarks(1);
		sunrise.addSignedMarkSupported("urn:ietf:params:xml:ns:signedMark-1.0");
		sunrise.addEncodedSignedMarkSupported("urn:ietf:params:xml:ns:signedMark-1.0");
		sunrise.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.sunrise));
		sunrise.addCreateForm(EPPLaunchPolicyPhase.CreateForm.sunrise);
		sunrise.setCreateValidateType(true);

		zone.addPhase(sunrise);

		// Claims LRP1
		EPPLaunchPolicyPhase lrp1 = new EPPLaunchPolicyPhase(EPPLaunchPolicyPhase.Phase.claims,
		      new GregorianCalendar(2017, 12, 01, 0, 0).getTime());
		lrp1.setName("lrp1");
		lrp1.setMode(EPPLaunchPolicyPhase.Mode.pendingRegistration);
		lrp1.setEndDate(new GregorianCalendar(2017, 12, 8, 0, 0).getTime());
		lrp1.setValidatePhase(true);
		lrp1.addValidatorId("tmch");
		lrp1.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.pendingValidation));
		lrp1.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.allocated));
		lrp1.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.rejected));
		lrp1.setPendingCreate(true);
		lrp1.addCheckForm(EPPLaunchPolicyPhase.CheckForm.claims);
		lrp1.addCheckForm(EPPLaunchPolicyPhase.CheckForm.availability);
		lrp1.addCheckForm(EPPLaunchPolicyPhase.CheckForm.trademark);
		lrp1.addMarkValidation(EPPLaunchPolicyPhase.MarkValidation.signedMark);
		lrp1.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.sunrise));
		lrp1.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.claims, "lrp1"));
		lrp1.addCreateForm(EPPLaunchPolicyPhase.CreateForm.claims);
		lrp1.setCreateValidateType(true);

		zone.addPhase(lrp1);

		// Claims Landrush
		EPPLaunchPolicyPhase landrush = new EPPLaunchPolicyPhase(EPPLaunchPolicyPhase.Phase.claims,
		      new GregorianCalendar(2017, 12, 8, 0, 0).getTime());
		landrush.setName("landrush");
		landrush.setMode(EPPLaunchPolicyPhase.Mode.pendingApplication);
		landrush.setEndDate(new GregorianCalendar(2017, 12, 15, 0, 0).getTime());
		landrush.setValidatePhase(true);
		landrush.addValidatorId("tmch");
		landrush.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.pendingAllocation));
		landrush.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.allocated));
		landrush.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.rejected));
		landrush.setPendingCreate(true);
		landrush.setPollPolicy(new EPPLaunchPolicyPollPolicy(false, false, false));
		landrush.addCheckForm(EPPLaunchPolicyPhase.CheckForm.claims);
		landrush.addCheckForm(EPPLaunchPolicyPhase.CheckForm.availability);
		landrush.addCheckForm(EPPLaunchPolicyPhase.CheckForm.trademark);
		landrush.addMarkValidation(EPPLaunchPolicyPhase.MarkValidation.signedMark);
		landrush.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.sunrise));
		landrush.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.claims, "lrp1"));
		landrush.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.claims, "landrush"));
		landrush.addCreateForm(EPPLaunchPolicyPhase.CreateForm.claims);
		landrush.setCreateValidateType(true);

		zone.addPhase(landrush);

		// Claims Open
		EPPLaunchPolicyPhase claims = new EPPLaunchPolicyPhase(EPPLaunchPolicyPhase.Phase.claims,
		      new GregorianCalendar(2017, 12, 15, 0, 0).getTime());
		claims.setName("open");
		claims.setMode(EPPLaunchPolicyPhase.Mode.fcfs);
		claims.setEndDate(new GregorianCalendar(2018, 2, 15, 0, 0).getTime());
		claims.setValidatePhase(true);
		claims.addValidatorId("tmch");
		claims.addCheckForm(EPPLaunchPolicyPhase.CheckForm.claims);
		claims.addCheckForm(EPPLaunchPolicyPhase.CheckForm.availability);
		claims.addCheckForm(EPPLaunchPolicyPhase.CheckForm.trademark);
		claims.addMarkValidation(EPPLaunchPolicyPhase.MarkValidation.signedMark);
		claims.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.claims, "landrush"));
		claims.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.claims, "open"));
		claims.addCreateForm(EPPLaunchPolicyPhase.CreateForm.claims);
		claims.addCreateForm(EPPLaunchPolicyPhase.CreateForm.general);
		claims.setCreateValidateType(true);

		zone.addPhase(claims);

		// Custom LRP2
		EPPLaunchPolicyPhase lrp2 = new EPPLaunchPolicyPhase(EPPLaunchPolicyPhase.Phase.custom,
		      new GregorianCalendar(2018, 2, 15, 0, 0).getTime());
		lrp2.setName("lrp2");
		lrp2.setMode(EPPLaunchPolicyPhase.Mode.pendingRegistration);
		lrp2.setEndDate(new GregorianCalendar(2018, 3, 15, 0, 0).getTime());
		lrp2.setValidatePhase(true);
		lrp2.addValidatorId("lrp2-custom");
		lrp2.addStatus(
		      new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.custom, "pendingInternalValidation"));
		lrp2.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.allocated));
		lrp2.addStatus(new EPPLaunchPolicyPhaseStatus(EPPLaunchPolicyPhaseStatus.Status.rejected));
		lrp2.setPendingCreate(true);
		lrp2.setPollPolicy(new EPPLaunchPolicyPollPolicy(true, false, false));
		lrp2.addCheckForm(EPPLaunchPolicyPhase.CheckForm.claims);
		lrp2.addCheckForm(EPPLaunchPolicyPhase.CheckForm.availability);
		lrp2.addCheckForm(EPPLaunchPolicyPhase.CheckForm.trademark);
		lrp2.addMarkValidation(EPPLaunchPolicyPhase.MarkValidation.signedMark);
		sunrise.setMaxMarks(1);
		sunrise.addSignedMarkSupported("http://www.example.com/epp/lrp2-custom-1.0");
		sunrise.addEncodedSignedMarkSupported("http://www.example.com/epp/lrp2-custom-1.0");
		lrp2.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.claims, "open"));
		lrp2.addInfoPhase(new EPPLaunchPolicyInfoPhase(EPPLaunchPolicyPhase.Phase.custom, "lrp2"));
		lrp2.addCreateForm(EPPLaunchPolicyPhase.CreateForm.sunrise);
		lrp2.addCreateForm(EPPLaunchPolicyPhase.CreateForm.general);
		lrp2.addCreateForm(EPPLaunchPolicyPhase.CreateForm.claims);
		lrp2.addCreateForm(EPPLaunchPolicyPhase.CreateForm.mixed);
		lrp2.setCreateValidateType(true);

		zone.addPhase(lrp2);

		// Open
		EPPLaunchPolicyPhase open = new EPPLaunchPolicyPhase(EPPLaunchPolicyPhase.Phase.open,
		      new GregorianCalendar(2018, 3, 15, 0, 0).getTime());
		open.setValidatePhase(false);

		zone.addPhase(open);

		return zone;
	}

}
