/* Copyright (C) 2001-2006 artofcode LLC.
   All Rights Reserved.
  
   This software is provided AS-IS with no warranty, either express or
   implied.

   This software is distributed under license and may not be copied, modified
   or distributed except as expressly authorized under the terms of that
   license.  Refer to licensing information at http://www.artifex.com/
   or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
   San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
*/

/* $Id: gdev4693.c 6651 2006-03-13 16:18:19Z stefan $*/
/* Driver for the Tektronix 4693d color plotter. */
#include "gdevprn.h"
#define prn_dev ((gx_device_printer *)dev) /* needed in 5.31 et seq */

/* Thanks to Karl Hakimian (hakimian@yoda.eecs.wsu.edu) */
/* for contributing this code to Aladdin Enterprises. */

#define X_DPI 100
#define Y_DPI 100
#define WIDTH_10THS 85
#define HEIGHT_10THS 110

private dev_proc_print_page(t4693d_print_page);
private dev_proc_map_rgb_color(gdev_t4693d_map_rgb_color);
private dev_proc_map_color_rgb(gdev_t4693d_map_color_rgb);

private gx_device_procs t4693d_procs =
	prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
		gdev_t4693d_map_rgb_color, gdev_t4693d_map_color_rgb);

#define t4693d_prn_device(name,depth,max_rgb) {prn_device_body( \
	gx_device_printer,t4693d_procs,name, \
	WIDTH_10THS, HEIGHT_10THS, X_DPI, Y_DPI, 0.25, 0.25, 0.25, 0.25, \
	3,depth,max_rgb,max_rgb,max_rgb + 1,max_rgb + 1, \
	t4693d_print_page)}

const gx_device_printer gs_t4693d2_device = t4693d_prn_device("t4693d2",8, 3);
const gx_device_printer gs_t4693d4_device = t4693d_prn_device("t4693d4",16, 15);
const gx_device_printer gs_t4693d8_device = t4693d_prn_device("t4693d8",24, 255);

private gx_color_index
gdev_t4693d_map_rgb_color(gx_device *dev, const gx_color_value cv[])
{
	ushort bitspercolor = prn_dev->color_info.depth / 3;
	ulong max_value = (1 << bitspercolor) - 1;

        gx_color_value r, g, b;
        r = cv[0]; g = cv[1]; b = cv[2];

	if (bitspercolor == 5) {
		bitspercolor--;
		max_value = (1 << bitspercolor) - 1;
	}

	return ((r*max_value/gx_max_color_value) << (bitspercolor*2)) +
		((g*max_value/gx_max_color_value) << bitspercolor) +
		(b*max_value/gx_max_color_value);
}

private int
gdev_t4693d_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
{
        ushort bitspercolor = prn_dev->color_info.depth / 3;
	ulong max_value = (1 << bitspercolor) - 1;

	if (bitspercolor == 5) {
		bitspercolor--;
		max_value = (1 << bitspercolor) - 1;
	}

	prgb[0] = (color >> (bitspercolor*2)) * gx_max_color_value / max_value;
	prgb[1] = ((color >> bitspercolor) & max_value) * gx_max_color_value / max_value;
	prgb[2] = (color & max_value) * gx_max_color_value / max_value;
	return(0);
}

private int
t4693d_print_page(gx_device_printer *dev, FILE *ps_stream)
{
	char header[32];
	int depth = prn_dev->color_info.depth;
	int line_size = gdev_mem_bytes_per_scan_line(prn_dev);
	byte *data = (byte *)gs_malloc(dev->memory, line_size, 1, "t4693d_print_page");
	char *p;
	ushort data_size = line_size/prn_dev->width;
	int checksum;
	int lnum;
	int i;
#if !arch_is_big_endian
	byte swap;
#endif

	if (data == 0) return_error(gs_error_VMerror);
	/* build header. */
	p = header;
	*p++ = (char)0x14;	/* Print request */
	*p++ = (char)0xc0|20;	/* Length of header */
	*p++ = (char)0xc0 | ((prn_dev->width >> 6)&0x3f);
	*p++ = (char)0x80 | (prn_dev->width&0x3f);
	*p++ = (char)0xc0 | ((prn_dev->height >> 6)&0x3f);
	*p++ = (char)0x80 | (prn_dev->height&0x3f);
	*p++ = (char)0xc1;	/* Handshake */
	*p++ = (char)0xc0;	/* Get number of prints from printer. */
	*p++ = (char)0xc0;	/* Get pixel shape from printer. */
	*p++ = (char)(depth == 8) ? 0xcb : (depth == 16) ? 0xcc : 0xcd;
	*p++ = (char)0xc1;	/* Pixel-data order 1. */
	*p++ = (char)0xc3;	/* Interpolate to maximum size. */
	*p++ = (char)0xc3;	/* Full color range 1. */
	*p++ = (char)0xc0;	/* Color conversion from printer. */
	*p++ = (char)0xc0;	/* Color manipulation from printer. */
	*p++ = (char)0xc0;	/* B/W inversion from printer. */
	*p++ = (char)0xc3;	/* Portrait mode centered. */
	*p++ = (char)0xc9;	/* Use printer default for media and printing. */
	*p++ = (char)0x95;
	*p++ = (char)0x81;

	for (checksum = 0, i = 0; &header[i] != p; i++)
		checksum += header[i];
		
	*p++ = ((checksum%128)&0x7f) | 0x80;
	*p = 0x02; /* end of line. */
	/* write header */
	if (fwrite(header,1,22,ps_stream) != 22) {
		errprintf("Could not write header (t4693d).\n");
		gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
		return_error(gs_error_ioerror);
	}

	for (lnum = 0; lnum < prn_dev->height; lnum++) {
		gdev_prn_copy_scan_lines(prn_dev,lnum,data,line_size);

		for (i = 0; i < line_size; i += data_size) {

			switch (depth) {
			case 8:
				data[i] &= 0x3f;
				break;
			case 16:
#if arch_is_big_endian
				data[i] &= 0x0f;
#else
				swap = data[i];
				data[i] = data[i + 1]&0x0f;
				data[i + 1] = swap;
#endif
				break;
			case 24:
				break;
			default:
				errprintf("Bad depth (%d) t4693d.\n",depth);
				gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
				return_error(gs_error_rangecheck);
			}

			if (fwrite(&data[i],1,data_size,ps_stream) != data_size) {
				errprintf("Could not write pixel (t4693d).\n");
				gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
				return_error(gs_error_ioerror);
			}

		}

		if (fputc(0x02,ps_stream) != 0x02) {
			errprintf("Could not write EOL (t4693d).\n");
			gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
			return_error(gs_error_ioerror);
		}

	}

	if (fputc(0x01,ps_stream) != 0x01) {
		errprintf("Could not write EOT (t4693d).\n");
		gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
		return_error(gs_error_ioerror);
	}

	gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
	return(0);
}
