/***********************************************************
 Copyright (C) 2004 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-0107  USA

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.verisign.epp.codec.change.EPPChangeCheckResp;
import com.verisign.epp.codec.change.EPPChangeInfoResp;
import com.verisign.epp.codec.change.util.RandomHelper;
import com.verisign.epp.codec.changeext.EPPChangeExt;
import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.codec.gen.EPPResult;
import com.verisign.epp.transport.EPPClientCon;
import com.verisign.epp.util.Environment;
import com.verisign.epp.util.TestThread;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Is a unit test of the <code>EPPChange</code> class. The unit test will
 * initialize a session with an EPP Server, will invoke <code>EPPChange</code>
 * operations, and will end a session with an EPP Server.
 * <p>
 * The last test in this file creates an EPP Change Extension and adds that
 * extension to the EPPChange interface. The particular interface doesn't matter
 * because the EPPChangeExt can be added to any EPPComponent. In this case we
 * use the EPPChange interface to add the EPPChangeExt to an EPPChangeCheckCmd
 * object. This is done to prevent dependancies on external libraries. In
 * practice, it is more likely that an EPPChangeExt will be added to an
 * EPPDomain or EPPHost interface.
 * <p>
 * The configuration file used by the unit test defaults to epp.config, but can
 * be changed by passing the file path as the first command line argument. The
 * unit test can be run in multiple threads by setting the "threads" system
 * property. For example, the unit test can be run in 2 threads with the
 * configuration file ../../epp.config with the following command: <br>
 * <br>
 * java com.verisign.epp.interfaces.EPPChangeTst -Dthreads=2 ../../epp.config
 * <br>
 * <br>
 * The unit test is dependent on the use of <a
 * href=http://www.mcwestcorp.com/Junit.html>JUNIT 3.5 </a> <br>
 * <br>
 * <br>
 * <br>
 * @author $Author: jim $
 * @version $Revision: 1.2 $
 */
public class EPPChangeTst {

  /**
   * LOGIC: Randomized behavior is carefully controlled. All tests are
   * pseudorandom, but if this flag is set, the pseudorandom results are made
   * repeatable for debugging purposes.
   */
  static private final boolean DETERMINISTIC = true;

  /**
   * Handle to the Singleton EPP Application instance (
   * <code>EPPApplicationSingle</code>)
   */
  private static EPPApplicationSingle app = EPPApplicationSingle.getInstance();

  /**
   * Logging category
   */
  private static Logger cat = LoggerFactory.getLogger(EPPChangeTst.class);

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

  /**
   * JUNIT <code>suite</code> static method, which returns the tests
   * associated with <code>EPPChangeTst</code>.
   */
  @BeforeClass
  public static void beforeClass() {

    String theConfigFileName = System.getProperty("EPP.ConfigFile");

    if (theConfigFileName != null) {
      configFileName = theConfigFileName;
    }

    try {
      System.out.println(configFileName);
      app.initialize(configFileName);
    } catch (EPPCommandException e) {
      e.printStackTrace();
      Assert.fail("Error initializing the EPP Application: " + e);
    }
  }

  /**
   * Connection to the EPP Server.
   */
  private EPPClientCon connection = null;

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

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

  /**
   * EPP Change associated with test
   */
  private EPPChange change = null;

