#include <iostream>
#include "Vviewtube.h"
#include <verilated.h>
#include <verilated_vcd_c.h>
#include <stdint.h>
#include "lodepng.h"

#define SIM_LEN 3980000
#define DISPLAY_WIDTH  640*2
#define DISPLAY_HEIGHT  480
#define BYTES_PER_PIXEL  4
#define DISPLAY_BUFF_LEN  DISPLAY_WIDTH * DISPLAY_HEIGHT * BYTES_PER_PIXEL

/*
Example 1
Encode from raw pixels to disk with a single function call
The image argument has width * height RGBA pixels or width * height * 4 bytes
*/
void encodeOneStep(const char* filename, const unsigned char* image, unsigned width, unsigned height) {
  /*Encode the image*/
  unsigned error = lodepng_encode32_file(filename, image, width, height);

  /*if there's an error, display it*/
  if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
}

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



  unsigned char display_buff[DISPLAY_BUFF_LEN];

  memset(display_buff,0,DISPLAY_BUFF_LEN);
  
  Vviewtube * dut = new Vviewtube;  // Instantiate the collatz module

  // Enable dumping a VCD file
  
  Verilated::traceEverOn(true);
  VerilatedVcdC * tfp = new VerilatedVcdC;
  dut->trace(tfp, 5);
  tfp->open("viewtube.vcd");

  // Initial values
  
  dut->reset = 1;
  dut->writedata = 0;
  dut->write = 0;

  dut->chipselect = 0;
  dut->address = 0;

  bool last_clk = true;
  uint64_t time;
  uint64_t hcount = 0;
  uint64_t vcount = 0;
  for (time = 0 ; time < SIM_LEN ; time += 1) {
    dut->clk = ((time % 2) >= 1) ? 1 : 0; // Simulate a 50 MHz clock
    
    if (time == 2) {
        dut->reset = 0;
        dut->chipselect = 0;
        dut->write = 0; 
    }


/*
 



*/
    if (time == SIM_LEN/2) {
        dut->chipselect = 1;
        dut->write = 1; // Pulse "write" for two cycles
        dut->writedata = 0x05800fff; // make I A: 0x05800fff
        dut->address = 1;
    }

    if (time == SIM_LEN/2 +2) {
        dut->chipselect = 0;
        dut->write = 0;
    }

    if (time == SIM_LEN/2+8) {
        dut->chipselect = 1;
        dut->write = 1; // Pulse "write" for two cycles
        dut->writedata = 0x0001111a; // hide train 1: 0x0001111a
        dut->address = 1;
    }

    if (time == SIM_LEN/2 +10) {
        dut->chipselect = 0;
        dut->write = 0;
    }


    if (time == SIM_LEN/2+20) {
        dut->chipselect = 1;
        dut->write = 1; // Pulse "write" for two cycles
        dut->writedata = 0x01832064; //jump train 2 to 100,100: 0x01832064
        dut->address = 2;
    }

    if (time == SIM_LEN/2 +22) {
        dut->chipselect = 0;
        dut->write = 0;
    }

    if (time == SIM_LEN/2+26) {
        dut->chipselect = 1;
        dut->write = 1; // Pulse "write" for two cycles
        dut->writedata = 0x020c8258; // slide train 3 to 600,400: 0x020c8258
        dut->address = 2;
    }

    if (time == SIM_LEN/2 +28) {
        dut->chipselect = 0;
        dut->write = 0;
    }

    dut->eval();     // Run the simulation for a cycle
    tfp->dump(time); // Write the VCD file for this cycle
    if(dut->VGA_BLANK_n && dut->clk) 
    {
	hcount++;
	if(hcount ==DISPLAY_WIDTH){
		hcount = 0;
		vcount ++;
	}
	if(vcount == DISPLAY_HEIGHT){
		vcount = 0;
	}
    }
    if(hcount >= 0 && hcount < DISPLAY_WIDTH && vcount >=0 && vcount < DISPLAY_HEIGHT)
    {
	display_buff[vcount * DISPLAY_WIDTH * BYTES_PER_PIXEL + hcount * BYTES_PER_PIXEL] = dut->VGA_R;
	display_buff[vcount * DISPLAY_WIDTH * BYTES_PER_PIXEL + hcount * BYTES_PER_PIXEL+1] = dut->VGA_G;
	display_buff[vcount * DISPLAY_WIDTH * BYTES_PER_PIXEL + hcount * BYTES_PER_PIXEL+2] = dut->VGA_B;
	display_buff[vcount * DISPLAY_WIDTH * BYTES_PER_PIXEL + hcount * BYTES_PER_PIXEL+3] = 255;

    }
  }

  std::cout << std::endl;

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

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

  encodeOneStep("display_at_end.png",display_buff, DISPLAY_WIDTH, DISPLAY_HEIGHT);


  return 0;
}

