/* Copyright (C) 1999 artofcode LLC.  All rights reserved.
  
  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.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gdevpxut.c,v 1.2.6.1.2.1 2003/01/17 00:49:01 giles Exp $ */
/* Utilities for PCL XL generation */
#include "math_.h"
#include "string_.h"
#include "gx.h"
#include "stream.h"
#include "gxdevcli.h"
#include "gdevpxat.h"
#include "gdevpxen.h"
#include "gdevpxop.h"
#include "gdevpxut.h"

/* ---------------- High-level constructs ---------------- */

/* Write the file header, including the resolution. */
int
px_write_file_header(stream *s, const gx_device *dev)
{
    static const char *const file_header =
	"\033%-12345X@PJL ENTER LANGUAGE = PCLXL\n\
) HP-PCL XL;1;1;Comment Copyright Aladdin Enterprises 1996\000\n";
    static const byte stream_header[] = {
	DA(pxaUnitsPerMeasure),
	DUB(0), DA(pxaMeasure),
	DUB(eBackChAndErrPage), DA(pxaErrorReport),
	pxtBeginSession,
	DUB(0), DA(pxaSourceType),
	DUB(eBinaryLowByteFirst), DA(pxaDataOrg),
	pxtOpenDataSource
    };

    /* We have to add 2 to the strlen because the next-to-last */
    /* character is a null. */
    px_put_bytes(s, (const byte *)file_header,
		 strlen(file_header) + 2);
    px_put_usp(s, (uint) (dev->HWResolution[0] + 0.5),
	       (uint) (dev->HWResolution[1] + 0.5));
    PX_PUT_LIT(s, stream_header);
    return 0;
}

/* Write the page header, including orientation. */
int
px_write_page_header(stream *s, const gx_device *dev)
{
    static const byte page_header_1[] = {
	DUB(ePortraitOrientation), DA(pxaOrientation)
    };

    PX_PUT_LIT(s, page_header_1);
    return 0;
}

/* Write the media selection command if needed, updating the media size. */
int
px_write_select_media(stream *s, const gx_device *dev, pxeMediaSize_t *pms)
{
#define MSD(ms, res, w, h)\
  { ms, (w) * 1.0 / (res), (h) * 1.0 / res },
    static const struct {
	pxeMediaSize_t ms;
	float width, height;
    } media_sizes[] = {
	px_enumerate_media(MSD)
	{ pxeMediaSize_next }
    };
#undef MSD
    float w = dev->width / dev->HWResolution[0],
	h = dev->height / dev->HWResolution[1];
    int i;
    pxeMediaSize_t size;

    /* The default is eLetterPaper, media size 0. */
    for (i = countof(media_sizes) - 2; i > 0; --i)
	if (fabs(media_sizes[i].width - w) < 5.0 / 72 &&
	    fabs(media_sizes[i].height - h) < 5.0 / 72
	    )
	    break;
    size = media_sizes[i].ms;
    /*
     * According to the PCL XL documentation, MediaSize must always
     * be specified, but MediaSource is optional.
     */
    px_put_uba(s, size, pxaMediaSize);
    if (!pms || size != *pms) {
	static const byte page_header_2[] = {
	    DUB(eAutoSelect), DA(pxaMediaSource)
	};

	PX_PUT_LIT(s, page_header_2);
	if (pms)
	    *pms = size;
    }
    return 0;
}

/*
 * Write the file trailer.  Note that this takes a FILE *, not a stream *,
 * since it may be called after the stream is closed.
 */
int
px_write_file_trailer(FILE *file)
{
    static const byte file_trailer[] = {
	pxtCloseDataSource,
	pxtEndSession,
	033, '%', '-', '1', '2', '3', '4', '5', 'X'
    };

    fwrite(file_trailer, 1, sizeof(file_trailer), file);
    return 0;
}

/* ---------------- Low-level data output ---------------- */

/* Write a sequence of bytes. */
void
px_put_bytes(stream * s, const byte * data, uint count)
{
    uint used;

    sputs(s, data, count, &used);
}

/* Utilities for writing data values. */
/* H-P printers only support little-endian data, so that's what we emit. */
void
px_put_a(stream * s, px_attribute_t a)
{
    sputc(s, pxt_attr_ubyte);
    sputc(s, a);
}
void
px_put_ac(stream *s, px_attribute_t a, px_tag_t op)
{
    px_put_a(s, a);
    sputc(s, op);
}

void
px_put_ub(stream * s, byte b)
{
    sputc(s, pxt_ubyte);
    sputc(s, b);
}
void
px_put_uba(stream *s, byte b, px_attribute_t a)
{
    px_put_ub(s, b);
    px_put_a(s, a);
}

void
px_put_s(stream * s, uint i)
{
    sputc(s, (byte) i);
    sputc(s, (byte) (i >> 8));
}
void
px_put_us(stream * s, uint i)
{
    sputc(s, pxt_uint16);
    px_put_s(s, i);
}
void
px_put_usa(stream *s, uint i, px_attribute_t a)
{
    px_put_us(s, i);
    px_put_a(s, a);
}
void
px_put_u(stream * s, uint i)
{
    if (i <= 255)
	px_put_ub(s, i);
    else
	px_put_us(s, i);
}

void
px_put_usp(stream * s, uint ix, uint iy)
{
    spputc(s, pxt_uint16_xy);
    px_put_s(s, ix);
    px_put_s(s, iy);
}
void
px_put_usq_fixed(stream * s, fixed x0, fixed y0, fixed x1, fixed y1)
{
    spputc(s, pxt_uint16_box);
    px_put_s(s, fixed2int(x0));
    px_put_s(s, fixed2int(y0));
    px_put_s(s, fixed2int(x1));
    px_put_s(s, fixed2int(y1));
}

void
px_put_ss(stream * s, int i)
{
    sputc(s, pxt_sint16);
    px_put_s(s, (uint) i);
}
void
px_put_ssp(stream * s, int ix, int iy)
{
    sputc(s, pxt_sint16_xy);
    px_put_s(s, (uint) ix);
    px_put_s(s, (uint) iy);
}

void
px_put_l(stream * s, ulong l)
{
    px_put_s(s, (uint) l);
    px_put_s(s, (uint) (l >> 16));
}

void
px_put_r(stream * s, floatp r)
{				/* Convert to single-precision IEEE float. */
    int exp;
    long mantissa = (long)(frexp(r, &exp) * 0x1000000);

    if (exp < -126)
	mantissa = 0, exp = 0;	/* unnormalized */
    if (mantissa < 0)
	exp += 128, mantissa = -mantissa;
    /* All quantities are little-endian. */
    spputc(s, (byte) mantissa);
    spputc(s, (byte) (mantissa >> 8));
    spputc(s, (byte) (((exp + 127) << 7) + ((mantissa >> 16) & 0x7f)));
    spputc(s, (exp + 127) >> 1);
}
void
px_put_rl(stream * s, floatp r)
{
    spputc(s, pxt_real32);
    px_put_r(s, r);
}

void
px_put_data_length(stream * s, uint num_bytes)
{
    if (num_bytes > 255) {
	spputc(s, pxt_dataLength);
	px_put_l(s, (ulong) num_bytes);
    } else {
	spputc(s, pxt_dataLengthByte);
	spputc(s, (byte) num_bytes);
    }
}
