package nxm.sys.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import nxm.sys.lib.Data;
import nxm.sys.lib.Table;

/** A collection of utility methods that are useful in testing. This class is for testing purposes
    only and is subject to change without notice.

    @author  J. Czechowski IV
    @since   NeXtMidas 2.5.5
 */
public final class TestUtil {

  private TestUtil () {}

  /** Throws a exception. */
  public static final void throwRuntimeException () {
    throw new RuntimeException("Sample RuntimeException");
  }

  /** Throws a exception from an Anon class. */
  public static final void throwAnonClassRuntimeException () {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        throwRuntimeException();
      }
    };
    r.run();
  }

  /** Throws an exception from a nested class */
  public static final void throwNestedClassRuntimeException () {
    Runnable r = new MyRunnable();
    r.run();
  }

  /** If you invoke this method, you will get an object whose <code>toString()</code> will always
      throw an exception
      @return Returns a sample RuntimeException
  */
  public static final Object exceptionInToString () {
    return new Object() {
      @Override
      public String toString () {
        throw new RuntimeException("Sample RuntimeException");
      }
    };
  }

  /** Creates an array of doubles from their String equivalents
   *  passed in as a comma separated String
   *
   *  @param commaSeparatedValues vararg containing the String representation of double values
   *  @return double array
   *  @since NeXtMidas 3.9.3
   */
  public static double[] createDoubleArray (String commaSeparatedValues) {
    String[] sValues = commaSeparatedValues.split(",");
    double[] dArray = new double[sValues.length];
    int i = 0;
    for (String value : sValues) {
      dArray[i++] = Double.valueOf(value);
    }
    return dArray;
  }

  /** Test class for throwing a runtime exception */
  public static final class MyRunnable implements Runnable {

    /** default constructor */
    public MyRunnable () {}

    @Override
    public void run() {
     throwRuntimeException();
    }
  }

  /** Check if two tables have the same values
   *  Note: compared using toString() of values
   *
   *  @param table1 first nxm.sys.lib.Table
   *  @param table2 second nxm.sys.lib.Table
   *  @return true if they have the same key/value pairs
   */
  public static boolean tablesEqual (Table table1, Table table2) {
    if (table1 == null) return table2==null;
    if (table1.getKeys().length != table2.getKeys().length) return false;
    return tableSubset(table1, table2);
  }

  /** Check if table 1 is a subset of table 2
   *  Note: compared using toString() of values
   *
   *  @param table1 first nxm.sys.lib.Table
   *  @param table2 second nxm.sys.lib.Table
   *  @return true if table 1 is a subset of table 2
   */
  public static boolean tableSubset (Table table1, Table table2) {
    if (table1 == null) return table2==null;
    if (table2 == null) return false;
    for (String t1_key : table1.getKeys()) {
      if (!table2.containsKey(t1_key)) {
        return false;
      }
      Object tab1value = table2.getKey(t1_key);
      Object tab2value = table2.getKey(t1_key);
      if ((tab1value instanceof Data) && (tab1value instanceof Data)) {
        return ((Data)tab1value).equals(((Data)tab2value), Data.EQ_ABSTOL, 0.006);
      }
      else if (!table2.getKey(t1_key).toString().equals(table1.getKey(t1_key).toString())) {
        return false;
     }
    }
    return true;
  }

  /** Get all unique permutations for a pipe-separated list
   *
   *  @param list The pipe-separated list
   *  @return The set of unique permutations as a single String
   *  @since NeXtMidas 4.1.1
   */
  public static String getPermutations (String list) {
    if (list.charAt(0) == '|') list = list.substring(1);
    if (list.endsWith("|")) list = list.substring(0,list.length() -1);
    list = list.replace('|', ',');
    String[] split = list.split(",");
    ArrayList<String> allPerms = new ArrayList<>();
    strPermutations(split.length, split, "", allPerms);
    String perms = allPerms.toString();
    perms = perms.substring(1,perms.length() -1);
    return perms;

  }

  private static void strPermutations (final int num, final String[] options,
      final String currentPermutation, ArrayList<String> result) {
    if (num == 0) {
      String[] components = currentPermutation.split("::");
      Set<String> unique = new HashSet<>(Arrays.asList(components));
      if (components.length == unique.size()) {
        result.add(currentPermutation);
      }
    }
    else {
      for (int i= 0; i < options.length;  i++) {
        String beginning = (currentPermutation.isEmpty()) ? "" : currentPermutation + "::";
        strPermutations(num - 1, options, beginning + options[i], result);
      }
    }
  }
}