/*
 * Threaded test program for CUPS.
 *
 * Copyright © 2020-2024 by OpenPrinting.
 * Copyright © 2012-2019 by Apple Inc.
 *
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more
 * information.
 */

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <errno.h>
#include <cups/cups.h>
#include <cups/thread-private.h>


/*
 * Local functions...
 */

static int	enum_dests_cb(void *_name, unsigned flags, cups_dest_t *dest);
static void	*run_query(cups_dest_t *dest);
static void	show_supported(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value);


/*
 * 'main()' - Main entry.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line arguments */
     char *argv[])			/* I - Command-line arguments */
{
 /*
  * Go through all the available destinations to find the requested one...
  */

  (void)argc;

  cupsEnumDests(CUPS_DEST_FLAGS_NONE, -1, NULL, 0, 0, enum_dests_cb, argv[1]);

  return (0);
}


/*
 * 'enum_dests_cb()' - Destination enumeration function...
 */

static int				/* O - 1 to continue, 0 to stop */
enum_dests_cb(void        *_name,	/* I - Printer name, if any */
              unsigned    flags,	/* I - Enumeration flags */
              cups_dest_t *dest)	/* I - Found destination */
{
  const char		*name = (const char *)_name;
					/* Printer name */
  cups_dest_t		*cdest;		/* Copied destination */


  (void)flags;

 /*
  * If a name was specified, compare it...
  */

  if (name && strcasecmp(name, dest->name))
    return (1);				/* Continue */

 /*
  * Copy the destination and run the query on a separate thread...
  */

  cupsCopyDest(dest, 0, &cdest);
  _cupsThreadWait(_cupsThreadCreate((_cups_thread_func_t)run_query, cdest));

  cupsFreeDests(1, cdest);

 /*
  * Continue if no name was specified or the name matches...
  */

  return (!name || !strcasecmp(name, dest->name));
}


/*
 * 'run_query()' - Query printer capabilities on a separate thread.
 */

static void *				/* O - Return value (not used) */
run_query(cups_dest_t *dest)		/* I - Destination to query */
{
  http_t	*http;			/* Connection to destination */
  cups_dinfo_t	*dinfo;			/* Destination info */
  unsigned	dflags = CUPS_DEST_FLAGS_NONE;
					/* Destination flags */


  if ((http = cupsConnectDest(dest, dflags, 300, NULL, NULL, 0, NULL, NULL)) == NULL)
  {
    printf("testthreads: Unable to connect to destination \"%s\": %s\n", dest->name, cupsLastErrorString());
    return (NULL);
  }

  if ((dinfo = cupsCopyDestInfo(http, dest)) == NULL)
  {
    printf("testdest: Unable to get information for destination \"%s\": %s\n", dest->name, cupsLastErrorString());
    return (NULL);
  }

  printf("\n%s:\n", dest->name);

  show_supported(http, dest, dinfo, NULL, NULL);

  return (NULL);
}



/*
 * 'show_supported()' - Show supported options, values, etc.
 */

static void
show_supported(http_t       *http,	/* I - Connection to destination */
	       cups_dest_t  *dest,	/* I - Destination */
	       cups_dinfo_t *dinfo,	/* I - Destination information */
	       const char   *option,	/* I - Option, if any */
	       const char   *value)	/* I - Value, if any */
{
  ipp_attribute_t	*attr;		/* Attribute */
  int			i,		/* Looping var */
			count;		/* Number of values */


  if (!option)
  {
    attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
    if (attr)
    {
      count = ippGetCount(attr);
      for (i = 0; i < count; i ++)
        show_supported(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
    }
    else
    {
      static const char * const options[] =
      {					/* List of standard options */
        CUPS_COPIES,
	CUPS_FINISHINGS,
	CUPS_MEDIA,
	CUPS_NUMBER_UP,
	CUPS_ORIENTATION,
	CUPS_PRINT_COLOR_MODE,
	CUPS_PRINT_QUALITY,
	CUPS_SIDES
      };

      puts("    No job-creation-attributes-supported attribute, probing instead.");

      for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
        if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
	  show_supported(http, dest, dinfo, options[i], NULL);
    }
  }
  else if (!value)
  {
    printf("    %s (%s - %s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option), cupsCheckDestSupported(http, dest, dinfo, option, NULL) ? "supported" : "not-supported");

    if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
    {
      count = ippGetCount(attr);

      switch (ippGetValueTag(attr))
      {
        case IPP_TAG_INTEGER :
	    for (i = 0; i < count; i ++)
              printf("        %d\n", ippGetInteger(attr, i));
	    break;

        case IPP_TAG_ENUM :
	    for (i = 0; i < count; i ++)
	    {
	      int val = ippGetInteger(attr, i);
	      char valstr[256];

              snprintf(valstr, sizeof(valstr), "%d", val);
              printf("        %s (%s)\n", ippEnumString(option, ippGetInteger(attr, i)), cupsLocalizeDestValue(http, dest, dinfo, option, valstr));
            }
	    break;

        case IPP_TAG_RANGE :
	    for (i = 0; i < count; i ++)
	    {
	      int upper, lower = ippGetRange(attr, i, &upper);

              printf("        %d-%d\n", lower, upper);
	    }
	    break;

        case IPP_TAG_RESOLUTION :
	    for (i = 0; i < count; i ++)
	    {
	      int xres, yres;
	      ipp_res_t units;
	      xres = ippGetResolution(attr, i, &yres, &units);

              if (xres == yres)
                printf("        %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
	      else
                printf("        %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
	    }
	    break;

	case IPP_TAG_KEYWORD :
	    for (i = 0; i < count; i ++)
              printf("        %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
	    break;

	case IPP_TAG_TEXTLANG :
	case IPP_TAG_NAMELANG :
	case IPP_TAG_TEXT :
	case IPP_TAG_NAME :
	case IPP_TAG_URI :
	case IPP_TAG_URISCHEME :
	case IPP_TAG_CHARSET :
	case IPP_TAG_LANGUAGE :
	case IPP_TAG_MIMETYPE :
	    for (i = 0; i < count; i ++)
              printf("        %s\n", ippGetString(attr, i, NULL));
	    break;

        case IPP_TAG_STRING :
	    for (i = 0; i < count; i ++)
	    {
	      int j, len;
	      unsigned char *data = ippGetOctetString(attr, i, &len);

              fputs("        ", stdout);
	      for (j = 0; j < len; j ++)
	      {
	        if (data[j] < ' ' || data[j] >= 0x7f)
		  printf("<%02X>", data[j]);
		else
		  putchar(data[j]);
              }
              putchar('\n');
	    }
	    break;

        case IPP_TAG_BOOLEAN :
	    break;

        default :
	    printf("        %s\n", ippTagString(ippGetValueTag(attr)));
	    break;
      }
    }

  }
  else if (cupsCheckDestSupported(http, dest, dinfo, option, value))
    puts("YES");
  else
    puts("NO");
}
