/**************************************************************************************************/

#include <stdbool.h>
#include <stdlib.h>

// #include <pthread.h>
// #include <signal.h>

/**************************************************************************************************/

#include <ngspice/sharedspice.h>

/**************************************************************************************************/

static bool error_flag = false;

bool ngspice_is_not_running = true;

/**************************************************************************************************/

/* SendChar* callback function for reading printf, fprintf, fputs
 *
 * NULL allowed
 */
int
ng_send_char (char *message, int ngspice_id, void *user_data)
{
  printf (">>> ng_send_char: %s\n", message);

  return 0;
}

/**************************************************************************************************/

/* SendStat* callback function for reading status string and percent value
 *
 * NULL allowed
 */
int
ng_send_stat (char *message, int ngspice_id, void *user_data)
{
  printf (">>> ng_send_stat: %s\n", message);

  return 0;
}

/**************************************************************************************************/

// purpose ???

/* ControlledExit* callback function for transferring a flag to
 * caller, generated by ngspice upon a call to function
 * controlled_exit. May be used by caller to detach ngspice.dll, if
 * dynamically loaded or to try any other recovery method, or to
 * exit.
 *
 * Required
 */
int
ng_exit (int exit_status, bool immediate_unloding, bool quit_exit, int ngspice_id, void *user_data)
{
  printf (">>> ng_exit %i %i %i\n", exit_status, immediate_unloding, quit_exit);

  if (quit_exit)
    {
      printf ("  Returned form quit with exit status %d\n", exit_status);
      exit (exit_status);
    }

  if (immediate_unloding)
    {
      printf ("  Unloading ngspice inmmediately is not possible\n");
      printf ("  Can we recover?\n");
    }
  else
    {
      printf ("  Unloading ngspice is not possible\n");
      printf ("  Can we recover? Send 'quit' command to ngspice.\n");
      error_flag = true;
      ngSpice_Command ("quit 5");
    }

  return exit_status;
}

/**************************************************************************************************/

/* SendData* callback function for sending an array of structs
 * containing data values of all vectors in the current plot
 * (simulation output)
 *
 * NULL allowed
 */
int
ng_send_data (pvecvaluesall data, int number_of_vectors, int ngspice_id, void *user_data)
{
  printf (">>> ng_send_data %i\n", data->vecindex);

  return 0;
}

/**************************************************************************************************/

/* SendInitData* callback function for sending an array of structs
 * containing info on all vectors in the current plot (immediately
 * before simulation starts)
 *
 * NULL allowed
 */
int
ng_send_init_data (pvecinfoall data, int ngspice_id, void *user_data)
{
  printf (">>> ng_send_init_data\n");
  size_t number_of_vectors = data->veccount;
  for (size_t i = 0; i < number_of_vectors; i++)
    {
      printf ("  Vector: %s\n", data->vecs[i]->vecname);
    }

  return 0;
}

/**************************************************************************************************/

/* BGThreadRunning* callback function for sending a boolean signal
 * (true if thread is running) WRONG ???
 *
 * NULL allowed
 */
int
ng_thread_running (bool not_running, int ngspice_id, void *user_data)
{
  printf (">>> ng_thread_running %i\n", not_running);

  ngspice_is_not_running = not_running;

  if (not_running)
    printf ("  bg thread is not running\n");
  else
    printf ("  bg thread is running\n");

  return 0;
}

/**************************************************************************************************/

int
main ()
{
  size_t i = 0;
  int rc;

  // ngSpice_Init(SendChar*, SendStat*, ControlledExit*, SendData*, SendInitData*, BGThreadRunning*, void) 
  rc = ngSpice_Init (NULL, NULL, ng_exit, NULL, NULL, NULL, NULL);
  // rc = ngSpice_Init (ng_send_char, ng_send_stat, ng_exit, ng_send_data, ng_send_init_data, ng_thread_running, NULL);
  printf ("ngSpice_Init returned: %d\n", rc);

  char ** circuit_array = (char**) malloc(sizeof(char*) * 7);
  circuit_array[0] = strdup(".title rc circuit");
  circuit_array[1] = strdup("V1 1 0 1");
  circuit_array[2] = strdup("R1 1 2 1");
  circuit_array[3] = strdup("C1 2 0 1 ic=0");
  // circuit_array[4] = strdup(".tran 100u 3 uic");
  circuit_array[4] = strdup(".tran 100m 3 uic");
  circuit_array[5] = strdup(".end");
  circuit_array[6] = NULL;

  rc = ngSpice_Circ(circuit_array);

  for(size_t i = 0; i < 6; i++)
    free(circuit_array[i]);
  free(circuit_array);

  /* start the background thread and simulation with 'run' */
  rc = ngSpice_Command ("bg_run");

  usleep (10000);

  /* read current plot while simulation continues */
  char *current_plot = ngSpice_CurPlot ();
  printf ("Current plot is %s\n", current_plot);

  char **all_plots = ngSpice_AllPlots ();
  i = 0;
  while (all_plots[i])
    {
      printf ("plot[%i] %s\n", i, all_plots[i]);
      i++;
    }

  char **all_vectors = ngSpice_AllVecs (current_plot);
  i = 0;
  while (all_vectors[i])
    {
      char *vector_name = all_vectors[i];
      char name[256];
      sprintf (name, "%s.%s", current_plot, vector_name);
      pvector_info vector_info = ngGet_Vec_Info (name);

      int length = vector_info->v_length;
      printf ("vector[%i] %s actual length %i\n", i, name, length);

      i++;
    }

  /* continue the main thread until bg thread is finished */
  printf ("simulation running? : %u\n", ngSpice_running());
  for (;;)
    {
      usleep (10000);
      printf ("simulation running? : %u\n", ngSpice_running());
      // if (ngspice_is_not_running)
      if (ngSpice_running() == 0)
	break;
    }
  printf("Simulation is done\n");

  all_plots = ngSpice_AllPlots ();
  i = 0;
  while (all_plots[i])
    {
      printf ("plot[%u] %s\n", i, all_plots[i]);
      i++;
    }

  char *plot_name = "tran1";
  all_vectors = ngSpice_AllVecs (plot_name);
  i = 0;
  while (all_vectors[i])
    {
      char *vector_name = all_vectors[i];
      char name[256];
      sprintf (name, "%s.%s", plot_name, vector_name);
      pvector_info vector_info = ngGet_Vec_Info (name);

      int length = vector_info->v_length;
      // src/include/ngspice/sim.h and dvec.h
      // enum simulation_types { SV_NOTYPE, SV_TIME, SV_FREQUENCY, SV_VOLTAGE, SV_CURRENT, ...
      printf ("vector[%i] %s type %i flags %i length %i\n",
	      i,
	      name,
	      vector_info->v_type,
	      vector_info->v_flags, // VF_REAL = 1 << 0, VF_COMPLEX = 1 << 1
	      length);
      if (vector_info->v_compdata == NULL)
	{
	  printf("  real data\n");
	  for (size_t k = 0; k < length; k++)
	    {
	      printf ("  [%u] %g\n", k, vector_info->v_realdata[k]);
	    }
	}
      else
	{
	  printf("  complex data\n");
	  for (size_t k = 0; k < length; k++)
	    {
	      ngcomplex_t value = vector_info->v_compdata[k];
	      printf ("  [%u] %g + i %g", k, value.cx_real, value.cx_imag);
	    }
	}

      i++;
    }

  return 0;
}

/**************************************************************************************************/
