/** @file spec.c
 * Thumbnail specification handling
 * @author Marko Mäkelä <msmakela@nic.funet.fi>
 */

/* Copyright © 2003 Marko Mäkelä.

   This file is part of PHOTOMOLO, a program for generating
   thumbnail images and HTML files for browsing digital photographs.

   PHOTOMOLO is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   PHOTOMOLO is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "spec.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/** parse a thumbnail specification
 * @param spec	the specification string
 * @return	the corresponding specification, or NULL on parse error
 */
struct thumb_spec*
parse_spec (const char* spec)
{
  const char* c = strchr (spec, '/');
  char* d;
  struct thumb_spec* thumb;
  if (!c) {
    fputs (spec, stderr);
    fputs (": expected dirname/s[248] or dirname/([hw][0-9]+)*\n", stderr);
    return 0;
  }
  if (c == spec ||
      ((c - spec) <= 2 && *spec == '.' &&
       ((c - spec) == 1 || spec[1] == '.'))) {
    fputs (spec, stderr);
    fputs (": thumbnail directory cannot be '', '.', or '..'\n", stderr);
    return 0;
  }

  /* allocate the structure and copy the directory name */
  thumb = calloc (1, (c - spec) + sizeof *thumb);
  if (!thumb) {
    fputs ("out of memory\n", stderr);
    return 0;
  }
  memcpy (thumb->thumbdir, spec, c - spec);
  thumb->thumbdir[c++ - spec] = 0;

  while (*c) {
    switch (*c) {
    default:
      fputs (spec, stderr);
      fputs (": parse error at `", stderr);
      fputs (c, stderr);
      fputs ("'\n", stderr);
    error:
      free (thumb);
      return 0;
    case 'w':
      if (thumb->shrink) {
      excl:
	fputs (spec, stderr);
	fputs (": 's' is mutually exclusive with 'w' or 'h'\n", stderr);
	goto error;
      }
      if (thumb->max_width) {
	fputs (spec, stderr);
	fputs (": at most one 'w', please\n", stderr);
	goto error;
      }
      thumb->max_width = strtoul (c + 1, &d, 0);
      if (thumb->max_width == 0 || thumb->max_width > 32767) {
	fputs ("maximum width must be w1..w32767\n", stderr);
	goto error;
      }
      c = d;
      break;
    case 'h':
      if (thumb->shrink)
	goto excl;
      if (thumb->max_height) {
	fputs (spec, stderr);
	fputs (": at most one 'h', please\n", stderr);
	goto error;
      }
      thumb->max_height = strtoul (c + 1, &d, 0);
      if (thumb->max_height == 0 || thumb->max_height > 32767) {
	fputs ("maximum height must be h1..h32767\n", stderr);
	goto error;
      }
      c = d;
      break;
    case 's':
      if (thumb->max_width || thumb->max_height)
	goto excl;
      if (thumb->shrink) {
	fputs (spec, stderr);
	fputs (": at most one 's', please\n", stderr);
	goto error;
      }
      thumb->shrink = strtoul (c + 1, &d, 0);
      c = d;
      switch (thumb->shrink) {
      case 2:
      case 4:
      case 8:
	break;
      default:
	fputs (spec, stderr);
	fputs (": shrink factor must be s2, s4 or s8\n", stderr);
	goto error;
      }
      break;
    }
  }

  if (!thumb->shrink && !thumb->max_width && !thumb->max_height) {
    fputs (spec, stderr);
    fputs (": shrink factor (s), width (w) or height (h) expected\n",
	   stderr);
    goto error;
  }

  return thumb;
}

/** convert a thumbnail specification to a string
 * @param spec	the specification string
 * @return	text string describing the specification
 */
const char*
specstr (const struct thumb_spec* spec)
{
  /* this buffer is long enough, as the dimensions are limited by parse_spec */
  static char str[13];
  if (spec->shrink)
    sprintf (str, "1/%u", spec->shrink);
  else if (spec->max_width && spec->max_height)
    sprintf (str, "%u\327%u", spec->max_width, spec->max_height);
  else if (spec->max_width)
    sprintf (str, "%u\327y", spec->max_width);
  else
    sprintf (str, "x\327%u", spec->max_height);
  return str;
}
