/*
 * Copyright (C) 19896 Lorens Younes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * Lorens Younes BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Lorens Younes shall not be
 * used in advertising or otherwise to promote the sale, use or other dealings
 * in this Software without prior written authorization from Lorens Younes.
 */

/*****************************************************************************\
* amigax.c:                                                                   *
*                                                                             *
*  XPM library                                                                *
*  Emulates some Xlib functionality for Amiga.                                *
*                                                                             *
*  Developed by Lorens Younes (d93-hyo@nada.kth.se) 7/95                      *
*  Revised 4/96                                                               *
\*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "XpmI.h"
#include "amigax.h"

#include <graphics/gfxbase.h>
#include <intuition/screens.h>

#include <proto/exec.h>


static struct RastPort *
AllocRastPort (unsigned int, unsigned int, unsigned int);
static void
FreeRastPort (struct RastPort *, unsigned int,unsigned int);


static struct RastPort *
AllocRastPort (
    unsigned int   width,
    unsigned int   height,
    unsigned int   depth)
{
    struct RastPort  *rp;

    rp = XpmMalloc (sizeof (*rp));
    if (rp != NULL)
    {
	InitRastPort (rp);
	if (GfxBase->LibNode.lib_Version >= 39)
	{
	    rp->BitMap = AllocBitMap (width, height, depth, BMF_CLEAR, NULL);
	    if (rp->BitMap == NULL)
	    {
		FreeRastPort (rp, width, height);
		return NULL;
	    }
	}
	else
	{
	    unsigned int   i;

	    rp->BitMap = XpmMalloc (sizeof (*rp->BitMap));
	    if (rp->BitMap == NULL)
	    {
		FreeRastPort (rp, width, height);
		return NULL;
	    }

	    InitBitMap (rp->BitMap, depth, width, height);
	    for (i = 0; i < depth; ++i)
		rp->BitMap->Planes[i] = NULL;
	    for (i = 0; i < depth; ++i)
	    {
		rp->BitMap->Planes[i] = (PLANEPTR)AllocRaster (width, height);
		if (rp->BitMap->Planes[i] == NULL)
		{
		    FreeRastPort (rp, width, height);
		    return NULL;
		}
	    }
	}
    }

    return rp;
}


static void
FreeRastPort (
    struct RastPort  *rp,
    unsigned int      width,
    unsigned int      height)
{
    if (rp != NULL)
    {
	if (rp->BitMap != NULL)
	{
	    WaitBlit ();
	    if (GfxBase->LibNode.lib_Version >= 39)
		FreeBitMap (rp->BitMap);
	    else
	    {
		unsigned int   i;

		for (i = 0; i < rp->BitMap->Depth; ++i)
		{
		    if (rp->BitMap->Planes[i] != NULL)
			FreeRaster (rp->BitMap->Planes[i], width, height);
		}
		XpmFree (rp->BitMap);
	    }
	}
	XpmFree (rp);
    }
}


XImage *
AllocXImage (
    unsigned int   width,
    unsigned int   height,
    unsigned int   depth)
{
    XImage  *img;

    img = XpmMalloc (sizeof (*img));
    if (img != NULL)
    {
	img->width = width;
	img->height = height;
	img->rp = AllocRastPort (img->width, img->height, depth);
	if (img->rp == NULL)
	{
	    FreeXImage (img);
	    return NULL;
	}
    }

    return img;
}


int
FreeXImage (
    XImage  *ximage)
{
    if (ximage != NULL)
    {
	FreeRastPort (ximage->rp, ximage->width, ximage->height);
	XpmFree (ximage);
    }

    return Success;
}


int
XPutPixel (
    XImage         *ximage,
    int             x,
    int             y,
    unsigned long   pixel)
{
    SetAPen (ximage->rp, pixel);
    WritePixel (ximage->rp, x, y);

    return Success;
}


Status
AllocBestPen (
    Colormap        colormap,
    XColor         *screen_in_out,
    unsigned long   precision,
    Bool            fail_if_bad)
{
    if (GfxBase->LibNode.lib_Version >= 39)
    {
	unsigned long   r, g, b;

	r = screen_in_out->red * 0x00010001;
	g = screen_in_out->green * 0x00010001;
	b = screen_in_out->blue * 0x00010001;
	screen_in_out->pixel = ObtainBestPen (colormap, r, g, b,
					      OBP_Precision, precision,
					      OBP_FailIfBad, fail_if_bad,
					      TAG_DONE);
	if (screen_in_out->pixel == -1)
	    return False;

	QueryColor (colormap, screen_in_out);
    }
    else
    {
	XColor   nearest, trial;
	long     nearest_delta, trial_delta;
	int      num_cells, i;

	num_cells = colormap->Count;
	nearest.pixel = 0;
	QueryColor (colormap, &nearest);
	nearest_delta = ((((screen_in_out->red >> 8) - (nearest.red >> 8))
			  * ((screen_in_out->red >> 8) - (nearest.red >> 8)))
			 +
			 (((screen_in_out->green >> 8) - (nearest.green >> 8))
			  * ((screen_in_out->green >> 8) - (nearest.green >> 8)))
			 +
			 (((screen_in_out->blue >> 8) - (nearest.blue >> 8))
			  * ((screen_in_out->blue >> 8) - (nearest.blue >> 8))));
	for (i = 1; i < num_cells; i++)
	{
	/* precision and fail_if_bad is ignored under pre V39 */
	    trial.pixel = i;
	    QueryColor (colormap, &trial);
	    trial_delta = ((((screen_in_out->red >> 8) - (trial.red >> 8))
			    * ((screen_in_out->red >> 8) - (trial.red >> 8)))
			   +
			   (((screen_in_out->green >> 8) - (trial.green >> 8))
			    * ((screen_in_out->green >> 8) - (trial.green >> 8)))
			   +
			   (((screen_in_out->blue >> 8) - (trial.blue >> 8))
			    * ((screen_in_out->blue >> 8) - (trial.blue >> 8))));
	    if (trial_delta < nearest_delta)
	    {
		nearest = trial;
		nearest_delta = trial_delta;
	    }
	}
	screen_in_out->pixel = nearest.pixel;
	screen_in_out->red = nearest.red;
	screen_in_out->green = nearest.green;
	screen_in_out->blue = nearest.blue;
    }

    return True;
}


