// Copyright (C) 2014-2025 Andreas Weber <andy.weber.aw@gmail.com>
//
// This program 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 3 of the License, or (at your option) any later
// version.
//
// This program 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
// this program; if not, see <http://www.gnu.org/licenses/>.

#ifndef _V4L2_HANDLER_
#define _V4L2_HANDLER_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libv4l2.h>

#include <octave/oct.h>
#include <octave/ov-struct.h>

#include <octave/dMatrix.h>
#include <iostream>
#include "cl_imaq_handler.h"
#include "cl_img_win.h"

#define CLEAR(x) memset(&(x), 0, sizeof(x))
#define xioctl(n, r, p) xioctl_name(n, r, p, #r, __FILE__, __LINE__)

//! buffers for mmap
struct buffer
{
  void   *start;  //!< start of buffer
  size_t length;  //!< length in bytes, e.g. 3*640*480 (nColors*width*height)
};

/*!
 * v4l2 wrapper for octave-image-acquisition
 *
 * A big help was the Video Grabber example using libv4l by Mauro Carvalho Cheha
 * https://www.linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/v4l2grab-example.html
 */
class v4l2_handler: public imaq_handler
{
public:

  v4l2_handler ();

  octave_base_dld_value *clone (void) const // TODO: check if this is okay
  {
    octave_stdout << "v4l2_handler clone" << std::endl;
    return new v4l2_handler (*this);
  }

  octave_base_dld_value *empty_clone (void) const // TODO: check if this is okay
  {
    octave_stdout << "v4l2_handler empty_clone" << std::endl;
    return new v4l2_handler ();
  }

  ~v4l2_handler (void);

  octave_map enum_devices ();

  octave_scalar_map open (std::string d, bool quiet); //!< open a v4l2 device e.g. /dev/video0
  void print (std::ostream& os, bool pr_as_read_syntax);  //!< print itself on ostream
  octave_value querycap ();          //!< Query device capabilities

  octave_value enum_inputs ();       //!< Enumerate video inputs
  int get_input ();                  //!< Query the current video input
  void set_input (int index);        //!< Select video input

  octave_value enum_formats ();      //!< Enumerate image formats

  Matrix enum_framesizes (std::string pixelformat);     //!< Enumerate frame sizes
  octave_scalar_map g_fmt ();                      //!< Get current format
  void s_fmt (std::string fmtstr, uint32_t xres, uint32_t yres); //!< Set format

  Matrix enum_frameintervals (std::string pixelformat, uint32_t width, uint32_t height);     //!< Enumerate frame intervals
  Matrix get_frameinterval ();
  void set_frameinterval (Matrix timeperframe);

  octave_value queryctrl ();                  //!< Query controls
  int g_ctrl (int id);                        //!< Get control
  void s_ctrl (int id, octave_value val);     //!< Set control

  octave_value_list capture (int nargout,
                             bool preview,
                             bool raw_output);//!< Retrieve captured image from buffer

  void capture_to_ppm (const char *fn);       //!< Retrieve captured image from buffer and save it as ppm

  void streamon (unsigned int n);             //!< start streaming with n buffers
  void streamoff ();                          //!< stop streaming

  void close ();                              //!< close v4l2 device

  bool is_video_capture ()
  {
    return _is_video_capture;
  }

  // see https://unix.stackexchange.com/questions/512759/multiple-dev-video-for-one-physical-device
  // why today video0 and video1 are created simulateneously
  bool is_meta_capture ()
  {
    return _is_meta_capture;
  }

private:
  v4l2_handler (const v4l2_handler& m);
  static bool type_loaded;

  int fd;
  std::string dev;
  unsigned int n_buffer;
  struct buffer *buffers;
  bool streaming;
  //img_win *preview_window;
  bool _is_video_capture;
  bool _is_meta_capture;

  void xioctl_name (int fh, unsigned long int request, void *arg, const char* name, const char* file, const int line);
  octave_scalar_map get_osm (struct v4l2_queryctrl queryctrl);
  void reqbufs (unsigned int n);  //!< Initiate Memory Mapping or User Pointer I/O
  void mmap ();
  void qbuf ();
  void munmap ();
  octave_scalar_map expand_cap (unsigned int cap);
};

#endif
