/**
 * Copyright (C) 2001-2019 Artifex Software, Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ijs.h"
#include "ijs_client.h"

static int
example_list_params (IjsClientCtx *ctx)
{
  int status = 0;
  char buf[4096];
  char ebuf[4096];
  char *p;
  status = ijs_client_list_params (ctx, 0, buf, sizeof(buf) - 1);
  if (status >= 0)
    {
      buf[status] = 0;
      fprintf (stderr, "settable params: %s\n", buf);
      p = strtok(buf, ",");
      while (p)
        {
          status = ijs_client_enum_param (ctx, 0, p, ebuf, sizeof(ebuf) - 1);
          if (status >= 0)
            {
              ebuf[status] = 0;
              fprintf (stderr, "  %s: %s\n", p, ebuf);
            }
          else
            {
              fprintf (stderr, "Error %d getting param %s\n", status, p);
            }
          p = strtok(NULL, ",");
        }
    }
  else
    {
      fprintf (stderr, "Error %d listing params\n", status);
    }
  return status;
}

static int
send_pnm_file (IjsClientCtx *ctx, FILE *f, int xres, int yres)
{
  int width, height;
  char *lp, type;
  int total_bytes, bytes_left;
  int n_chan, bps;
  char buf[4096];
  int status = 0;

  lp = fgets (buf, sizeof(buf), f);
  if (lp == NULL)
    {
      fprintf (stderr, "error reading file\n");
      return 1;
    }
  if (lp[0] != 'P' || lp[1] < '4' || lp[1] > '6')
    {
      fprintf (stderr, "need pnmraw file\n");
      return 1;
    }
  type = lp[1];
  do
    {
      lp = fgets (buf, sizeof(buf), f);
    }
  while (lp != NULL && lp[0] == '#');
  if (sscanf (lp, "%d %d", &width, &height) != 2)
    {
      fprintf (stderr, "format error\n");
      return 1;
    }
  if (type >= '5')
    {
      /* skip depth */
      do
        {
          lp = fgets (buf, sizeof(buf), f);
        }
      while (lp != NULL && lp[0] == '#');
    }

  n_chan = (type == '6') ? 3 : 1;
  bps = (type == '4') ? 1 : 8;

  /* Set required parameters. Note: we should be checking the return
     values. */
  sprintf (buf, "%d", n_chan);
  ijs_client_set_param (ctx, 0, "NumChan", buf, strlen (buf));
  sprintf (buf, "%d", bps);
  ijs_client_set_param (ctx, 0, "BitsPerSample", buf, strlen (buf));
  strcpy (buf, (n_chan == 3) ? "DeviceRGB" : "DeviceGray");
  ijs_client_set_param (ctx, 0, "ColorSpace", buf, strlen (buf));
  sprintf (buf, "%d", width);
  ijs_client_set_param (ctx, 0, "Width", buf, strlen (buf));
  sprintf (buf, "%d", height);
  ijs_client_set_param (ctx, 0, "Height", buf, strlen (buf));
  sprintf (buf, "%dx%d", xres, yres);
  ijs_client_set_param (ctx, 0, "Dpi", buf, strlen (buf));

  ijs_client_begin_page (ctx, 0);

  total_bytes = ((n_chan * bps * width + 7) >> 3) * height;
  bytes_left = total_bytes;
  while (bytes_left)
    {
      int n_bytes = bytes_left;
      if (n_bytes > sizeof(buf))
        n_bytes = sizeof(buf);
      fread (buf, 1, n_bytes, f); /* todo: check error */
      if (type == '4')
        {
          /* invert pbm so black is 0, as per DeviceGray color space */
          int i;
          for (i = 0; i < n_bytes; i++)
            buf[i] ^= 0xff;
        }
      status = ijs_client_send_data_wait (ctx, 0, buf, n_bytes);
      if (status)
        break;
      bytes_left -= n_bytes;
    }

  ijs_client_end_page (ctx, 0);

  return status;
}

static void
verify_context (IjsClientCtx *ctx)
{
  if (ctx == NULL)
    {
      fprintf (stderr, "Must specify valid server with -s flag\n");
      exit (1);
    }
}

static void
param_usage (void)
{
  fprintf (stderr, "parameter list must be in key=value, key=value format\n");
}

