#include <iostream>
#include <iomanip>
#include "Vbit_repr_vector.h"
#include <verilated.h>
#include <verilated_vcd_c.h>

// ciphertext
int c[] = { 0x1, 0x2, 0x3, 0x4,   // 0-3
			     0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8}; // 4-7

// bit-repr_ciphertext
int c_star[] = { 0x0, 0x0, 0x0, 0x0,      // 0-3
                 0x1, 0x0, 0x0, 0x0,                // 4-7
                 0x1, 0x0, 0x0, 0x0,                // 8-11
                 0x0, 0x1, 0x1, 0x0,                // 12-15
                 0x0, 0x1, 0x0, 0x0,                // 16-19
                 0x0, 0x0, 0x0, 0x0,                // 20-23
                 0xffffffff, 0x0, 0x0, 0x0,         // 24-27
                 0xffffffff, 0x0, 0x0, 0x0,         // 28-31
                 0xffffffff, 0x0, 0x0, 0x0,         // 32-35
                 0xffffffff, 0x0, 0x0, 0x0};        // 36-39
			     

int main(int argc, const char ** argv, const char ** env) {
  Verilated::commandArgs(argc, argv);

  // Treat the argument on the command-line as the place to start
  int width, ell;
  if (argc > 1 && argv[1][0] != '+') {
    width = atoi(argv[1]);
    ell = atoi(argv[2]);
  } else {
    width = 8; // Default
    ell = 5;  // Default
  }

  Vbit_repr_vector * dut = new Vbit_repr_vector;  // Instantiate a new module
  
  // Enable dumping a VCD file
  Verilated::traceEverOn(true);
  VerilatedVcdC * tfp = new VerilatedVcdC;
  dut->trace(tfp, 99);
  tfp->open("bit_repr_vector.vcd");

  dut->clk = 0;
  dut->width = width;
  dut->start = 0;
  dut->ell = ell;
  
  int time;
  std::cout << "width: " << width;
  std::cout << std::endl;
  std::cout << "ell: " << ell;
  std::cout << std::endl;
  int i = 0;
  int j = 0;
  int err = 0;
  int start_stamp = 100;
  // let run (width * ell) cycles for computations
  // extra 11 cycles also needed
  int read_stamp = start_stamp + width * ell * 20 + 11 * 20;
  for (time = 0 ; time < 10000 ; time += 10) {
    dut->clk = ((time % 20) >= 10) ? 1 : 0; // Simulate a 50 MHz clock
    if (time == 20) dut->reset = 1; // Handle "reset" on for four cycles
    if (time == 100) {
      dut->reset = 0;
      dut->start = 1;   // Put "start" on
    }

    // taking inputs for (width) cycles
    if (time >= start_stamp && time <= start_stamp + width * 20 && dut->clk == 1) {
        dut->c_i = c[i];
        std::cout << i << "-th input received: " << (int) c[i];
        std::cout << std::endl;
        i += 1;
    }

    if (time == start_stamp + width * 20) dut->start = 0;

    dut->eval();     // Run the simulation for a cycle
    tfp->dump(time); // Write the VCD file for this cycle

    // compare outputs for (width * ell) cycles
    if (time >= read_stamp && time <= read_stamp + width * ell * 20 && dut->clk == 1) {
        std::cout << ' ' <<  std::setfill('0') << std::setw(0) << (int) dut->data_out;
        if (dut->data_out == c_star[j])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c_star[j];
            err += 1;
        }
        std::cout << std::endl;
        j += 1;
    }

    if (dut->done) break;
  }

  tfp->close(); // Stop dumping the VCD file
  delete tfp;

  dut->final(); // Stop the simulation
  delete dut;

  return 0;
}