//#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lfsr.h"
#include "tcl.h"

void Ncnt_Create(int length, int *width, t_POLY *polynom, unsigned long *start){
  printf("NCNT counter:\n");
  printf("\tLENGTH: %d\n", length);

  // Find width of LFSR counter after it
  *width = ceil(log(length + 1)/log(2));
  
  printf("\tLFSR_WIDTH: %2d\n", *width);

  if(*width > sizeof(unsigned int)*8){
    printf("Can't work with width of %d\n", *width);
    exit(1);
  }

  unsigned long stop;
  // If LFSR part is neccessery
  if(*width != 0) {

    //Get primitive polynoms
    *polynom = LFSR_GetPrimPoly(*width);;
    printf("\tLFSR_POLYNOM:");
    LFSR_PrintPoly(*polynom);
    putchar('\n');

    //Reverse polynom
    //Now it's not needed since we have LFSR_Prev  function
   
    // Stop will be (others => '1')
    stop = 0;
    for(int i = *width; i > 0; i--){
      stop = stop << 1 | 1;
    }
    printf("\tLFSR_STOP_STATE: %lx\n", stop);
    *start = stop;

    // Find start state
    while(length--){
      *start = LFSR_Prev(*polynom, *start);
      printf("\t%d: %lx\n", length, *start);
    }
    printf("\tLFSR_START_STATE: %lx\n", *start);
  }
}

static int Ncnt_Tcl(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj * CONST * objv){
  if(objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "usage: ncnt create [steps]");
    return TCL_ERROR;
  } 
  if(strcmp(Tcl_GetStringFromObj(objv[1], NULL), "create")){
    Tcl_SetStringResult(interp, "Unknown subcommand ", Tcl_GetStringFromObj(objv[1], NULL));
    return TCL_ERROR;
  }

  int steps;
  TCL_CATCH(Tcl_GetIntFromObj(interp, objv[2], &steps), "Usage: ncnt create [steps as integer]");

  Tcl_Obj* result = Tcl_NewDictObj();
  int width; 
  t_POLY polynom;
  unsigned long start;

  Ncnt_Create(steps, &width, &polynom, &start);

  TCL_CATCH(Tcl_DictObjPut(interp, result, Tcl_NewStringObj("length", -1), Tcl_NewIntObj(steps)),
            "Addition of Ncnt.length to dictionary failed");
  TCL_CATCH(Tcl_DictObjPut(interp, result, Tcl_NewStringObj("width", -1), Tcl_NewIntObj(width)),
            "Addition of Ncnt.width to dictionary failed");
  TCL_CATCH(Tcl_DictObjPut(interp, result, Tcl_NewStringObj("polynom", -1), Tcl_NewLongObj(polynom)),
            "Addition of Ncnt.polynom to dictionary failed");
  TCL_CATCH(Tcl_DictObjPut(interp, result, Tcl_NewStringObj("start", -1), Tcl_NewLongObj(start)),
            "Addition of Ncnt.start to dictionary failed");
  TCL_CATCH(Tcl_DictObjPut(interp, result, Tcl_NewStringObj("stop", -1), Tcl_NewLongObj((1UL<<width) - 1)),
            "Addition of Ncnt.stop to dictionary failed");
 
  Tcl_SetObjResult(interp, result); 
  return TCL_OK;
}

void Ncnt_TclRegister(Tcl_Interp* tcl) {
  TCL_FUNCTION("ncnt", Ncnt_Tcl);
}
