/* LaHaShem HaAretz U'Mloah */
/* Copyright 2013, 2014, and 2015 Purdue University. All rights reserved. */

#include <stdarg.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <math.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#ifndef __cplusplus
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#endif
#include <arv.h>
#include <Imlib2.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define sft_error(...) {				\
    fprintf(stderr, "%s:%d: ", __FUNCTION__, __LINE__);	\
    fprintf(stderr, __VA_ARGS__);			\
    fprintf(stderr, "\n");				\
    exit(EXIT_FAILURE); 				\
  }

#define BARRIER(t1, t2) {					\
    int retval = pthread_barrier_wait(&barrier);                \
    if (retval!=0&&retval!=PTHREAD_BARRIER_SERIAL_THREAD) {     \
      sft_error("%s %u can't wait on %s barrier", t1, id, t2);	\
    }                                                           \
  }

#ifndef TRUE
#define TRUE (0==0)
#endif
#ifndef FALSE
#define FALSE (0!=0)
#endif
#ifndef MIN
#define MIN(a,b) ((a)>(b)?(b):(a))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define PIPELINE 2
#define QUANTUM 1000
#define SAVED 3
#define HEIGHT 600
#define WIDTH 800
#define MAX_THREADS 1
#define CURRENT (frame_number%PIPELINE)
#define PREVIOUS ((frame_number-1)%PIPELINE)
#define MAX_VIDEO 256

/* Scheme->C */
/* Fix Scheme->C signed integers */

#ifdef S2CINTBITS
void sc_error(char *symbol, char *format, TSCP args);
#endif

/* ffmpeg */

#ifndef __cplusplus
struct ffmpeg_video {
  AVFormatContext *pFormatCtx;
  int videoStream;
  AVCodecContext *pCodecCtx;
  AVFrame *pFrame;
  AVFrame *pFrameBGRA;
  uint8_t *buffer;
  struct SwsContext *img_convert_ctx;
  AVPacket packet;
  int frame;
  int videoFinished;
};
#endif

/* aravis */

struct aravis {
  pthread_t capture_thread;
  int capture_thread_running;
  ArvBuffer *current_buffer;
  pthread_mutex_t buffer_lock;
  ArvCamera *camera;
  ArvDevice *device;
  ArvStream *stream;
  int height, width;
  unsigned char *rgba_buffer;
};

/* V4L and other OpenCV supported cameras */

#ifndef __cplusplus
struct opencv_camera {
  CvCapture *capture;
  IplImage *outputBGRA;
};
#endif

struct task_args {
  unsigned int id;
};

#ifdef __cplusplus
extern "C"
#endif
int calibrate_undistort(void);

/* variables */

extern unsigned int threads;
extern unsigned int frame_number, source, height, width;
extern int running;		/* should be volatile */
extern int halt;
extern volatile char video[MAX_VIDEO];
extern Window display_pane;	/* should be volatile */
extern void *((*task[MAX_THREADS])(void *));
extern pthread_t thread[MAX_THREADS];
extern struct task_args task_args[MAX_THREADS];
extern pthread_mutex_t halt_mutex;
extern pthread_barrier_t barrier;
extern double fps;		/* should be volatile */
extern int undistort;		/* should be volatile */
extern volatile int start_saving_frames, saving_frames;
extern volatile int start_playback, playback_running;
extern volatile unsigned int frames_to_save;
unsigned int saving_frame_number;
/* should be volatile */
extern double calibration[9];
/* should be volatile */
extern double distortion[5];
extern Imlib_Image raw_frame[PIPELINE];
extern IplImage *bgra[PIPELINE];

#ifdef S2CINTBITS
/* Scheme->C */
TSCP bool_tscp(int bool);

int tscp_bool(TSCP bool);

/* Fix Scheme->C signed integers */
TSCP sc_int_tscp(int n);

int sc_tscp_int(TSCP p);
#endif

/* my stuff */

#ifdef __cplusplus
extern "C"
#endif
void *sft_malloc(size_t size);

void set_source(unsigned int new_value);

void set_display_pane(Window new_value);

double current_time(void);

/* ffmpeg */

#ifndef __cplusplus
int ffmpeg_first_video_stream(struct ffmpeg_video *ffmpeg_video);

AVCodecContext *ffmpeg_get_codec(struct ffmpeg_video *ffmpeg_video);

int ffmpeg_next_frame(struct ffmpeg_video *ffmpeg_video);

int ffmpeg_video_finished(struct ffmpeg_video *ffmpeg_video);

void ffmpeg_close_and_free_video(struct ffmpeg_video *ffmpeg_video);

struct ffmpeg_video *ffmpeg_open_video(char *filename);

Imlib_Image ffmpeg_get_frame_as_imlib(struct ffmpeg_video *ffmpeg_video);
#endif

/* aravis */

void *aravis_thread(void *args);

ArvBuffer *aravis_get_buffer(struct aravis *aravis);

void aravis_put_buffer(struct aravis *aravis, ArvBuffer *buffer);

struct aravis *aravis_setup(char *camera_id, double fps);

void aravis_start(struct aravis *aravis);

void aravis_stop(struct aravis *aravis);

void aravis_cleanup(struct aravis *aravis);

Imlib_Image aravis_fetch_frame(struct aravis *aravis);

/* V4L and other OpenCV supported cameras */

struct opencv_camera *opencv_camera_setup(void);

void opencv_camera_start(struct opencv_camera *opencv_camera);

void opencv_camera_stop(struct opencv_camera *opencv_camera);

void opencv_camera_cleanup(struct opencv_camera *opencv_camera);

Imlib_Image opencv_camera_fetch_frame(struct opencv_camera *opencv_camera);

/* undistort */

Imlib_Image undistort_image(Imlib_Image image);

/* tasks */

void *camera_task(void *args);

void start_threads(void);

void stop_threads(void);

void playback(char *pathname);

void write_saved_frames(char *pathname);

/* Tam V'Nishlam Shevah L'El Borei Olam */
