/*
 *  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/>.
 */

#include "webcam.h"

static struct v4l2_queryctrl queryctrl;
static struct v4l2_querymenu querymenu;


static void
enumerate_menu(const webcam_t *cam)
{
  memset(&querymenu, 0, sizeof(querymenu));
  querymenu.id = queryctrl.id;

  for (querymenu.index = queryctrl.minimum;
       querymenu.index <= (unsigned)queryctrl.maximum;
       querymenu.index++)
    if (0 == ioctl(cam->fd, VIDIOC_QUERYMENU, &querymenu)) {
      VERBOSE(printf ("[i]   - %s\n", querymenu.name));
    } else {
      xperror("VIDIOC_QUERYMENU");
    }
}


static void
enumerate_base_cids(const webcam_t *cam)
{
  memset(&queryctrl, 0, sizeof(queryctrl));

  for (queryctrl.id = V4L2_CID_BASE;
       queryctrl.id < V4L2_CID_LASTP1;
       queryctrl.id++) {
    if (0 == ioctl(cam->fd, VIDIOC_QUERYCTRL, &queryctrl)) {
      if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
        continue;
      }

      VERBOSE(printf("[i] * %s\n", queryctrl.name));

      if (queryctrl.type == V4L2_CTRL_TYPE_MENU) {
        enumerate_menu(cam);
      }
    } else {
      if (errno == EINVAL) {
        continue;
      } else {
        xperror("VIDIOC_QUERYCTRL");
      }
    }
  }
}


static void
enumerate_private_cids(webcam_t *cam)
{
  memset(&queryctrl, 0, sizeof (queryctrl));

  for (queryctrl.id = V4L2_CID_PRIVATE_BASE;
       ;
       queryctrl.id++) {
    if (0 == ioctl(cam->fd, VIDIOC_QUERYCTRL, &queryctrl)) {
      if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
        continue;
      }

      VERBOSE(printf ("[i] * %s\n", queryctrl.name));

      if (queryctrl.type == V4L2_CTRL_TYPE_MENU) {
        enumerate_menu(cam);
      }
    } else {
      if (errno == EINVAL) {
        break;
      } else {
        xperror("VIDIOC_QUERYCTRL");
      }
    }
  }
}


void
enumerate_cids(webcam_t *cam)
{
  VERBOSE(printf("[i] Webcam %d: base controls\n", cam->cam_no));
  enumerate_base_cids(cam);
  VERBOSE(printf("[i] Webcam %d: private controls\n", cam->cam_no));
  enumerate_private_cids(cam);
}


static void
set_ctrl(int fd, const int ctrl, const int value)
{
  struct v4l2_queryctrl queryctrl;
  struct v4l2_control control;

  memset(&queryctrl, 0, sizeof(queryctrl));
  queryctrl.id = ctrl;

  if (-1 == xioctl(fd, VIDIOC_QUERYCTRL, &queryctrl)) {
    if (errno != EINVAL) {
      xerror("VIDIOC_QUERYCTRL\n");
    } else {
      fprintf(stderr, "[!] V4L2_CID_BRIGHTNESS is not supported\n");
    }
  } else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
    fprintf(stderr, "[!] %s is not supported\n",
            (ctrl == V4L2_CID_VFLIP) ? "V4L2_CID_VFLIP" : "V4L2_CID_HFLIP");
  } else {
    memset(&control, 0, sizeof(control));
    control.id = ctrl;
    control.value = queryctrl.default_value;

    if (-1 == xioctl(fd, VIDIOC_S_CTRL, &control)) {
      xerror("VIDIOC_S_CTRL\n");
    }
  }

  VERBOSE(printf("[i] %s: default= %d",
                 ((ctrl == V4L2_CID_VFLIP) ? "V4L2_CID_VFLIP" : "V4L2_CID_HFLIP"),
                 control.value));

  memset(&control, 0, sizeof (control));
  control.id = ctrl;

  if (0 == xioctl(fd, VIDIOC_G_CTRL, &control)) {
    control.value = value;

    if (-1 == xioctl(fd, VIDIOC_S_CTRL, &control)
        && errno != ERANGE) {
      xerror("VIDIOC_S_CTRL\n");
    } else {
      VERBOSE(printf(" set: %d\n", value));
    }
    /* Ignore if V4L2_CID_CONTRAST is unsupported */
  } else if (errno != EINVAL) {
    xerror("VIDIOC_G_CTRL\n");
  }
}


void
cam_hflip(int fd, const int value)
{
  set_ctrl(fd, V4L2_CID_HFLIP, value);
}


void
cam_vflip(int fd, const int value)
{
  set_ctrl(fd, V4L2_CID_VFLIP, value);
}


int
list_inputs(const webcam_t *cam)
{
  struct v4l2_input inputs;
  int index = 0;
  int fd = cam->fd;

  inputs.index = 0;
  while (ioctl(fd, VIDIOC_ENUMINPUT, &inputs) == 0) {
    VERBOSE(printf("[i] Webcam %d: input #%d\n", cam->cam_no, index));
    VERBOSE(printf("[i] * Name: %s\n", inputs.name));

    VERBOSE(printf("[i] * Type: "));
    if (inputs.type == V4L2_INPUT_TYPE_CAMERA) {
      VERBOSE(printf("camera\n"));
    } else if (inputs.type == V4L2_INPUT_TYPE_TUNER) {
      VERBOSE(printf("tuner\n"));
    } else {
      assert(0);
    }

    VERBOSE(printf("[i] * Video standard: %d\n", (int)inputs.std));
    index++;
    inputs.index = index;
  }

  return index;
}
