////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// Implementation file
//
// SPDX-FileCopyrightText: © 1997, Mark Danks and the GEM contributors
// SPDX-License-Identifier: GPL-2.0-or-later
//
////////////////////////////////////////////////////////
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif


#include "videoSGI.h"
#include "plugins/PluginFactory.h"
using namespace gem::plugins;

#include "Gem/RTE.h"

#ifdef HAVE_VL_VL_H
# include <unistd.h>
# include <dmedia/vl_vino.h>

REGISTER_VIDEOFACTORY("sgi", videoSGI);


/*
  from pix_indycam:

  "zoom" -> zoomMess
  "bright" -> brightMess
  "contrast" -> contrastMess
  "hue" -> hueMess
  "sat" -> satMess
*/


/////////////////////////////////////////////////////////
//
// video
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
videoSGI :: videoSGI()
  : videoBase("sgi", 0),
    m_haveVideo(0), m_swap(1), m_colorSwap(0),
    m_svr(NULL), m_drn(NULL), m_src(NULL), m_path(NULL)
{
  provide("indy");
  provide("analog");
}

////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
videoSGI :: ~videoSGI()
{
  stopTransfer();
  close();
  cleanPixBlock();
}

bool videoSGI :: openDevice()
{
  // Connect to the daemon
  if ( !(m_svr = vlOpenVideo("")) ) {
    logpost(0, 3+0, "[GEM:videoSGI] Unable to open video");
    goto cleanup;
  }
  // Set up a drain node in memory
  m_drn = vlGetNode(m_svr, VL_DRN, VL_MEM, VL_ANY);

  // Set up a source node on any video source
  m_src = vlGetNode(m_svr, VL_SRC, VL_VIDEO, VL_ANY);

  // Create a path using the first device that will support it
  m_path = vlCreatePath(m_svr, VL_ANY, m_src, m_drn);

  // Set up the hardware for and define the usage of the path
  if ( (vlSetupPaths(m_svr, (VLPathList)&m_path, 1, VL_SHARE,
                     VL_SHARE)) < 0 ) {
    logpost(0, 3+0, "[GEM:videoSGI] Unable to setup video path");
    goto cleanup;
  }

  // Set the packing to RGBA
  VLControlValue val;

  // first try to see if it can handle RGBA format

  // according to vl.h, this is really RGBA (OpenGL format)
  val.intVal = VL_PACKING_ABGR_8;
  if ( vlSetControl(m_svr, m_path, m_drn, VL_PACKING, &val) ) {
    // nope, so try ABGR, and set the color swapping
    // according to vl.h, this is really ABGR (IrisGL format)
    val.intVal = VL_PACKING_RGBA_8;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_PACKING, &val) ) {
      logpost(0, 3+0, "[GEM:videoSGI] Unable to set the video packing");
      goto cleanup;
    }
    logpost(0, 3+1, "[GEM:videoSGI] Video has to color swap (ABGR to RGBA)");
    m_colorSwap = 1;
  }

  // Get the video size

  VLControlValue value;
  if(m_width>0) {
    value.xyVal.x = m_width;
    value.xyVal.y = m_height;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_SIZE, &value) ) {
      vlGetControl(m_svr, m_path, m_drn, VL_SIZE, &value);

      logpost(0, 3+1, "[GEM:videoSGI] dimen error: wanted %dx%d got %dx%d", m_width,
              m_height, value.xyVal.x, value.xyVal.y);
      m_width =value.xyVal.x;
      m_height=value.xyVal.y;
    }
  }
  vlGetControl(m_svr, m_path, m_drn, VL_SIZE, &value);
  m_width =value.xyVal.x;
  m_height=value.xyVal.y;

  m_pixBlock.image.xsize = m_width;
  m_pixBlock.image.ysize = m_height;
  m_pixBlock.image.reallocate();

  return true;
cleanup:

  closeDevice();
  return false;
}

void videoSGI :: closeDevice()
{
  m_src=NULL;
  m_drn=NULL;
  if(m_svr) {
    if(m_path) {
      vlDestroyPath(m_svr, m_path);
    }

    vlCloseVideo(m_svr);
  }

  m_path=NULL;
  m_svr=NULL;
}



