/*
 * Part of Comedi::Lib
 *
 * Copyright (c) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
 *
 */
#include <comedilib.h>

int lib_close(comedi_t *dev)
{
   return comedi_close(dev);
}

comedi_t *lib_open(const char *fn)
{
   return comedi_open(fn);
}

int lib_loglevel(int level)
{
   return comedi_loglevel(level);
}

void lib_perror(const char *str)
{
   return comedi_perror(str);
}

const char *lib_strerror(int errnum)
{
   return comedi_strerror(errnum);
}

int lib_errno()
{
   return comedi_errno();
}

int lib_fileno(comedi_t *dev)
{
   return comedi_fileno(dev);
}

int lib_get_n_subdevices(comedi_t *dev)
{
   return comedi_get_n_subdevices(dev);
}

int lib_get_version_code(comedi_t *dev)
{
   return comedi_get_version_code(dev);
}

const char *lib_get_driver_name(comedi_t *dev)
{
   return comedi_get_driver_name(dev);
}

const char *lib_get_board_name(comedi_t *dev)
{
   return comedi_get_board_name(dev);
}

int lib_get_subdevice_type(comedi_t *dev, unsigned int subdev)
{
   return comedi_get_subdevice_type(dev, subdev);
}

int lib_find_subdevice_by_type(comedi_t *dev, int type, unsigned int start)
{
   return comedi_find_subdevice_by_type(dev, type, start);
}

int lib_get_read_subdevice(comedi_t *dev)
{
   return comedi_get_read_subdevice(dev);
}

int lib_get_write_subdevice(comedi_t *dev)
{
   return comedi_get_write_subdevice(dev);
}

int lib_get_subdevice_flags(comedi_t *dev, unsigned int subdev)
{
   return comedi_get_subdevice_flags(dev, subdev);
}

int lib_get_n_channels(comedi_t *dev, unsigned int subdev)
{
   return comedi_get_n_channels(dev, subdev);
}

int lib_range_is_chan_specific(comedi_t *dev, unsigned int subdev)
{
   return comedi_range_is_chan_specific(dev, subdev);
}

int lib_maxdata_is_chan_specific(comedi_t *dev, unsigned int subdev)
{
   return comedi_maxdata_is_chan_specific(dev, subdev);
}

lsampl_t lib_get_maxdata(comedi_t *dev, unsigned int subdev,
                         unsigned int chan)
{
   return comedi_get_maxdata(dev, subdev, chan);
}

int lib_get_n_ranges(comedi_t *dev, unsigned int subdev, unsigned int chan)
{
   return comedi_get_n_ranges(dev, subdev, chan);
}

/* Comedilib returns a pointer to a comedi_range structure */
HV *lib_get_range(comedi_t *dev, unsigned int subdev, unsigned int chan,
                  unsigned int rng)
{
   comedi_range *range;
   HV *range_hash = (HV *)sv_2mortal((SV *)newHV());

   range = comedi_get_range(dev, subdev, chan, rng);
   if (!range) {
      hv_undef(range_hash); /* optional */
      return range_hash;
   }
   
   /*
    * The comedi_range structure contains the following components:
    * double min;
    * double max;
    * unsigned int unit;
    */
   hv_store(range_hash, "min",  3, newSVnv(range->min),  0);
   hv_store(range_hash, "max",  3, newSVnv(range->max),  0);
   hv_store(range_hash, "unit", 4, newSVuv(range->unit), 0);

   return range_hash;
}

int lib_find_range(comedi_t *dev, unsigned int subdev, unsigned int chan,
                   unsigned int unit, double min, double max)
{
   return comedi_find_range(dev, subdev, chan, unit, min, max);
}

int lib_get_buffer_size(comedi_t *dev, unsigned int subdev)
{
    return comedi_get_buffer_size(dev, subdev);
}

int lib_get_max_buffer_size(comedi_t *dev, unsigned int subdev)
{
   return comedi_get_max_buffer_size(dev, subdev);
}

int lib_set_buffer_size(comedi_t *dev, unsigned int subdev, unsigned int size)
{
   return comedi_set_buffer_size(dev, subdev, size);
}

/* _DEPRECATED_ - comedi_trigger */