int
FreePens (
    Colormap        colormap,
    unsigned long  *pixels,
    int             npixels)
{
    if (GfxBase->LibNode.lib_Version >= 39)
    {
	int   i;

	for (i = 0; i < npixels; i++)
	    ReleasePen (colormap, pixels[i]);
    }

    return Success;
}


Status
ParseColor (
    char    *spec,
    XColor  *exact_def_return)
{
    int spec_length;

    if (spec == 0)
	return False;

    spec_length = strlen(spec);
    if (spec[0] == '#')
    {
	int hexlen;
	char hexstr[10];

	hexlen = (spec_length - 1) / 3;
	if (hexlen < 1 || hexlen > 4 || hexlen * 3 != spec_length - 1)
	    return False;

	hexstr[hexlen] = '\0';
	strncpy (hexstr, spec + 1, hexlen);
	exact_def_return->red = strtoul (hexstr, NULL, 16) << (16 - 4*hexlen);
	strncpy (hexstr, spec + 1 + hexlen, hexlen);
	exact_def_return->green = strtoul (hexstr, NULL, 16) << (16 - 4*hexlen);
	strncpy (hexstr, spec + 1 + 2 * hexlen, hexlen);
	exact_def_return->blue = strtoul (hexstr, NULL, 16) << (16 - 4*hexlen);

	return True;
    }
    else
    {
	FILE  *rgbf;
	int    items, red, green, blue;
	char   line[512], name[512];
	Bool   success = False;

	rgbf = fopen ("LIBS:rgb.txt", "r");
	if (rgbf == NULL)
	    return False;

	while (fgets(line, sizeof (line), rgbf) && !success)
	{
	    items = sscanf (line, "%d %d %d %[^\n]\n",
			    &red, &green, &blue, name);
	    if (items != 4)
		continue;

	    if (red < 0 || red > 0xFF
		|| green < 0 || green > 0xFF
		|| blue < 0 || blue > 0xFF)
	    {
		continue;
	    }

	    if (0 == xpmstrcasecmp (spec, name))
	    {
		exact_def_return->red = red * 0x0101;
		exact_def_return->green = green * 0x0101;
		exact_def_return->blue = blue * 0x0101;
		success = True;
	    }
	}
	fclose (rgbf);

	return success;
    }
}


int
QueryColor (
    Colormap   colormap,
    XColor    *def_in_out)
{
    if (GfxBase->LibNode.lib_Version >= 39)
    {
	unsigned long   rgb[3];

	GetRGB32 (colormap, def_in_out->pixel, 1, rgb);
	def_in_out->red = rgb[0] >> 16;
	def_in_out->green = rgb[1] >> 16;
	def_in_out->blue = rgb[2] >> 16;
    }
    else
    {
	unsigned short   rgb;

	rgb = GetRGB4 (colormap, def_in_out->pixel);
	def_in_out->red = ((rgb >> 8) & 0xF) * 0x1111;
	def_in_out->green = ((rgb >> 4) & 0xF) * 0x1111;
	def_in_out->blue = (rgb & 0xF) * 0x1111;
    }

    return Success;
}


int
QueryColors (
    Colormap   colormap,
    XColor    *defs_in_out,
    int        ncolors)
{
    int   i;

    for (i = 0; i < ncolors; i++)
	QueryColor (colormap, &defs_in_out[i]);

    return Success;
}