////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
pixBlock *videoSGI::getFrame(void)
{
  {
    if(!(m_haveVideo && m_capturing)) {
      return NULL;
    }

    VLInfoPtr info = vlGetLatestValid(m_svr, m_buffer);
    while (!info) {
      sginap(1);
      info = vlGetLatestValid(m_svr, m_buffer);
    }

    // Get a pointer to the frame
    unsigned char *dataPtr = (unsigned char *)(vlGetActiveRegion(m_svr,
                             m_buffer, info));
    m_pixBlock.image.fromABGR(dataPtr);

    // free the frame
    vlPutFree(m_svr, m_buffer);

    m_pixBlock.newimage = 1;
    return &m_pixBlock;

  }


////////////////////////////////////////////////////////
// startTransfer
//
/////////////////////////////////////////////////////////
  bool videoSGI :: startTransfer() {
    if(NULL=m_svr || NULL=m_path || NULL==m_drn || NULL=m_src) {
      return false;
    }

    // Create and register a buffer for 1 frame
    m_buffer = vlCreateBuffer(m_svr, m_path, m_drn, 1);
    if ( !m_buffer ) {
      pd_error(0, "[GEM:videoSGI] Unable to allocate buffer");
      return false;
    }

    vlRegisterBuffer(m_svr, m_path, m_drn, m_buffer);

    // Begin the data transfer
    if ( vlBeginTransfer(m_svr, m_path, 0, NULL) ) {
      pd_error(0, "[GEM:videoSGI] Unable to start video transfer");
      vlDeregisterBuffer(m_svr, m_path, m_drn, m_buffer);
      vlDestroyBuffer(m_svr, m_buffer);
      return false;
    }
    return true;
  }

////////////////////////////////////////////////////////
// stopTransfer
//
/////////////////////////////////////////////////////////
  bool videoSGI :: stopTransfer() {
    if ( !m_capturing ) {
      return false;
    }
    if(NULL=m_svr || NULL=m_path || NULL==m_drn || NULL=m_src) {
      return false;
    }

    // Clean up the buffer
    vlEndTransfer(m_svr, m_path);
    vlDeregisterBuffer(m_svr, m_path, m_drn, m_buffer);
    vlDestroyBuffer(m_svr, m_buffer);
    m_buffer=NULL;

    return true;
  }

#if 0
////////////////////////////////////////////////////////
// offsetMess
//
/////////////////////////////////////////////////////////
  void videoSGI :: offsetMess(int x, int y) {
    if (!m_haveVideo) {
      logpost(0, 3+1, "[GEM:videoSGI] Connect to video first");
      return;
    }

    // stop the transfer and destroy the buffer
    if ( !stopTransfer() ) {
      logpost(0, 3+0, "[GEM:videoSGI] error stopping transfer");
      return;
    }

    VLControlValue value;
    value.xyVal.x = x;
    value.xyVal.y = y;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_OFFSET, &value) ) {
      logpost(0, 3+0, "[GEM:videoSGI] offset error");
      startTransfer();
      return;
    }

    // start the transfer and rebuild the buffer
    if ( !startTransfer() ) {
      logpost(0, 3+0, "[GEM:videoSGI] error starting transfer");
      return;
    }
  }


/////////////////////////////////////////////////////////
// zoomMess
//
/////////////////////////////////////////////////////////
  void videoINDY :: zoomMess(int num, int denom) {
    if (!m_haveVideo) {
      logpost(0, 3+1, "[GEM:videoSGI] Connect to video first");
      return;
    }
    VLControlValue value;
    value.fractVal.numerator = num;
    value.fractVal.denominator = denom;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_ZOOM, &value) ) {
      pd_error(0, "[GEM:videoSGI] zoom error");
    }
  }

/////////////////////////////////////////////////////////
// brightMess
//
/////////////////////////////////////////////////////////
  void videoINDY :: brightMess(int val) {
    if (!m_haveVideo) {
      logpost(0, 3+1, "[GEM:videoSGI] Connect to video first");
      return;
    }
    VLControlValue value;
    value.intVal = val;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_BRIGHTNESS, &value) ) {
      pd_error(0, "[GEM:videoSGI] problem setting brightness");
    }
  }

/////////////////////////////////////////////////////////
// contrastMess
//
/////////////////////////////////////////////////////////
  void videoINDY :: contrastMess(int val) {
    if (!m_haveVideo) {
      logpost(0, 3+1, "[GEM:videoSGI] Connect to video first");
      return;
    }
    VLControlValue value;
    value.intVal = val;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_CONTRAST, &value) ) {
      pd_error(0, "[GEM:videoSGI] problem setting contrast");
    }
  }

/////////////////////////////////////////////////////////
// hueMess
//
/////////////////////////////////////////////////////////
  void videoINDY :: hueMess(int val) {
    if (!m_haveVideo) {
      logpost(0, 3+1, "[GEM:videoSGI] Connect to video first");
      return;
    }
    VLControlValue value;
    value.intVal = val;
    if ( vlSetControl(m_svr, m_path, m_drn, VL_HUE, &value) ) {
      pd_error(0, "[GEM:videoSGI] problem setting hue");
    }
  }

/////////////////////////////////////////////////////////
// satMess
//
/////////////////////////////////////////////////////////
  void videoINDY :: satMess(int val) {
    if (!m_haveVideo) {
      logpost(0, 3+1, "[GEM:videoSGI] Connect to video first");
      return;
    }
    VLControlValue value;
    value.intVal = val;
    if ( vlSetControl(m_svr, m_path, m_src, VL_VINO_INDYCAM_SATURATION,
                      &value) ) {
      pd_error(0, "[GEM:videoSGI] problem setting saturation");
    }
  }


#endif
///////////////////////////////////////////////////////
// dimenMess
//
////////////////////////////////////////////////////////
  bool videoSGI :: setDimen(int x, int y, int leftmargin, int rightmargin,
                            int topmargin, int bottommargin) {
    {
      bool result=true;
      m_width=x;
      m_height=y

      if(NULL=m_svr || NULL=m_path || NULL==m_drn || NULL=m_src) {
        return false;
      }
      if(!m_capturing) {
        return false;
      }

      stopTransfer();

      VLControlValue value;
      value.xyVal.x = m_width;
      value.xyVal.y = m_height;
      if ( vlSetControl(m_svr, m_path, m_drn, VL_SIZE, &value) ) {
        vlGetControl(m_svr, m_path, m_drn, VL_SIZE, &value);

        logpost(0, 3+1, "[GEM:videoSGI] dimen error: wanted %dx%d got %dx%d", m_width,
                m_height, value.xyVal.x, value.xyVal.y);
        m_width =value.xyVal.x;
        m_height=value.xyVal.y;
        result=false;
      }
      m_pixBlock.image.xsize = m_width;
      m_pixBlock.image.ysize = m_height;
      m_pixBlock.image.reallocate();

      start();
      return result;
    }
#else /* !HAVE_VL_VL_H */
videoSGI ::  videoSGI() : videoBase("") { }
videoSGI :: ~videoSGI() { }
#endif  /* !HAVE_VL_VL_H */
