/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou 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 of the License, or
 *  (at your option) any later version.
 *
 *  lebiniou 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 lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __BINIOU_CONTEXT_H
#define __BINIOU_CONTEXT_H

#include <inttypes.h>
#ifdef WITH_GL
#include <GL/gl.h>
#endif
#include "input.h"
#include "sequencemanager.h"
#include "params3d.h"
#include "particles.h"
#include "imagefader.h"
#include "cmapfader.h"
#include "alarm.h"
#include "brandom.h"
#include "buffer_RGBA.h"
#include "bulfius.h"
#include "images.h"

#define NFPS          (25 * 5) // to get mean fps
#define MAX_BANKS     24
#define MAX_SHORTCUTS 10


enum RandomMode { BR_NONE = 0, BR_SEQUENCES, BR_SCHEMES, BR_BOTH, BR_NB };
#define DEFAULT_RANDOM_MODE BR_BOTH

enum Shortcuts { SH_COLORMAP = 0, SH_IMAGE, SH_NB };

// Random changes delays
#define DELAY_MIN 15
#define DELAY_MAX 30
enum RandomDelays { BD_COLORMAPS = 0, BD_IMAGES, BD_SEQUENCES
#ifdef WITH_WEBCAM
, BD_WEBCAMS
#endif
                  };


#define ACTIVE_BUFFER  0
#define PASSIVE_BUFFER 1
#define SAVE_BUFFER    2

#ifdef WITH_WEBCAM
#define NSCREENS       6 // <--- ??? oliv3
#else
#define NSCREENS       3
#endif

#define MAX_CAMS       6

// store the last N frames from the webcam -same as EffectTV
#define CAM_SAVE       32


typedef struct BKey_s {
  uint32_t val;
  uint16_t mod;
} BKey_t;

typedef struct Context_s {
  uint8_t    running:1;

  Plugin_t  *input_plugin;
  Input_t   *input;

  // Buffers
  Buffer8_t *buffers[NSCREENS];
  BufferRGBA_t *rgba_buffers[NSCREENS];
  /* 0 = active_buffer
   * 1 = passive_buffer - for double-buffering
   * 2 = save_buffer    - push/pop screen for lens effects
   */

  int webcams;
#ifdef WITH_WEBCAM
  // webcam
  Buffer8_t *cam_save[MAX_CAMS][CAM_SAVE];
  Buffer8_t *cam_ref[MAX_CAMS];
  Buffer8_t *cam_ref0[MAX_CAMS]; /* reference picture taken on program start */
  uint8_t ref_taken[MAX_CAMS];

  // auto-change webcams
  Shuffler_t *webcams_shuffler;
  Alarm_t    *a_webcams;
  uint8_t    lock_webcam;

  // Webcam
  pthread_mutex_t cam_mtx[MAX_CAMS];
  uint8_t cam; /* active webcam */
#endif

  // Faders
  ImageFader_t *imgf;
  Alarm_t *a_images;

  CmapFader_t *cf;
  Alarm_t *a_cmaps;

  GSList    *outputs;

  SequenceManager_t *sm;

  Params3d_t params3d;

  // auto-change sequences (user or generated)
  enum RandomMode random_mode;
  Alarm_t *a_random;
  uint8_t allow_random_changes;

  Timer_t *timer;

  // FPS
  uint8_t max_fps;
  float   i_max_fps; // inverse of the maximum frames per second
  int     fps[NFPS];
  Timer_t *fps_timer;

  uint8_t window_decorations:1;
  uint8_t fullscreen:1;
  uint8_t display_colormap:1;
  uint8_t take_screenshot:1;
  uint8_t bypass:1;

  // Banks
  uint32_t banks[MAX_BANKS][MAX_BANKS];
  uint8_t  bank_set; // current bankset
  uint8_t  bank; // current bank

  // Shortcuts
  uint32_t shortcuts[SH_NB][MAX_SHORTCUTS];

  // OpenGL
#ifdef WITH_GL
  uint8_t texture_ready:1;
  uint8_t texture_used:1;
  uint8_t gl_done:1;
  GLuint textures[NSCREENS];
  GLuint cam_textures[MAX_CAMS];
  uint8_t pulse_3d:1;
  uint8_t force_cube:1;
#endif

  // Target
  Image8_t *target_pic;

  Plugin_t *locked; // Locked plugin feature

  uint32_t input_size;

  uint8_t allow_auto_colormaps;
  uint8_t auto_colormaps;
  uint8_t lock_colormap;
  uint8_t allow_auto_images;
  uint8_t auto_images;
  uint8_t lock_image;
#ifdef WITH_WEBCAM
  uint8_t auto_webcams;
#endif

  // used to randomly mix buffers, values: 0 | 1
  Buffer8_t *random;

  // Ulfius
  struct _u_instance vui_instance;
  GSList *ws_clients;
  pthread_mutex_t ws_clients_mutex;

  // Current frame
  Pixel_t *frame;
  pthread_mutex_t frame_mutex;

  // Playlist
  json_t *playlist;
  Timer_t *track_timer;
  float track_duration;
  Shuffler_t *playlist_shuffler;

  // Video
  Buffer8_t *video_save[CAM_SAVE];

  // Bandpass layer mode
  uint8_t bandpass_min, bandpass_max;

  // UI commands
  GAsyncQueue *commands;
} Context_t;


