#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_timer.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <unistd.h>
#include <iostream>
#include <chrono>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <fftw3.h>
#include "scope.h"

spectrum_buffer_manager_class::spectrum_buffer_manager_class()
  {
  my_formatted_spectrum_buffer = NULL;
  }

spectrum_buffer_manager_class::~spectrum_buffer_manager_class()
  {
  if (my_formatted_spectrum_buffer != NULL)
    {
    free(my_formatted_spectrum_buffer);
    }
  }

int spectrum_buffer_manager_class::initialize_spectrum(int horizontal_size,
  int vertical_size, Sint16 *main_buffer)
  {
  // This is the formatted buffer that will be fed to the display 
  my_formatted_spectrum_buffer = (int *)malloc ((sizeof(int) * AUDIO_BUFFER_SIZE));
  if (my_formatted_spectrum_buffer == NULL)
    {
    printf ("cannot allocate formatted spectrum spectrum buffer\n");
    perror ("error is");
    return 1;
    }

  //Allocate output fftw buffer
  if ((output_fftw = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) 
    * FFT_BUFFER_SIZE * 2)) == NULL)
    {
    printf ("cannot allocate fftw output buffer\n");
    perror ("error is");
    return 1;
    }

  //Allocate input fftw buffer
  if ((input_fftw = (double*) fftw_malloc(sizeof(double) * FFT_BUFFER_SIZE)) == NULL)
    {
    printf ("cannot allocate fftw input buffer\n");
    perror ("error is");
    return 1;
    }

  // Now lets attempt to perform an fftw plan
  if ((my_fftw_plan = fftw_plan_dft_r2c_1d(FFT_BUFFER_SIZE, input_fftw,
    output_fftw, FFTW_MEASURE)) == NULL) 
    {
    printf ("cannot fftw plan");
    perror ("error is");
    return 1;
    }

  // my_read_spectrum_index is which chunk we will put the read buffer into
  my_read_spectrum_index = 0;

  // index to read the individual elements into the fftw input buffer
  read_buffer_counter = 0;

  // my_output_fft_buffer_index is used to flip output buffer
  // members all to negative for display purposes 

  my_output_fft_buffer_index = 0;

  my_spectrum_horizontal_size = horizontal_size;
  my_spectrum_vertical_size = vertical_size; 
 
  // The main buffer is the read buffer from main buffer read
  my_main_buffer = main_buffer;
  return 0;
  }

// Returns address for formatted fft buffer for ready to display
// Return NULL for waiting for buffer fill
int *spectrum_buffer_manager_class::process_spectrum()
  {
  // Fill the fftw input array
  // Fill in section corresponging to the current read_buffer_counter
  input_fftw_write_position = input_fftw + my_read_spectrum_index;
  for (read_buffer_counter = 0; read_buffer_counter < READ_BUFFER_SIZE;
   read_buffer_counter += 1)
    {
    main_buffer_hold =  (Sint16)(*(my_main_buffer + read_buffer_counter) * SPECTRUM_VOLUME);
    *(input_fftw_write_position + read_buffer_counter) = (double)main_buffer_hold;
    }

  /* prep for next time */
  my_read_spectrum_index += 1;
  // Are we still working on filling this buffer?
  if (my_read_spectrum_index < FFT_NUMBER_READ_BUFFERS)
    {
    // Lets wait 
    return NULL; 
    }
  else
    // Done filling up fft buffer - lets process
    {
    // First, reset the spectrum index to 0
    my_read_spectrum_index = 0;
    
    // Perform the fft
    fftw_execute(my_fftw_plan);

    // Now grab the real part of the complex output
    for (my_output_fft_buffer_index = 0;
     my_output_fft_buffer_index < my_spectrum_horizontal_size;
     my_output_fft_buffer_index += 1)
      {
      *(my_formatted_spectrum_buffer + my_output_fft_buffer_index)
       = (int)(output_fftw[(FFT_START_BUCKET + 
       (my_output_fft_buffer_index))][0] * SPECTRUM_VOLUME);
      if (*(my_formatted_spectrum_buffer + my_output_fft_buffer_index) > 0 )
        {
        // Flip any positive values to negative because negative causes location
        // to go up on the sdl coordinate system
        *(my_formatted_spectrum_buffer + my_output_fft_buffer_index) =
        - *(my_formatted_spectrum_buffer + my_output_fft_buffer_index);
        }
      }
    }

  return my_formatted_spectrum_buffer;
  }
    
