package brn.swans.radio.biterrormodels;

import java.io.ByteArrayInputStream;
import java.util.Formatter;
import java.util.Random;

import jist.swans.Constants;
import jist.swans.misc.Util;
import jist.swans.radio.RadioFactory;
import jist.swans.radio.RadioInfo;
import brn.swans.radio.TransmissionMode;
import brn.swans.radio.RadioNoiseAdditiveBER.ChunkErrorRates;

/**
 * Abstract base class for bit error allocation.
 */
public abstract class BitErrorModel {

  /**
   * Process a chunk of bit, allocate errors.
   *
   * @param rates
   * @param bitPosition
   * @param current
   * @param previous
   * @param snr
   * @return
   */
  public int treatChunk(ChunkErrorRates rates, int bitPosition, long current, long previous, double snr) {
    TransmissionMode mode = rates.getMode();
    int dataBits = mode.getNumDataBits(current - previous);

    double csr = mode.getCachedSuccessRate(snr, dataBits);
    BitErrorMask chunkErrorMask = createErrors(dataBits, snr, mode);

    rates.updateChunk(bitPosition, csr, chunkErrorMask);

    return dataBits;
  }

  /**
   *
   * @param length
   * @param snr
   * @param mode
   * @return
   */
  protected abstract BitErrorMask createErrors(int length, double snr, TransmissionMode mode);

  /**
   *
   * @param rates
   */
  public void postProcess(ChunkErrorRates rates) {}


  public static void main(String[] args) {
    BitErrorModel[] bems = new BitErrorModel[] {
        new None(),
        new UniformDistribution(),
        new Accumulated(),
    };
    int nobems = bems.length;

    String radioDesc = "mac=802.11g \n"
      + "bitrates=6,9,12,18,24,36,48,54 \n"
      + "txpowers=19,19,19,19,19,18,16,15 \n"
      // from http://nova.stanford.edu/~bbaas/ps/isscc2002_fig5.pdf
      + "thresholds=5.4,5.8,7.0,9.5,11.3,14.9,18.6,20.6 \n"
      // + "thresholds=1.,3.5,3.8,6.5,8.8,12.3,16.8,19.0 \n" // theoretical
      + "basicRates=6,12,24 \n"
      + "frequencies=2400 \n"
      + "frequency_spacings=5 \n"
      + "frequency_widths=20 \n"
      + "number_of_channels=1 \n";
    RadioInfo radioInfo = RadioFactory.createRadioInfo(Constants.MAC_802_11g_PURE,
        new ByteArrayInputStream(radioDesc.getBytes()));

    // Adjust curves to theoretical limit (http://nova.stanford.edu/~bbaas/ps/isscc2002_fig5.pdf)
    TransmissionMode.setReceiverLoss(1000, .1, radioInfo.getBitrateSnrThresholds());


    int nobytes = 2000;
    int iterations = 100;
    double[] snrStart = new double[] {
        5.4,
        5.8,
        7.0,
        9.5,
        11.3,
        14.9,
        18.6,
        20.6
    };

    Constants.random = new Random(2);
    int[] bitrates = new int[] {
        Constants.BANDWIDTH_6Mbps,
        Constants.BANDWIDTH_9Mbps,
        Constants.BANDWIDTH_12Mbps,
        Constants.BANDWIDTH_18Mbps,
        Constants.BANDWIDTH_24Mbps,
        Constants.BANDWIDTH_36Mbps,
        Constants.BANDWIDTH_48Mbps,
        Constants.BANDWIDTH_54Mbps,
    };

    for (int b = 0; b < bitrates.length; b++) {
      TransmissionMode mode = TransmissionMode.get80211gMode(
          (int)Constants.CHANNEL_WIDTH_80211a, bitrates[b]);
      long current = nobytes * 8 * Constants.SECOND / mode.getDataRate();

      int[] accumerros = new int[nobems+1];
      for (double snr_dB = snrStart[b]-2; snr_dB < 30; snr_dB += .1) {
        String s = "snr="+new Formatter().format("%2.1f", snr_dB)+"dB, per: ";
        double snr = Util.fromDB(snr_dB);

        bemsloop: for (int i = 0; i < nobems; i++) {
          double per = .0;
          double corruptPakets = .0;

          for (int iter = 0; iter < iterations; iter++) {
            ChunkErrorRates rates = new ChunkErrorRates(mode, snr, 0, true);
            bems[i].treatChunk(rates, 0, current, 0, snr);
            bems[i].postProcess(rates);
            if (rates.errorsPresent())
              corruptPakets++;
            per += rates.getPer();
          }
          if (0 >= per)
            break bemsloop;
          per = per / iterations;
          accumerros[i] += corruptPakets;
          if (0 == i)
            accumerros[nobems] += per * iterations;
          s += new Formatter().format("%1.4f", per) + "("+new Formatter().format("%4.0f", corruptPakets)+")" + ", ";
          Util.assertion(Math.abs(per - corruptPakets / (double)iterations) <= .30);
        }
        //rates[i].getPer()
        System.out.println(s);
      }

      System.out.print("total errors: ");
      for (int i = 0; i < accumerros.length; i++)
        System.out.print(accumerros[i] + ", ");
      System.out.println();
      for (int i = 0; i < nobems; i++) {
        Util.assertion(Math.abs(accumerros[i] / (double)accumerros[nobems] - 1) <= .15);
      }

    }


  }

}
