/*
   pspresent: PostScript presentation tool
   ps.c: Parsing of PostScript Document Structuring Convention
   Copyright (C) Matthew Chapman 2001-2003

   This program 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 of the License, or
   (at your option) any later version.

   This program 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "pspresent.h"

#include <assert.h>

#define DSC_END_COMMENTS   "%%EndComments"
#define DSC_BOUNDING_BOX   "%%BoundingBox: "
#define DSC_ORIENTATION    "%%Orientation: "
#define DSC_PAGES          "%%Pages: "
#define DSC_PAGE           "%%Page: "
#define DSC_BEGIN_DOCUMENT "%%BeginDocument"
#define DSC_END_DOCUMENT   "%%EndDocument"
#define DSC_TRAILER        "%%Trailer"
#define LEN(str)           (sizeof(str)-1)

static char *PSGetLine(char **p, char *end)
{
	char *next = *p;

	if (next != NULL)
	{
		*p = memchr(next, '\n', end-next);
		if (*p != NULL)
			(*p)++;
	}

	return next;
}

static Bool PSGetBoundingBox(char *string, int bounds[4])
{
	char *p;

	bounds[0] = strtol(string, &p, 10);
	if (*p != ' ')
		return False;

	bounds[1] = strtol(p+1, &p, 10);
	if (*p != ' ')
		return False;

	bounds[2] = strtol(p+1, &p, 10);
	if (*p != ' ')
		return False;

	bounds[3] = strtol(p+1, &p, 10);
	if ((*p != '\n') && (*p != 0))
		return False;

	return True;
}

Bool PSGetOrientation(char *string, int *orientation)
{
	if (!strncmp(string, "Portrait", 8))
		*orientation = Portrait;
	else if (!strncmp(string, "Landscape", 9))
		*orientation = Landscape;
	else if (!strncmp(string, "Upside-Down", 11))
		*orientation = UpsideDown;
	else if (!strncmp(string, "Seascape", 8))
		*orientation = Seascape;
	else
		return False;

	return True;
}

Bool PSScanDocument(char *start, char *end, int bounds[4], int *orientation, 
		    int *num_pages, struct pspage **ret_pages,
		    int *num_real_pages, int **ret_real_pages)
{
	char *line;
	Bool gotBB = False, gotOR = False;
	int pages_size = 0, page = 0, real_page = 0;
	int eps_level = 0, this_page, last_page = 0;
	struct pspage *pages;
	int *real_pages;

	if ((start[0] != '%') || (start[1] != '!'))
	{
		fprintf(stderr, "ERROR: Not a PostScript file\n");
		return False;
	}

	while ((line = PSGetLine(&start, end)) != NULL)
	{
		if ((line[0] != '%')
		    || !strncmp(line, DSC_END_COMMENTS, LEN(DSC_END_COMMENTS)))
		{
			break;
		}
		else if (!strncmp(line, DSC_BOUNDING_BOX, LEN(DSC_BOUNDING_BOX)))
		{
			gotBB = PSGetBoundingBox(line + LEN(DSC_BOUNDING_BOX), bounds);
		}
		else if (!strncmp(line, DSC_ORIENTATION, LEN(DSC_ORIENTATION)))
		{
			gotOR = PSGetOrientation(line + LEN(DSC_ORIENTATION), orientation);
		}
		else if (!strncmp(line, DSC_PAGES, LEN(DSC_PAGES)))
		{
			pages_size = atoi(line + LEN(DSC_PAGES)) + 1;
		}
	}

	if (!gotBB)
	{
		fprintf(stderr, "ERROR: No bounding box information found\n");
		return False;
	}

	if (!gotOR)
		*orientation = Portrait;

	if (pages_size == 0)
		pages_size = 100;

	pages = malloc(pages_size * sizeof(struct pspage));
	real_pages = malloc(pages_size * sizeof(int));

	while ((line = PSGetLine(&start, end)) != NULL)
	{
		if (line[0] != '%')
		{
		}
		else if (!strncmp(line, DSC_BEGIN_DOCUMENT, LEN(DSC_BEGIN_DOCUMENT)))
		{
			eps_level++;
		}
		else if (!strncmp(line, DSC_END_DOCUMENT, LEN(DSC_END_DOCUMENT)))
		{
			eps_level--;
		}
		else if (!strncmp(line, DSC_PAGE, LEN(DSC_PAGE)) && (eps_level == 0))
		{
			this_page = atoi(line + LEN(DSC_PAGE));
			if ((page != 0) && (this_page != last_page))
			{
				pages[page-1].is_last = 1;
				real_pages[real_page++] = page-1;
			}
			last_page = this_page;
			pages[page].data = line;
			pages[page].is_last = 0;
			if (++page == pages_size)
			{
				pages_size += 100;
				pages = realloc(pages, pages_size * sizeof(struct pspage));
				real_pages = realloc(real_pages, pages_size * sizeof(int));
			}
		}
		else if (!strncmp(line, DSC_TRAILER, LEN(DSC_TRAILER)) && 
			 (eps_level == 0))
		{
			if (page != 0)
			{
				pages[page-1].is_last = 1;
				real_pages[real_page++] = page-1;
			}
			pages[page].data = line;
			pages[page].is_last = 0;
			break;
		}
	}

	if (page == 0)
	{
		fprintf(stderr, "ERROR: Document does not follow PostScript structuring convention\n");
		free(pages);
		free(real_pages);
		return False;
	}

	*num_pages = page;
	*ret_pages = pages;
	*num_real_pages = real_page;
	*ret_real_pages = real_pages;
	return True;
}