  /**
   * Handle an <code>EPPCommandException</code>, 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 response = session.getResponse();
    aException.printStackTrace();
    // Is a server specified error?
    if ((response != null) && (!response.isSuccess())) {
      Assert.fail("Server Error : " + response);
    } else {
      Assert.fail("General Error : " + aException);
    }
  }// End EPPChangeTst.handleException(EPPCommandException)

  public void changeCheck() {
    printStart("changeCheck");
    try {
      EPPChangeCheckResp response = change.sendCheck(RandomHelper.getTransId());
      System.out.println("\nchangeCheckCmd: " + change.getLastCommand());
      System.out.println("\nchangeCheckRsp: " + response);

      /**
       * Result Set
       */
      for (int i = 0; i < response.getResults().size(); i++) {
        EPPResult myResult = (EPPResult) response.getResults().elementAt(i);
        System.out.println("Result Code    : " + myResult.getCode());
        System.out.println("Result Message : " + myResult.getMessage());
        System.out.println("Result Lang    : " + myResult.getLang());
        if (myResult.isSuccess()) {
          System.out.println("Command Passed ");
        } else {
          System.out.println("Command Failed ");
        }
        if (myResult.getValues() != null) {
          for (int k = 0; k < myResult.getValues().size(); k++) {
            System.out.println("Result Values  : "
                + myResult.getValues().elementAt(k));
          }
        }
      }

    } catch (EPPCommandException e) {
      handleException(e);
    }

    printEnd("changeCheck");
  }

  public void changeInfo() {
    printStart("changeInfo");
    try {
      EPPChangeInfoResp response = change.sendInfo(RandomHelper.getTransId());
      System.out.println("\nchangeInfoCmd: " + change.getLastCommand());
      System.out.println("\nchangeInfoRsp: " + response);

      /**
       * Result Set
       */
      for (int i = 0; i < response.getResults().size(); i++) {
        EPPResult myResult = (EPPResult) response.getResults().elementAt(i);
        System.out.println("Result Code    : " + myResult.getCode());
        System.out.println("Result Message : " + myResult.getMessage());
        System.out.println("Result Lang    : " + myResult.getLang());
        if (myResult.isSuccess()) {
          System.out.println("Command Passed ");
        } else {
          System.out.println("Command Failed ");
        }
        if (myResult.getValues() != null) {
          for (int k = 0; k < myResult.getValues().size(); k++) {
            System.out.println("Result Values  : "
                + myResult.getValues().elementAt(k));
          }
        }
      }

    } catch (EPPCommandException e) {
      handleException(e);
    }

    printEnd("changeInfo");
  }

  public void changeCreate() {
    printStart("changeCreate");
    try {
      EPPResponse response = change.sendCreate(RandomHelper.getTransId(),
          RandomHelper.getLabel(), RandomHelper.getDescription());
      System.out.println("\nchangeCreateCmd: " + change.getLastCommand());
      System.out.println("\nchangeCreateRsp: " + response);

      /**
       * Result Set
       */
      for (int i = 0; i < response.getResults().size(); i++) {
        EPPResult myResult = (EPPResult) response.getResults().elementAt(i);
        System.out.println("Result Code    : " + myResult.getCode());
        System.out.println("Result Message : " + myResult.getMessage());
        System.out.println("Result Lang    : " + myResult.getLang());
        if (myResult.isSuccess()) {
          System.out.println("Command Passed ");
        } else {
          System.out.println("Command Failed ");
        }
        if (myResult.getValues() != null) {
          for (int k = 0; k < myResult.getValues().size(); k++) {
            System.out.println("Result Values  : "
                + myResult.getValues().elementAt(k));
          }
        }
      }

    } catch (EPPCommandException e) {
      handleException(e);
    }

    printEnd("changeCreate");
  }

  public void changeUpdate() {
    printStart("changeUpdate");
    try {
      EPPResponse response = null;
      if (RandomHelper.p(.5)) {
        response = change.sendUpdateAttrs(RandomHelper.getTransId(),
            RandomHelper.getPriority(), RandomHelper.getLabel(),
            RandomHelper.getDescription());
      } else if (RandomHelper.p(.5)) {
        response = change.sendUpdateClear(RandomHelper.getTransId());
      } else {
        response = change.sendUpdateSubmit(RandomHelper.getTransId());
      }
      System.out.println("\nchangeUpdateCmd: " + change.getLastCommand());
      System.out.println("\nchangeUpdateRsp: " + response);

      /**
       * Result Set
       */
      for (int i = 0; i < response.getResults().size(); i++) {
        EPPResult myResult = (EPPResult) response.getResults().elementAt(i);
        System.out.println("Result Code    : " + myResult.getCode());
        System.out.println("Result Message : " + myResult.getMessage());
        System.out.println("Result Lang    : " + myResult.getLang());
        if (myResult.isSuccess()) {
          System.out.println("Command Passed ");
        } else {
          System.out.println("Command Failed ");
        }
        if (myResult.getValues() != null) {
          for (int k = 0; k < myResult.getValues().size(); k++) {
            System.out.println("Result Values  : "
                + myResult.getValues().elementAt(k));
          }
        }
      }

    } catch (EPPCommandException e) {
      handleException(e);
    }

    printEnd("changeUpdate");
  }

  public void changeDelete() {
    printStart("changeDelete");
    try {
      EPPResponse response = change.sendDelete(RandomHelper.getTransId());
      System.out.println("\nchangeDeleteCmd: " + change.getLastCommand());
      System.out.println("\nchangeDeleteRsp: " + response);

      /**
       * Result Set
       */
      for (int i = 0; i < response.getResults().size(); i++) {
        EPPResult myResult = (EPPResult) response.getResults().elementAt(i);
        System.out.println("Result Code    : " + myResult.getCode());
        System.out.println("Result Message : " + myResult.getMessage());
        System.out.println("Result Lang    : " + myResult.getLang());
        if (myResult.isSuccess()) {
          System.out.println("Command Passed ");
        } else {
          System.out.println("Command Failed ");
        }
        if (myResult.getValues() != null) {
          for (int k = 0; k < myResult.getValues().size(); k++) {
            System.out.println("Result Values  : "
                + myResult.getValues().elementAt(k));
          }
        }
      }

    } catch (EPPCommandException e) {
      handleException(e);
    }

    printEnd("changeDelete");
  }

  /**
   * This command issues a Change Check with a Change Extension. In practice,
   * it is more likely to add an EPPChangeExt to an EPPDomain or EPPHost
   * interface. (see above)
   */
  public void changeExtension() {
    printStart("changeExtension");
    try {
      change.addExtension(new EPPChangeExt("tk421"));
      EPPResponse response = change.sendCheck(RandomHelper.getTransId());
      System.out.println("\nchangeCheckCmd: " + change.getLastCommand());
      System.out.println("\nchangeCheckRsp: " + response);

      /**
       * Result Set
       */
      for (int i = 0; i < response.getResults().size(); i++) {
        EPPResult myResult = (EPPResult) response.getResults().elementAt(i);
        System.out.println("Result Code    : " + myResult.getCode());
        System.out.println("Result Message : " + myResult.getMessage());
        System.out.println("Result Lang    : " + myResult.getLang());
        if (myResult.isSuccess()) {
          System.out.println("Command Passed ");
        } else {
          System.out.println("Command Failed ");
        }
        if (myResult.getValues() != null) {
          for (int k = 0; k < myResult.getValues().size(); k++) {
            System.out.println("Result Values  : "
                + myResult.getValues().elementAt(k));
          }
        }
      }

    } catch (EPPCommandException e) {
      handleException(e);
    }

    printEnd("changeExtension");
  }

  /**
   * JUNIT test method to implement the <code>EPPChangeTst TestCase</code>.
   * Each sub-test will be invoked in order to satisfy testing the EPPChange
   * interface.
   */
  @Test
  public void testChange() {
    int numIterations = 1;
    String iterationsStr = System.getProperty("iterations");
    if (iterationsStr != null) {
      numIterations = Integer.parseInt(iterationsStr);
    }
    for (iteration = 0; (numIterations == 0) || (iteration < numIterations); iteration++) {
      if (DETERMINISTIC) RandomHelper.reset(iteration);
      printStart("Test Suite");
      changeCheck();
      changeInfo();
      changeCreate();
      changeUpdate();
      changeDelete();
      changeExtension();
      printEnd("Test Suite");
    }
  }

  /**
   * Unit test of <code>EPPSession.endSession</code>. The session with the
   * EPP Server will be terminated.
   */
  private void endSession() {
    printStart("endSession");
    session.setTransId("ABC-12345-XYZ");
    // End the session
    try {
      session.endSession();
    } catch (EPPCommandException e) {
      EPPResponse response = 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");
  }// End EPPChangeTst.endSession()

  /**
   * Unit test of <code>EPPSession.initSession</code>. The session
   * attribute is initialized with the attributes defined in the EPP sample
   * files.
   */
  private void initSession() {
    printStart("initSession");
    // Set attributes for initSession
    session.setTransId("ABC-12345-XYZ");
    session.setVersion("1.0");
    session.setLang("en");

    // Initialize the session
    try {
      session.initSession();
    } catch (EPPCommandException e) {
      EPPResponse response = 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");
  }// End EPPChangeTst.initSession()

  /**
   * Print the end of a test with the <code>Thread</code> name if the
   * current thread is a <code>TestThread</code>.
   * @param Logical name for the test
   */
  private void printEnd(String aTest) {
    System.out
        .println("****************************************************************");
    if (Thread.currentThread() instanceof TestThread) {
      System.out.print(Thread.currentThread().getName() + ", iteration "
          + iteration + ": ");
      cat.info(Thread.currentThread().getName() + ", iteration " + iteration
          + ": " + aTest + " End");
    }
    System.out.println("End of " + aTest);
    System.out.println("\n");
  }// End EPPChangeTst.testEnd(String)

  /**
   * Print error message
   * @param aMsg errpr message to print
   */
  private void printError(String aMsg) {
    if (Thread.currentThread() instanceof TestThread) {
      System.err.println(Thread.currentThread().getName() + ", iteration "
          + iteration + ": " + aMsg);
      cat.error(Thread.currentThread().getName() + ", iteration " + iteration
          + ": " + aMsg);
    } else {
      System.err.println(aMsg);
      cat.error(aMsg);
    }
  }// End EPPChangeTst.printError(String)

  /**
   * Print message
   * @param aMsg message to print
   */
  private void printMsg(String aMsg) {
    if (Thread.currentThread() instanceof TestThread) {
      System.out.println(Thread.currentThread().getName() + ", iteration "
          + iteration + ": " + aMsg);
      cat.info(Thread.currentThread().getName() + ", iteration " + iteration
          + ": " + aMsg);
    } else {
      System.out.println(aMsg);
      cat.info(aMsg);
    }
  }// End EPPChangeTst.printMsg(String)

  /**
   * Print the start of a test with the <code>Thread</code> name if the
   * current thread is a <code>TestThread</code>.
   * @param Logical name for the test
   */
  private void printStart(String aTest) {
    if (Thread.currentThread() instanceof TestThread) {
      System.out.print(Thread.currentThread().getName() + ", iteration "
          + iteration + ": ");
      cat.info(Thread.currentThread().getName() + ", iteration " + iteration
          + ": " + aTest + " Start");
    }
    System.out.println("Start of " + aTest);
    System.out
        .println("****************************************************************\n");
  }// End EPPChangeTst.testStart(String)

  /**
   * JUNIT <code>setUp</code> method, which sets the default client Id to
   * "theRegistrar".
   */
  @Before
  public 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");
          }

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

      session.setClientID(Environment.getProperty("EPP.Test.clientId", "ClientX"));
      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");
    change = new EPPChange(session);
  }// End EPPChangeTst.setUp();

  /**
   * JUNIT <code>tearDown</code>, which currently does nothing.
   */
  @After
  public void tearDown() {
    endSession();
  }// End EPPChangeTst.tearDown();
}// End class EPPChangeTst
