#include <sane/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/fcntl.h>

#include <sane/sane.h>
#include <sane/sanei.h>
#include <sane/sanei_wire.h>
#include <sane/sanei_codec_ascii.h>
#include <sane/sanei_codec_bin.h>

static Wire w;

static SANE_Word dpi_word_list[] =
  {
    4,				/* # of elements */
    3, 30, 300, -600
  };

static SANE_String_Const mode_list[] =
  {
    "Lineart", "Grayscale", "Color", 0
  };

static char *program_name;
static char *default_codec = "bin";
static char *default_outfile = "test_wire.out";

static int
usage (int code)
{
  if (code == 0)
    {
      printf ("Usage: %s [OPTION]...\n\
\n\
Test the SANE wire manipulation library.\n\
\n\
    --codec=CODEC        set the codec [default=%s]\n\
    --help               display this message and exit\n\
-o, --output=FILE        set the output file [default=%s]\n\
    --readonly           do not create FILE, just read it\n\
    --version            print version information\n\
\n\
Valid CODECs are: `ascii' `bin'\n",
	      program_name, default_codec, default_outfile);
    }
  else
    {
      fprintf (stderr, "Type ``%s --help'' for more information.\n",
	       program_name);
    }
  exit (code);
}


int
main (int argc, char ** argv)
{
  SANE_Option_Descriptor desc[2], *desc_ptr;
  SANE_Word len;
  char *codec = default_codec;
  char *outfile = default_outfile;
  int readonly = 0;

  program_name = argv[0];
  argv ++;
  while (*argv != 0)
    {
      if (!strcmp (*argv, "--codec"))
	{
	  if (argv[1] == 0)
	    {
	      fprintf (stderr, "%s: option `%s' requires an argument\n",
		       program_name, *argv);
	      usage (1);
	    }

	  argv ++;
	  codec = *argv;
	}
      else if (!strncmp (*argv, "--codec=", 8))
	{
	  codec = *argv + 8;
	}
      else if (!strcmp (*argv, "--help"))
	{
	  usage (0);
	}
      else if (!strcmp (*argv, "-o") || !strcmp (*argv, "--output"))
	{
	  if (argv[1] == 0)
	    {
	      fprintf (stderr, "%s: option `%s' requires an argument\n",
		       program_name, *argv);
	      usage (1);
	    }

	  argv ++;
	  outfile = *argv;
	}
      else if (!strncmp (*argv, "--output=", 9))
	{
	  outfile = *argv + 9;
	}
      else if (!strcmp (*argv, "--readonly"))
	{
	  readonly = 1;
	}
      else if (!strcmp (*argv, "--version"))
	{
	  printf ("test_wire (%s) %s\n", PACKAGE, VERSION);
	  exit (0);
	}
      else if (**argv == '-')
	{
	  fprintf (stderr, "%s: unrecognized option `%s'\n",
		   program_name, *argv);
	  usage (1);
	}
      else
	{
	  fprintf (stderr, "%s: too many arguments\n", program_name);
	}

      argv ++;
    }


  if (!strcmp (codec, "bin"))
    sanei_w_init (&w, sanei_codec_bin_init);
  else if (!strcmp (codec, "ascii"))
    sanei_w_init (&w, sanei_codec_ascii_init);
  else
    {
      fprintf (stderr, "%s: unknown codec type `%s'\n", program_name, codec);
      usage (1);
    }

  desc[0].name = "resolution";
  desc[0].title = 0;
  desc[0].desc = "Determines scan resolution in dots/inch (\"DPI\").";
  desc[0].type = SANE_TYPE_FIXED;
  desc[0].unit = SANE_UNIT_DPI;
  desc[0].size = sizeof (SANE_Word);
  desc[0].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
  desc[0].constraint_type = SANE_CONSTRAINT_WORD_LIST;
  desc[0].constraint.word_list = dpi_word_list;

  desc[1].name = "mode";
  desc[1].title ="Scan Mode";
  desc[1].desc = "Determines scan mode.";
  desc[1].type = SANE_TYPE_STRING;
  desc[1].unit = SANE_UNIT_NONE;
  desc[1].size = 10;
  desc[1].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT
		 | SANE_CAP_AUTOMATIC);
  desc[1].constraint_type = SANE_CONSTRAINT_STRING_LIST;
  desc[1].constraint.string_list = mode_list;

  {
    int flags;
    if (readonly)
      {
	printf ("reading %s output from %s\n", codec, outfile);
	flags = O_RDONLY;
      }
    else
      {
	printf ("creating %s\n", outfile);
	flags = O_RDWR | O_CREAT | O_TRUNC;
      }

    w.io.fd = open (outfile, flags, 0666);

    if (w.io.fd < 0)
      {
	perror (outfile);
	return -1;
      }
  }
  w.io.read  = read;
  w.io.write = readonly ? 0 : write;

  if (!readonly)
    {
      sanei_w_set_dir (&w, WIRE_ENCODE);
      w.status = 0;

      len = NELEMS(desc);
      desc_ptr = desc;
      sanei_w_array (&w, &len, (void **) &desc_ptr,
		     (WireCodecFunc) sanei_w_option_descriptor,
		     sizeof (desc[0]));

      if (w.status == 0)
	printf ("%s encode successful\n", codec);
      else
	fprintf (stderr, "%s: %s encode error %d: %s\n",
		 codec, program_name, w.status, strerror (w.status));

      printf ("%s output written to %s\n", codec, outfile);
    }

  sanei_w_set_dir (&w, WIRE_DECODE);
  w.status = 0;

  if (!readonly)
    {
      printf ("reading %s output from %s\n", codec, outfile);
      lseek (w.io.fd, 0, SEEK_SET);
    }

  sanei_w_array (&w, &len, (void **) &desc_ptr,
		 (WireCodecFunc) sanei_w_option_descriptor, sizeof (desc[0]));

  if (w.status == 0)
    printf ("%s decode successful\n", codec);
  else
    fprintf (stderr, "%s: %s decode error %d: %s\n",
	     program_name, codec, w.status, strerror (w.status));

  sanei_w_set_dir (&w, WIRE_FREE);
  w.status = 0;
  sanei_w_array (&w, &len, (void **) &desc_ptr,
		 (WireCodecFunc) sanei_w_option_descriptor, sizeof (desc[0]));

  if (w.status == 0)
    printf ("free successful\n");
  else
    fprintf (stderr, "%s: free error %d: %s\n",
	     program_name, w.status, strerror (w.status));

  close (w.io.fd);

  return 0;
}
