// Copyright 2003 Regents of the University of California

// Astropulse is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2, or (at your option) any later
// version.

// Astropulse is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.

// You should have received a copy of the GNU General Public License along
// with Astropulse; see the file COPYING.  If not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#ifndef H_ASTROPULSE
#define H_ASTROPULSE
#define DEBUGGING_0 // Tells the client to regularly inform stdout about its progress
#define POWER_MAX 1000
#define DM_MAX 16000

// #define TOTAL_FLOPS 6.21524e+14
// #define TOTAL_FLOPS 1.1187432e+15
// Need to adjust for variable numbers of DMs
#define FLOPS_PER_DM 7.93210e+10 

#include "ctype.h"
#include "stdio.h"
#include <string>
#include "fftw3.h"
#include "boinc_api.h"
#include "filesys.h"
#include "mfile.h"
#include "db_table.h"
#include "ap_schema.h"

#include "ap_const.h"

#include "ap_time_series.h"

namespace Astropulse {

#ifndef ENUM_CODE_SEGMENT_NAME
#define ENUM_CODE_SEGMENT_NAME
enum code_segment_name {main_ffa_long, main_ffa_short, build_threshold_table_long, build_threshold_table_short, single_pulse};
#endif

};


#include "ap_state.h"
#include "gdata.h"
#include "time.h"

namespace Astropulse {
using std::string;


enum OnOff {
  on, off
};

const double frac_in_main_ffa[10] = {0.895, 0.89, 0.87, 0.85, 0.8, 
                                   // 0      1     2     3     4 
                                     0.7, 0.6, 0.4, 0.2, 0.05};
                                   // 5    6    7    8    9

const double frac_in_btt[10] = {0.1,   0.1,  0.1,  0.1,  0.1,
				   // 0      1     2     3     4
				   0.1, 0.1, 0.1, 0.1, 0.05};
				   // 5    6    7    8    9

const double frac_in_main_ffa_short = 0.2;

inline int int_log2(int input) {
   int i = 0;
   while(input >>= 1) i++;
   return i;
}

class timer {
  private:
    clock_t ticks;
    clock_t last_clock;
    clock_t start_clock;

    long seconds;
    long last_time;
    long start_seconds;

    OnOff state;

  public:
    timer();
    void start();
    void stop();
    void reset();
    clock_t get_ticks();
    clock_t get_total_ticks();
    long get_seconds();
    long get_total_seconds();
    void print_total_time();
};




struct time_series_b_t {
  enum { MAXLENGTH = 64 };
  std::vector<unsigned char> data;
  int length;

  time_series_b_t() : length(0) {
    data.reserve(MAXLENGTH);
  }
  void build(float array[], int index, int range, float n_bins);
};

enum ERROR_CODES {
  FWRITE_ERR = -1,                      // Error writing a file.
  SBTF_ERR  = -2,                       // Error from splitter_bits_to_float
  FOPEN_ERR = -3,
  PARSE_ERR = -4,
  FREAD_ERR = -5
};

class Wufile {
    std::ifstream wufile;
  public:
    void init();
    void parse_header_to_state();
    void parse_header_for_writeout();
    void read_raw_data();
    void finish();
};

void debug_loop(int level, int number, bool debug_loop_msg);
void read_signal_vector(std::vector<ap_signal> &signal_vector);
bool add_signal(std::vector<ap_signal> &signal_vector, ap_signal s);
bool is_blank(ap_signal s);
bool similar_ap_signals(ap_signal s1, ap_signal s2);
int pow_int(int a, int b);

class Outfile {
    /* MFILE is a boinc API construct that can be used to
     * write to output and state files.  It "facilitates atomic
     * checkpoint" meaning it allows output to be written as
     * a single operation (instead of separate character writes?)
     */
    MFILE outfile;
  public:
    void init();
    void finish_results();
    void finish();

    void output_result(const ap_signal &s);
    void write_header();
    void write_main(std::vector<ap_signal> signal_vector);
    void flush() {
      outfile.flush();
    }
};

class Statefile {
  public:
    bool resumed;
    Statefile() : resumed(0) {}
    void Read();
    void write();
    void finish();
};

class Foldfile {
  public:
    int Read();
    void write();
    void finish();
};

class Science {
    fftwf_complex* data;
    fftwf_complex* temp;
    fftwf_complex* chirp;
    fftwf_plan fp, fp2, ip;
    // fftwf_plan* cp;

    // for remove radar
    int fftlen_patch, fftlen_envelope;
    double * envelope;
    double * pre_envelope;
    fftwf_complex* patch;
    fftwf_complex* envelope_part;
    fftwf_plan fp_patch_inv, fp_envelope;

    class Wisdom {
      public:
        void load();
        void save();
    };
    Wisdom wisdom;
  public:
    void init();
    void mainloop();
    void finish();
  protected:
    void build_chirp_table(int dm_start);
    void convert_bits_to_float();
    void compute_forward_fft();
    void high_pass_filter();
    void dechirp(int dm, float *power, int sign);
    void coadd_in_place(float *power, int size);
    int ffa(float *data, int num_sub_buffers, bool is_long, bool write_lcgf, bool debug_msg, int pulse_limit_single, int pulse_limit_rep, double ffa_thresh_mult);
    void build_threshold_table(int*& table, bool debug_msg, bool is_long, double ffa_thresh_mult);
    int lcgf_binary_search(int min, int max, int ntc, float thresh_lcgf);
    void print_DC_strengths();
    int get_indices_to_randomize(std::vector<long>& indices, int scale);
    void randomize_indices(const std::vector<long>& indices, int num_indices, int data_chunk_now, int fft_len, state_t& state);
    void randomize_samples(int fft_len, MtRand& mtr, int debug);
    void generate_envelope(const std::vector<long>& indices, int num_indices);
    void generate_pre_envelope();
};
    
    void generate_time_noise_with_envelope(fftwf_complex * noise, double * envelope, int fftlen_noise, int fftlen_envelope, fftwf_plan fp_noise_inv, MtRand &mtr);
    void one_bit(fftwf_complex * noise, int fftlen_noise);
    double gasdev(MtRand &mtr); 

class Client {
  public:
    workunit_header wuheader;
    const char* status;         // Spot to put program status for display.
    // NOTE: any data assigned to `status' must be static data -- if this
    // constraint is not acceptable, change it back to `string' instead
    // of `const char*'.

    Outfile   outfile;
    Wufile    wufile;
    Foldfile  foldfile;
    Statefile statefile;
    AP_SHMEM *ap_shmem;

    Science science;

    std::vector<ap_signal> signal_vector;

    std::vector<unsigned char> rawdata;
    std::vector<float> power;
    std::vector<float> fold_buf_long_pos;
    std::vector<float> fold_buf_short_pos;
    std::vector<float> fold_buf_long_neg;
    std::vector<float> fold_buf_short_neg;

    void init();
    void finish();

    inline void do_checkpoint() {
      // outfile.flush();
      // FILE *randsampfile = fopen("randsamp.txt", "a");
      // fprintf(randsampfile, "doing checkpoint\n");
      // fclose(randsampfile);
      outfile.write_main(signal_vector);
      foldfile.write();
      statefile.write();
    };
    inline void checkpoint() {
      if (boinc_time_to_checkpoint()) {
	do_checkpoint();
	boinc_checkpoint_completed();
      }
    };
};

extern Client client;
extern timer main_timer;
};

#ifndef INFINITY
#define INFINITY (-log(0.0))
#endif

#endif


