package in.ac.iitb.cse.autoperf;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.w3c.dom.Element;

/**
 * Handles probabilistic session generation
 */
// @SuppressWarnings({"rawtypes", "unchecked", "unused"})
public class ProbSessGen { // extends Thread {

  static Logger cat = LogManager.getLogger(ProbSessGen.class.getName());
  static private ProbSessGen _instance = null;
  static private Vector<Transaction> transactionVector = null;
  static private double[][] matrix = null;
  private int n = -1; // number of states in the transition matrix
  Profile profile = null;

  /**
   * Reads the XML file which is given as input to the tool
   */
  private ProbSessGen(Element notUsedButDoNotRemove) {

    try {
      transactionVector = AutoPerf.getInstance().getTransactions();

      n = transactionVector.size() + 2; // 2 more for entry and exit
      matrix = new double[n][n];
      BufferedReader inputFileReader =
          new BufferedReader(new FileReader(AutoPerf.getInstance().getcbmgFile()));
      /**
       * Read and neglect the first line.. it is just to add more readability
       */
      String line = inputFileReader.readLine();
      int i = 0;
      boolean firstTime = true;
      /**
       * Now here onwards read each line. The first token will be a transaction name (followed by a
       * space) and then onwards the probability of transition to another transactions as a sequence
       * of floats separated by spaces.
       */
      while ((line = inputFileReader.readLine()) != null) {
        line = line.trim();
        /**
         * This means that we can separate the values in the cbmg file by more than one space.
         */
        String[] columns = line.split("\\s+");
        cat.debug(line);
        if (firstTime) {
          firstTime = false;
        }
        /**
         * This would give the name of the transaction. First word on each line; just neglect it. It
         * is just for the user for readability
         */
        String transactionName = columns[0];
        cat.debug("Parsed PSG File for Transaction Name : " + transactionName);
        cat.debug("transaction size is " + transactionVector.size());
        if (transactionName.equalsIgnoreCase(("entry"))) {
          i = 0;
        } else if (transactionName.equalsIgnoreCase(("exit"))) {
          i = n - 1;
        } else {

          for (i = 0; i < transactionVector.size(); i++) {
            if (transactionVector.get(i).getTransactionName().equalsIgnoreCase(transactionName)) {
              i = i + 1;
              break;
            }
          }
        }
        int j = 0, k = 1;
        while (k < columns.length) {
          String tt = columns[k].trim();
          cat.debug(tt);
          matrix[i][j] = Double.parseDouble(tt);
          j++;
          k++;
        }
      }
      inputFileReader.close();
    } catch (IOException ioe) {
      cat.warn("File \"" + AutoPerf.getInstance().getcbmgFile() + "\" not found. Breaking!");
      System.exit(0);
      // }catch(Exception ex) {
      // cat.error(ex.getMessage());
      // Helper.logStackTrace(cat, ex);
    }

    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        cat.debug(matrix[i][j] + "  ");
      }
      cat.debug("");
    }
    cat.debug("");
  }

  public ProbSessGen() {}

  static public void setInstance() {
    if (_instance != null)
      return;
    _instance = new ProbSessGen(null);
  }

  // public void run() {
  // int currentState = 0; //to keep track of current state
  // int exitState = n-1;
  // StatePredictor statePredictor = new StatePredictor(matrix,n);
  //
  // cat.debug("beginning execution");
  // String cookie = null;
  //
  // cat.debug("Exit State : "+exitState);
  // while(true) {
  // currentState = statePredictor.getNextState(currentState);
  // if(currentState >= exitState)
  // break;
  // Transaction t =transactionVector.get(currentState-1);
  // int size = t.getUrls().size();
  // try {
  // for(int i = 0; i < size; i++) {
  // Url url =((Url)t.getUrls().get(i));
  // cat.debug("Am in State : "+currentState+" "+"Connecting to Url --> \""+url.getUrlName() +"\"");
  // System.exit(1);
  // cookie = url.makeConnection(cookie,null,null,/*PropertyFile.getInstance().getTimeOut()*/-1,-1);
  // }
  // }
  // catch(Exception e) {
  // cat.error(e.getMessage());
  // Helper.logStackTrace(cat, e);
  // }
  // }
  // }

  /**
   * 
   * @param ifManualInputMode
   * @return
   * @throws InterruptedException
   */
  public Vector<Vector<Result>> generateLoad(boolean ifManualInputMode)
      throws InterruptedException {
    profile = new Profile();
    Vector<Vector<Result>> ResultVector = new Vector<Vector<Result>>();
    if (!ifManualInputMode) {
      ResultVector = profile.runProfiling(AutoPerf.getInstance().getProfiler(), transactionVector,
          AutoPerf.getInstance().getMaxUsers(), false, -1,
          AutoPerf.getInstance().getminUsers()/* start load level in case of PSG */);
    } else
      ResultVector = profile.runProfiling(AutoPerf.getInstance().getProfiler(), transactionVector,
          AutoPerf.getInstance().getMaxUsers(), false, -2,
          -2/* start load level in case of manual PSG */);
    // Vector<Vector<Vector<Result>>> farmResultVector = new Vector<Vector<Vector<Result>>>();
    // farmResultVector.add(ResultVector);

    Vector<Vector<Result>> autoProfResultVector = new Vector<Vector<Result>>();
    autoProfResultVector = ResultVector;
    return autoProfResultVector;
  }

  static public ProbSessGen getInstance() {
    return _instance;
  }

  public Profile getProfile() {
    return profile;
  }

  public int getN() {
    return n;
  }

  public double[][] getMatrix() {
    return matrix;
  }

  static public Vector<Transaction> getTransactionVector() {
    return transactionVector;
  }
}