int lib_do_insn(comedi_t *dev, HV *insn_ref)
{
   SV **insn, **n, **subdev, **chanspec, **data;
   AV *data_arr;
   int len;
   unsigned int *insn_data;
   int i;
   SV **ele;
   comedi_insn cinsn;
   int retval;

   if (!(insn = hv_fetch(insn_ref, "insn", 4, 0)))
      return -1;

   if (!(n = hv_fetch(insn_ref, "n", 1, 0)))
      return -1;

   if (!(subdev = hv_fetch(insn_ref, "subdev", 6, 0)))
      return -1;

   if (!(chanspec = hv_fetch(insn_ref, "chanspec", 8, 0)))
      return -1;

   if (!(data = hv_fetch(insn_ref, "data", 4, 0)))
      return -1;

   if (!(data_arr = (AV *)SvRV(*data)))
      return -1;

   len = av_len(data_arr);
   if (len == -1)
      /* An empty array makes no sense at all */
      return len;

   Newx(insn_data, len + 1, unsigned int);

   for (i = 0; i <= len; i++) {
      if (!(ele = av_fetch(data_arr, i, FALSE)))
         return -1;
      insn_data[i] = SvUV(*ele);
   }

   cinsn.insn = SvUV(*insn);
   cinsn.n = SvUV(*n);
   cinsn.data = insn_data;
   cinsn.subdev = SvUV(*subdev);
   cinsn.chanspec = SvUV(*chanspec);

   if ((retval = comedi_do_insn(dev, &cinsn)) == -1)
      return retval;

   /* Copy back, essential for a number of
    * instructions (e.g. INSN_READ) */
   for (i = 0; i <= len; i++)
      av_store(data_arr, i, newSVuv(cinsn.data[i]));

   return retval;
}

int lib_do_insnlist(comedi_t *dev, HV *insnlist_ref)
{
   SV **n_insns, **insns;
   AV *insns_arr;
   int insns_len;
   comedi_insn *insns_tmp;
   AV **data_arr;
   int *data_len;
   int i;
   SV **insn_ele;
   HV *insn_hash;
   SV **insn, **n, **subdev, **chanspec, **data;
   unsigned int *insn_data;
   int j;
   SV **data_ele;
   comedi_insn cinsn;
   comedi_insnlist insnlist;
   int retval;

   if (!(n_insns = hv_fetch(insnlist_ref, "n_insns", 7, 0)))
      return -1;

   if (n_insns <= 0) /* There's nothing to do */
      return 0;

   if (!(insns = hv_fetch(insnlist_ref, "insns", 5, 0)))
      return -1;

   if (!(insns_arr = (AV *)SvRV(*insns)))
      return -1;

   insns_len = av_len(insns_arr);
   if (insns_len == -1)
      /* An empty array makes no sense at all */
      return insns_len;

   Newx(insns_tmp, insns_len + 1, comedi_insn);
   Newx(data_arr, insns_len + 1, AV *);
   Newx(data_len, insns_len + 1, int);

   for (i = 0; i <= insns_len; i++) {
      if (!(insn_ele = av_fetch(insns_arr, i, FALSE)))
         return -1;

      if (!(insn_hash = (HV *)SvRV(*insn_ele)))
         return -1;

      if (!(insn = hv_fetch(insn_hash, "insn", 4, 0)))
         return -1;

      if (!(n = hv_fetch(insn_hash, "n", 1, 0)))
         return -1;

      if (!(subdev = hv_fetch(insn_hash, "subdev", 6, 0)))
         return -1;
      
      if (!(chanspec = hv_fetch(insn_hash, "chanspec", 8, 0)))
         return -1;

      if (!(data = hv_fetch(insn_hash, "data", 4, 0)))
         return -1;

      if (!(data_arr[i] = (AV *)SvRV(*data)))
         return -1;

      data_len[i] = av_len(data_arr[i]);
      if (data_len[i] == -1)
         continue; /* Or better return -1 ? */

      Newx(insn_data, data_len[i] + 1, unsigned int);

      for (j = 0; j <= data_len[i]; j++) {
         if (!(data_ele = av_fetch(data_arr[i], j, FALSE)))
            return -1;
         insn_data[j] = SvUV(*data_ele);
      }

      cinsn.insn = SvUV(*insn);
      cinsn.n = SvUV(*n);
      cinsn.data = insn_data;
      cinsn.subdev = SvUV(*subdev);
      cinsn.chanspec = SvUV(*chanspec);
   
      insns_tmp[i] = cinsn;
   }

   insnlist.n_insns = SvUV(*n_insns);
   insnlist.insns = insns_tmp;

   retval = comedi_do_insnlist(dev, &insnlist);
   if (retval == -1)
      return retval;

   /* Copy back */
   for (i = 0; i <= insns_len; i++)
      for (j = 0; j <= data_len[i]; j++)
         av_store(data_arr[i], j, newSViv(insnlist.insns[i].data[j]));
   
   return retval;
}

int lib_lock(comedi_t *dev, unsigned int subdev)
{
   return comedi_lock(dev, subdev);
}

int lib_unlock(comedi_t *dev, unsigned int subdev)
{
   return comedi_unlock(dev, subdev);
}

/* _DEPRECATED_ - comedi_to_phys */
/* _DEPRECATED_ - comedi_from_phys */

