package test.sim.scenario;

import brn.sim.AbstractDriver;
import brn.sim.AbstractParams;
import brn.sim.handler.TimeBarRadioChannelHandler;
import brn.sim.handler.LinkQualityHandler;
import brn.sim.builder.*;
import brn.sim.scenario.mchannel.McExORParams;
import brn.analysis.mcexor.McExOREval;
import brn.analysis.mcexor.TestConstants;
import brn.analysis.mcexor.ImgCandidateSet;
import brn.swans.app.UdpApplication;
import test.sim.handler.McExORTestEventHandler;

import java.util.List;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.File;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.PreparedStatement;

import jist.runtime.JistAPI;
import jist.swans.Node;
import jist.swans.Constants;
import jist.swans.trans.AbstractTrans;

/**
 *
 */
public abstract class AbstractEvalMcExOR extends AbstractDriver {

  protected McExOREval eval;
  protected String testRunParamAsString;
  protected double exponent, stdDeviation;
  protected long coherenceTime;
  protected int seed, fading;
  protected int constantDataBitRate, constantCtrlBitRate;

  protected abstract String getFileName();

  protected void installHandlers(AbstractParams options) throws Exception {
    super.installHandlers(options);

    McExORParams opts = (McExORParams)options;
    if (opts.handlerRadioTimeBar) {
      int chNumber = ((FieldBuilder.FieldParams)opts.field).channelNumber;
      if (chNumber > 1) {
        TimeBarRadioChannelHandler chhandler = new TimeBarRadioChannelHandler();
        chhandler.registerHandlers();
        dataManager.add(chhandler);
      }
    }

    Object linkQualityObj = dataManager.getContributor(LinkQualityHandler.ID);

    if (linkQualityObj != null) {

      double sampleLen = -1;

      Builder.Params routeParams = ((RouteBuilder.MCExORParams)opts.node.route).metric;

      if (routeParams instanceof MetricBuilder.EtxParams) {
        sampleLen = ((MetricBuilder.EtxParams)(routeParams)).tau;
      } else if (routeParams instanceof MetricBuilder.EttParams) {
        sampleLen = ((MetricBuilder.EttParams)(routeParams)).tau;
      }

      ((LinkQualityHandler)(linkQualityObj)).setSampleLen(/*to sec*/ sampleLen / 1000);
    }

    McExORTestEventHandler jeh = new McExORTestEventHandler();
    jeh.registerHandlers();

  }

  public static int getFadingParameter(FadingBuilder.FadingParams params) {
    if (params instanceof FadingBuilder.NoneParams)
      return Constants.FADING_NONE;
    else if (params instanceof FadingBuilder.RayleighParams)
      return Constants.FADING_RAYLEIGH;
    else if (params instanceof FadingBuilder.RicianParams)
      return Constants.FADING_RICIAN;
    
    return -1;
  }
  