Context_t *Context_new(const uint8_t);
void Context_delete(Context_t *);

void Context_set(Context_t *);
void Context_run(Context_t *);
void Context_update(Context_t *);
void Context_update_auto(Context_t *);

int  Context_add_rand(Sequence_t *, const enum PluginOptions, const Plugin_t *);

void Context_set_max_fps(Context_t *, const uint8_t);
void Context_set_engine_random_mode(Context_t *, const enum RandomMode);

void Context_insert_plugin(Context_t *, Plugin_t *);
void Context_remove_plugin(Context_t *, Plugin_t *);

void Context_screenshot(Context_t *, const u_char);
int Context_fps(const Context_t *);

void Context_first_sequence(Context_t *);
void Context_previous_sequence(Context_t *);
void Context_next_sequence(Context_t *);
void Context_last_sequence(Context_t *);
void Context_random_sequence(Context_t *);
void Context_set_sequence(Context_t *, const uint64_t);

json_t *Context_process_command(Context_t *, const enum Command);
json_t *Context_process_ui_command(Context_t *, const enum Command, const json_t *);
json_t *Context_process_vui_command(Context_t *, const enum Command, const json_t *);

Buffer8_t *active_buffer(const Context_t *);
Buffer8_t *passive_buffer(const Context_t *);


static inline Buffer8_t *
save_buffer(const Context_t *ctx)
{
  return ctx->buffers[SAVE_BUFFER];
}


static inline void
swap_buffers(Context_t *ctx)
{
  Buffer8_t *tmp = ctx->buffers[ACTIVE_BUFFER];
  ctx->buffers[ACTIVE_BUFFER] = ctx->buffers[PASSIVE_BUFFER];
  ctx->buffers[PASSIVE_BUFFER] = tmp;
}


static inline void
push_buffer(const Context_t *ctx)
{
  Buffer8_copy(active_buffer(ctx), save_buffer(ctx));
}


static inline void
pop_buffer(const Context_t *ctx)
{
  Buffer8_copy(save_buffer(ctx), active_buffer(ctx));
}


Pixel_t *export_RGB_buffer(const Context_t *, const uint8_t, const uint8_t);
Pixel_t *export_RGB_active_buffer(const Context_t *, const uint8_t);

Pixel_t *export_YUV_buffer(const Context_t *, const uint8_t, const uint8_t);
Pixel_t *export_YUV_active_buffer(const Context_t *, const uint8_t);

const RGBA_t *export_RGBA_buffer(const Context_t *, const uint8_t);
const RGBA_t *export_RGBA_active_buffer(const Context_t *);

/* Banks */
void Context_save_banks(const Context_t *);
void Context_load_banks(Context_t *);
void Context_use_bank(Context_t *, const uint8_t);
void Context_clear_bank(Context_t *, const uint8_t);
void Context_store_bank(Context_t *, const uint8_t);
json_t *Context_get_bank_set(const Context_t *, const uint8_t);

/* Shortcuts */
void Context_save_shortcuts(const Context_t *);
void Context_load_shortcuts(Context_t *);
json_t *Context_get_shortcuts(const Context_t *);

void Context_make_GL_RGBA_texture(Context_t *ctx, const uint8_t);

#ifdef WITH_WEBCAM
void Context_make_GL_gray_texture(Context_t *ctx, const uint8_t);
void Context_push_webcam(Context_t *, Buffer8_t *, const int);
#endif

enum ShufflerMode Context_get_shuffler_mode(const enum RandomDelays);

void Context_set_input_size(Context_t *, const uint32_t);
uint32_t Context_get_input_size(const Context_t *);

// Volume scaling
void Context_set_volume_scale(Context_t *, const double);
double Context_get_volume_scale(const Context_t *);

void Context_mix_buffers(const Context_t *, Buffer8_t *[2]);
void Context_interleave_buffers(const Context_t *);

// JQuery UI
void Context_start_ulfius(Context_t *);
void Context_stop_ulfius(Context_t *);
// Vue UI
void Context_start_vui(Context_t *);
void Context_stop_vui(Context_t *);

json_t *Context_get_state(const Context_t *);
void Context_websocket_send_colormap(Context_t *);
void Context_websocket_send_image(Context_t *);
void Context_websocket_send_sequence(Context_t *);
// Usage statistics
void Context_statistics(const Context_t *);

// Frame
void Context_to_PNG(Context_t *, uint8_t **, size_t *, const uint16_t, const uint16_t);

// Web UI
json_t *Context_output_plugins(const Context_t *);

// Playlist
void Context_init_playlist(Context_t *);
void Context_next_track(Context_t *);

// Video
void Context_push_video(Context_t *, Buffer8_t *);

// Timers
void Context_create_timers(Context_t *);

// UI commands
void Context_push_command(Context_t *, Command_t *);
void Context_process_commands(Context_t *);
void Context_free_commands(Context_t *);

#endif /* __BINIOU_CONTEXT_H */