int lib_data_read(comedi_t *dev, unsigned int subdev, unsigned int chan,
                  unsigned int rng, unsigned int aref, SV *data)
{
   int retval;
   lsampl_t data_tmp;
   SV *sv;

   retval = comedi_data_read(dev, subdev, chan, rng, aref, &data_tmp);
   if (retval == -1)
      return retval;   

   sv = SvRV(data);
   sv_setuv(sv, data_tmp);

   return retval;
}

int lib_data_read_delayed(comedi_t *dev, unsigned int subdev, unsigned int chan,
                          unsigned int rng, unsigned int aref, SV *data,
                          unsigned int ns)
{
   int retval;
   lsampl_t data_tmp;
   SV *sv;

   retval = comedi_data_read_delayed(dev, subdev, chan, rng, aref, &data_tmp,
                                     ns);
   if (retval == -1)
      return retval;

   sv = SvRV(data);
   sv_setuv(sv, data_tmp);

   return retval;
}

int lib_data_read_hint(comedi_t *dev, unsigned int subdev, unsigned int chan,
                       unsigned int rng, unsigned int aref)
{
   return comedi_data_read_hint(dev, subdev, chan, rng, aref);   
}

int lib_data_write(comedi_t *dev, unsigned int subdev, unsigned int chan,
                   unsigned int rng, unsigned int aref, lsampl_t data)
{
   return comedi_data_write(dev, subdev, chan, rng, aref, data);
}

int lib_dio_config(comedi_t *dev, unsigned int subdev, unsigned int chan,
                   unsigned int dir)
{
   return comedi_dio_config(dev, subdev, chan, dir);
}

int lib_dio_get_config(comedi_t *dev, unsigned int subdev, unsigned int chan,
                       SV *dir)
{
   int retval;
   unsigned int dir_tmp;
   SV *sv;

   retval = comedi_dio_get_config(dev, subdev, chan, &dir_tmp);
   if (retval == -1)
      return retval;

   sv = SvRV(dir);
   sv_setuv(sv, dir_tmp);
   
   return retval;
}

int lib_dio_read(comedi_t *dev, unsigned int subdev, unsigned int chan,
                 SV *bit)
{
   int retval;
   unsigned int bit_tmp;
   SV *sv;

   retval = comedi_dio_read(dev, subdev, chan, &bit_tmp);
   if (retval == -1)
      return retval;

   sv = SvRV(bit);
   sv_setuv(sv, bit_tmp);

   return retval;
}

int lib_dio_write(comedi_t *dev, unsigned int subdev, unsigned int chan,
                  unsigned int bit)
{
   return comedi_dio_write(dev, subdev, chan, bit);
}

/* _DEPRECATED_ - comedi_dio_bitfield */

int lib_dio_bitfield2(comedi_t *dev, unsigned int subdev,
                      unsigned int write_mask, SV *bits,
                      unsigned int base_ch)
{
   int retval;
   unsigned int bits_tmp;
   SV *sv;

   retval = comedi_dio_bitfield2(dev, subdev, write_mask, &bits_tmp, base_ch);
   if (retval == -1)
      return retval;

   sv = SvRV(bits);
   sv_setuv(sv, bits_tmp);

   return retval;
}

/* _DEPRECATED_ - comedi_sv_init */
/* _DEPRECATED_ - comedi_sv_update */
/* _DEPRECATED_ - comedi_sv_measure */

/* Not implemented yet - comedi_get_cmd_src_mask */
/* Not implemented yet - comedi_get_cmd_generic_timed */

int lib_cancel(comedi_t *dev, unsigned int subdev)
{
   return comedi_cancel(dev, subdev);
}

/* Not implemented yet - comedi_command */
/* Not implemented yet - comedi_command_test */

int lib_poll(comedi_t *dev, unsigned int subdev)
{
   return comedi_poll(dev, subdev);
}

int lib_set_max_buffer_size(comedi_t *dev, unsigned int subdev,
                            unsigned int max_size)
{
   return comedi_set_max_buffer_size(dev, subdev, max_size);
}

int lib_get_buffer_contents(comedi_t *dev, unsigned int subdev)
{
   return comedi_get_buffer_contents(dev, subdev);
}

int lib_mark_buffer_read(comedi_t *dev, unsigned int subdev,
                         unsigned int num_bytes)
{
   return comedi_mark_buffer_read(dev, subdev, num_bytes);
}

int lib_mark_buffer_written(comedi_t *dev, unsigned int subdev,
                            unsigned int num_bytes)
{
   return comedi_mark_buffer_written(dev, subdev, num_bytes);
}

int lib_get_buffer_offset(comedi_t *dev, unsigned int subdev)
{
   return comedi_get_buffer_offset(dev, subdev);
}

/* _DEPRECATED_ - comedi_get_timer */
/* _DEPRECATED_ - comedi_timed_1chan */