  /**
   * Called after successful hookup. Overwrite if you want to do some
   * custom initialization.
   *
   * @param opts
   * @throws brn.sim.builder.BuilderException
   */
  protected void postHookUp(AbstractParams opts) throws BuilderException {

    McExORParams params = (McExORParams)opts;

    seed = params.seed;
    exponent = ((PathLossBuilder.DistShadowingParams)
        ((FieldBuilder.FieldParams)params.field).pathloss).exponent;
    stdDeviation = ((PathLossBuilder.DistShadowingParams)
        ((FieldBuilder.FieldParams)params.field).pathloss).stdDeviation;
    coherenceTime = ((PathLossBuilder.DistShadowingParams)
        ((FieldBuilder.FieldParams)params.field).pathloss).coherenceTime;
    fading = getFadingParameter(((FieldBuilder.FieldParams)params.field).fading);

    constantDataBitRate = ((MacBuilder.MCExORParams)params.node.mac).constantDataBitRate;
    constantCtrlBitRate = ((MacBuilder.MCExORParams)params.node.mac).constantCtrlBitRate;

    eval = new McExOREval(nodes, params.appParams.serverAddr.getId());
    eval.registerHandlers();

    testRunParamAsString = setTestRunParamString(((RouteBuilder.MCExORParams)params.node.route).testRunOpts);

    JistAPI.runAt(new Runnable() {
      public void run() {
        try {

          int sendData  = eval.getSendData().size();
          int retryData = eval.getRetryPackets().size();
          int notDetectedDuplicates = eval.getNotDetectedDuplicates().size();
          long cumDist  = eval.getCumulatedDistances();

          // write the stuff in DB
          Connection conn = null;

          try {
             String userName = "userdb";
             String password = "userdb";
             String url = "jdbc:mysql://localhost:3306/sim";
             Class.forName ("com.mysql.jdbc.Driver").newInstance ();
             conn = DriverManager.getConnection (url, userName, password);
             System.out.println ("Database connection established");

             PreparedStatement s = conn.prepareStatement (
                 "INSERT INTO EVAL_MCEXOR (seed, spec, exponent, stdDeviation, coherenceTime, fading, drate, crate,"
                 + " send, retry, dupl, dist) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)");

             int idx = 1;

             s.setInt(idx++,    seed);
             s.setString(idx++, testRunParamAsString);
             s.setDouble(idx++, exponent);
             s.setDouble(idx++, stdDeviation);
             s.setLong(idx++,   coherenceTime);
             s.setInt(idx++,    fading);
             s.setInt(idx++,    constantDataBitRate / Constants.BANDWIDTH_1Mbps);
             s.setInt(idx++,    constantCtrlBitRate / Constants.BANDWIDTH_1Mbps);
             s.setInt(idx++,    sendData);
             s.setInt(idx++,    retryData);
             s.setInt(idx++,    notDetectedDuplicates);
             s.setLong(idx++,   cumDist);

             s.executeUpdate ();

             s.close ();
          } catch (Exception e) {
           e.printStackTrace();
          } finally {
             if (conn != null) {
                 try {
                     conn.close ();
                     System.out.println ("Database connection terminated");
                 } catch (Exception e) { /* ignore close errors */ }
             }
          }

          File f = new File(getFileName());
          FileWriter fw = new FileWriter(f, true);

          StringBuffer content = new StringBuffer();
          content.append(testRunParamAsString).append("\t");
          content.append(sendData).append("\t");
          content.append(retryData).append("\t");
          content.append(notDetectedDuplicates).append("\t");
          content.append(cumDist).append("\t");
          content.append("\n");

          fw.write(content.toString());
          fw.flush();
          fw.close();

          System.out.println("seed                = " + seed);
          System.out.println("spec                = " + testRunParamAsString);
          System.out.println("data rate           = " + constantDataBitRate);
          System.out.println("ctrl rate           = " + constantCtrlBitRate);
          System.out.println("sendPackets.size    = " + sendData);
          System.out.println("retryPackets.size   = " + retryData);
          System.out.println("duplPackets.size    = " + notDetectedDuplicates);
          System.out.println("mean distance gain  = " + cumDist);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }, JistAPI.END);
  }

  protected String setTestRunParamString(Hashtable opts) {
    String var = "";
    Enumeration keys = opts.keys();
    while (keys.hasMoreElements()) {
      Integer nodeId = (Integer)keys.nextElement();
      List testRunOpts = Arrays.asList((Object[])opts.get(nodeId));

      var = var + nodeId + "-";
      if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_SNGL_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_SNGL_KEY) + 1;
        var += ImgCandidateSet.getSingleCsAsString((Integer)testRunOpts.get(idx));
      } else if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_TWIN_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_TWIN_KEY) + 1;
        var += ImgCandidateSet.getTwinCsAsString((Integer)testRunOpts.get(idx));
      } else if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_TRPL_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_TRPL_KEY) + 1;
        var += ImgCandidateSet.getTripleCsAsString((Integer)testRunOpts.get(idx));
      } else if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_QUAD_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_QUAD_KEY) + 1;
        var += ImgCandidateSet.getQuadCsAsString((Integer)testRunOpts.get(idx));
      }
      var += ",";
    }
    return var;
  }

  protected String setTestRtsRunParamString(Hashtable opts) {
    String var = "";
    Enumeration keys = opts.keys();
    while (keys.hasMoreElements()) {
      Integer nodeId = (Integer)keys.nextElement();
      List testRunOpts = Arrays.asList((Object[])opts.get(nodeId));

      var = var + nodeId + "-";
      if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_RTS_SNGL_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_RTS_SNGL_KEY) + 1;
        var += ImgCandidateSet.getSingleCsAsString((Integer)testRunOpts.get(idx));
      } else if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_RTS_TWIN_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_RTS_TWIN_KEY) + 1;
        var += ImgCandidateSet.getTwinCsAsString((Integer)testRunOpts.get(idx));
      } else if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_RTS_TRPL_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_RTS_TRPL_KEY) + 1;
        var += ImgCandidateSet.getTripleCsAsString((Integer)testRunOpts.get(idx));
      } else if (testRunOpts.contains(TestConstants.IMG_CANDIDATE_SET_RTS_QUAD_KEY)) {
        int idx = testRunOpts.indexOf(TestConstants.IMG_CANDIDATE_SET_RTS_QUAD_KEY) + 1;
        var += ImgCandidateSet.getQuadCsAsString((Integer)testRunOpts.get(idx));
      }
      var += ",";
    }
    return var;
  }

  protected void setupApplication(AbstractParams opts, int nodeId, Node node) throws BuilderException {
    super.setupApplication(opts, nodeId, node);

    McExORParams params = (McExORParams)opts;

    { // transport
      Builder builder = builderProvider.getBuilder(params.udpParams);
      AbstractTrans trans = (AbstractTrans) builder.build(params.udpParams, node);
      builderProvider.addHookUp(builder, params.udpParams, node, trans);
      node.addTransport(trans);
    }

    { // app
      Builder builder = builderProvider.getBuilder(params.appParams);
      UdpApplication app = (UdpApplication) builder.build(params.appParams, node);
      if (null != app) {
        builderProvider.addHookUp(builder, params.appParams, node, app);
        node.addApplication(app);
      }
    }
  }
}