static void
example_set_params (IjsClientCtx *ctx, const char *arg)
{
  int code;
  int i, inext;
  char key[256];
  char buf[4096];
  int buf_ix;

  for (i = 0; arg[i] != 0; i = inext)
    {
      int ibeg, ieq, iend;
      int key_size;

      for (ibeg = i; arg[ibeg] == ' '; ibeg++);

      for (ieq = ibeg; arg[ieq] != 0; ieq++)
        {
          if (arg[ieq] == '=')
            break;
        }
      if (arg[ieq] == 0)
        {
          param_usage ();
          return;
        }
      for (iend = ieq; iend >= ibeg; iend--)
        if (arg[iend - 1] != ' ')
          break;
      if (iend == ibeg)
        {
          param_usage ();
          return;
        }
      key_size = iend - ibeg;
      if (key_size + 1 > sizeof(key))
        {
          fprintf (stderr, "Key exceeds %d bytes\n", sizeof(key));
          return;
        }
      memcpy (key, arg + ibeg, key_size);
      key[key_size] = 0;
      buf_ix = 0;
      for (i = ieq + 1; arg[i] == ' '; i++);
      for (; arg[i] != 0; i++)
        {
          if (arg[i] == ',')
            break;
          if (buf_ix == sizeof(buf))
            {
              fprintf (stderr, "Value for %s exceeds %d bytes\n",
                       key, sizeof(buf));
              return;
            }
          if (arg[i] == '\\' && arg[i + 1] != 0)
            buf[buf_ix++] = arg[++i];
          else
            buf[buf_ix++] = arg[i];
        }
      if (arg[i] == ',')
        inext = i + 1;
      else
        inext = i;
      code = ijs_client_set_param (ctx, 0, key, buf, buf_ix);
      if (code < 0)
        fprintf (stderr, "Warning: error %d setting parameter %s\n",
                 code, key);
    }
}

static void
example_get_param (IjsClientCtx *ctx, const char *arg)
{
  char buf[4096];
  int status;

  status = ijs_client_get_param (ctx, 0, arg, buf, sizeof(buf) - 1);
  if (status >= 0)
    {
      buf[status] = 0;
      fprintf (stderr, "value of param %s = %s\n", arg, buf);
    }
  else
    {
      fprintf (stderr, "Error %d getting param %s\n", status, arg);
    }
}

static void
example_enum_param (IjsClientCtx *ctx, const char *arg)
{
  char buf[4096];
  int status;

  status = ijs_client_enum_param (ctx, 0, arg, buf, sizeof(buf) - 1);
  if (status >= 0)
    {
      buf[status] = 0;
      fprintf (stderr, "enumeration of param %s: %s\n", arg, buf);
    }
  else
    {
      fprintf (stderr, "Error %d getting param %s\n", status, arg);
    }
}

static const char *
get_arg (int argc, char **argv, int *pi, const char *arg)
{
  if (arg[0] != 0)
    return arg;
  else
    {
      (*pi)++;
      if (*pi == argc)
        return NULL;
      else
        return argv[*pi];
    }
}

int
main (int argc, char **argv)
{
  IjsClientCtx *ctx;
  int i;
  int xres = 300, yres = 300;

  ctx = NULL;

  for (i = 1; i < argc; i++)
    {
      const char *arg = argv[i];

      if (arg[0] == '-')
        {
          switch (arg[1])
            {
            case  'r':
              {
                char *tail;

                arg = get_arg (argc, argv, &i, arg + 2);
                xres = strtol (arg, &tail, 10);
                if (tail[0] == 0)
                  yres = xres;
                else if (tail[0] == 'x')
                  yres = strtol (tail + 1, &tail, 10);
              }
              break;
            case 's':
              arg = get_arg (argc, argv, &i, arg + 2);
              ctx = ijs_invoke_server (arg);
              if (!ctx) {
                fprintf (stderr, "ijs_invoke_server %s failed\n", arg);
                return 1;
              }
              ijs_client_open (ctx);
              ijs_client_begin_job (ctx, 0);
              break;
            case 'p':
              arg = get_arg (argc, argv, &i, arg + 2);
              verify_context (ctx);
              example_set_params (ctx, arg);
              break;
            case 'g':
              arg = get_arg (argc, argv, &i, arg + 2);
              verify_context (ctx);
              example_get_param (ctx, arg);
              break;
            case 'e':
              arg = get_arg (argc, argv, &i, arg + 2);
              verify_context (ctx);
              example_enum_param (ctx, arg);
              break;
            case 'l':
              verify_context (ctx);
              example_list_params (ctx);
              break;
            case 0:
              verify_context (ctx);
              send_pnm_file (ctx, stdin, xres, yres);
              break;
            }
        }
      else
        {
          FILE *f = fopen (arg, "rb");

          if (f == NULL)
            {
              fprintf (stderr, "error opening %s\n", arg);
              return 1;
            }
          verify_context (ctx);
          send_pnm_file (ctx, f, xres, yres);
          fclose (f);
        }
    }

  verify_context (ctx);

  ijs_client_end_job (ctx, 0);
  ijs_client_close (ctx);

  /* todo - suffield race, proceduralize */
  ijs_client_begin_cmd (ctx, IJS_CMD_EXIT);
  ijs_client_send_cmd_wait (ctx);

  return 0;
}
