/* rotate.c - image rotation
 *
 * Raster graphics library
 *
 * Copyright (c) 2000-2003 Alfredo K. Kojima
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include "wraster.h"

#include <math.h>

#ifndef PI
#define PI 3.14159265358979323846
#endif

static RImage *rotateImage(RImage * image, float angle);

RImage *RRotateImage(RImage * image, float angle)
{
	RImage *img;
	int nwidth, nheight;
	int x, y;
	int bpp = image->format == RRGBAFormat ? 4 : 3;

	angle = ((int)angle % 360) + (angle - (int)angle);

	if (angle == 0.0) {
		return RCloneImage(image);

	} else if (angle == 90.0) {
		nwidth = image->height;
		nheight = image->width;

		img = RCreateImage(nwidth, nheight, True);
		if (!img) {
			return NULL;
		}

		if (bpp == 3) {
			unsigned char *optr, *nptr;
			unsigned offs;

			offs = nwidth * 4;

			optr = image->data;
			nptr = img->data;

			for (x = 0; x < nwidth; x++) {
				nptr = img->data + x * 4;
				for (y = nheight; y; y--) {
					nptr[0] = *optr++;
					nptr[1] = *optr++;
					nptr[2] = *optr++;
					nptr[3] = 255;

					nptr += offs;
				}
			}
		} else {
			unsigned *optr, *nptr;
			unsigned *p;

			optr = (unsigned *)image->data;
			p = (unsigned *)img->data;
			for (x = 0; x < nwidth; x++) {
				nptr = p++;
				for (y = nheight; y; y--) {
					*nptr = *optr++;
					nptr += nwidth;
				}
			}
		}
	} else if (angle == 180.0) {

		nwidth = image->width;
		nheight = image->height;
		img = RCreateImage(nwidth, nheight, True);
		if (!img) {
			return NULL;
		}

		if (bpp == 3) {
			unsigned char *optr, *nptr;

			optr = image->data;
			nptr = img->data + nwidth * nheight * 4 - 4;

			for (y = 0; y < nheight; y++) {
				for (x = 0; x < nwidth; x++) {
					nptr[0] = optr[0];
					nptr[1] = optr[1];
					nptr[2] = optr[2];
					nptr[3] = 255;

					optr += 3;
					nptr -= 4;
				}
			}
		} else {
			unsigned *optr, *nptr;

			optr = (unsigned *)image->data;
			nptr = (unsigned *)img->data + nwidth * nheight - 1;

			for (y = nheight * nwidth - 1; y >= 0; y--) {
				*nptr = *optr;
				optr++;
				nptr--;
			}
		}
	} else if (angle == 270.0) {
		nwidth = image->height;
		nheight = image->width;

		img = RCreateImage(nwidth, nheight, True);
		if (!img) {
			return NULL;
		}

		if (bpp == 3) {
			unsigned char *optr, *nptr;
			unsigned offs;

			offs = nwidth * 4;

			optr = image->data;
			nptr = img->data;

			for (x = 0; x < nwidth; x++) {
				nptr = img->data + x * 4;
				for (y = nheight; y; y--) {
					nptr[0] = *optr++;
					nptr[1] = *optr++;
					nptr[2] = *optr++;
					nptr[3] = 255;

					nptr += offs;
				}
			}
		} else {
			unsigned *optr, *nptr;
			unsigned *p;

			optr = (unsigned *)image->data;
			p = (unsigned *)img->data + nwidth * nheight;
			for (x = 0; x < nwidth; x++) {
				nptr = p--;
				for (y = nheight; y; y--) {
					*nptr = *optr++;
					nptr -= nwidth;
				}
			}
		}
	} else {
		img = rotateImage(image, angle);
	}

	return img;
}

/*
 * Image rotation through Bresenham's line algorithm:
 *
 * If a square must be rotate by angle a, like in:
 *  _______
 * |    B  |
 * |   /4\ |
 * |  /3 8\|
 * | /2 7 /|
 * |A1 6 / |      A_______B
 * | \5 / a| <--- |1 2 3 4|
 * |__C/_)_|      |5 6 7 8|
 *                C-------
 *
 * for each point P1 in the line from C to A
 *	for each point P2 in the perpendicular line starting at P1
 *		get pixel from the source and plot at P2
 * 		increment pixel location from source
 *
 */

static void
copyLine(int x1, int y1, int x2, int y2, int nwidth, int format, unsigned char *dst, unsigned char **src)
{
	unsigned char *s = *src;
	int dx, dy;
	int xi, yi;
	int offset;
	int dpr, dpru, p;

	dx = abs(x2 - x1);
	dy = abs(y2 - y1);

	if (x1 > x2)
		xi = -1;
	else
		xi = 1;
	if (y1 > y2)
		yi = -1;
	else
		yi = 1;

	if (dx >= dy) {

		dpr = dy << 1;
		dpru = dpr - (dx << 1);
		p = dpr - dx;

		while (dx-- >= 0) {
			/* fetch and draw the pixel */
			offset = (x1 + y1 * nwidth) << 2;
			dst[offset++] = *s++;
			dst[offset++] = *s++;
			dst[offset++] = *s++;
			if (format == RRGBAFormat)
				dst[offset++] = *s++;
			else
				dst[offset++] = 255;

			/* calc next step */
			if (p > 0) {
				x1 += xi;
				y1 += yi;
				p += dpru;
			} else {
				x1 += xi;
				p += dpr;
			}
		}
	} else {

		dpr = dx << 1;
		dpru = dpr - (dy << 1);
		p = dpr - dy;

		while (dy-- >= 0) {
			/* fetch and draw the pixel */
			offset = (x1 + y1 * nwidth) << 2;
			dst[offset++] = *s++;
			dst[offset++] = *s++;
			dst[offset++] = *s++;
			if (format == RRGBAFormat)
				dst[offset++] = *s++;
			else
				dst[offset++] = 255;

			/* calc next step */
			if (p > 0) {
				x1 += xi;
				y1 += yi;
				p += dpru;
			} else {
				y1 += yi;
				p += dpr;
			}
		}
	}

	*src = s;
}

static RImage *rotateImage(RImage * image, float angle)
{
	RImage *img;
	int nwidth, nheight;
	int x1, y1;
	int x2, y2;
	int dx, dy;
	int xi, yi;
	int xx, yy;
	unsigned char *src, *dst;
	int dpr, dpru, p;

	/* only 180o for now */
	if (angle > 180.0)
		angle -= 180.0;

	angle = (angle * PI) / 180.0;

	nwidth = ceil(abs(cos(angle) * image->width))
	    + ceil(abs(cos(PI / 2 - angle) * image->width));

	nheight = ceil(abs(sin(angle) * image->height))
	    + ceil(abs(cos(PI / 2 - angle) * image->height));

	img = RCreateImage(nwidth, nheight, True);
	if (!img)
		return NULL;

	src = image->data;
	dst = img->data;

	x1 = floor(abs(cos(PI / 2 - angle) * image->width));
	y1 = 0;

	x2 = 0;
	y2 = floor(abs(sin(PI / 2 - angle) * image->width));

	xx = floor(abs(cos(angle) * image->height)) - 1;
	yy = nheight - 1;

	printf("%ix%i, %i %i     %i %i %i\n", nwidth, nheight, x1, y1, x2, y2, (int)((angle * 180.0) / PI));

	dx = abs(x2 - x1);
	dy = abs(y2 - y1);

	if (x1 > x2)
		xi = -1;
	else
		xi = 1;
	if (y1 > y2)
		yi = -1;
	else
		yi = 1;

	if (dx >= dy) {
		dpr = dy << 1;
		dpru = dpr - (dx << 1);
		p = dpr - dx;

		while (dx-- >= 0) {

			copyLine(x1, y1, xx, yy, nwidth, image->format, dst, &src);

			/* calc next step */

			if (p > 0) {
				x1 += xi;
				y1 += yi;
				xx += xi;
				yy += yi;
				p += dpru;
			} else {
				x1 += xi;
				xx += xi;
				p += dpr;
			}
		}
	} else {
		puts("NOT IMPLEMTENED");
		return img;
		dpr = dx << 1;
		dpru = dpr - (dy << 1);
		p = dpr - dy;

		while (dy-- >= 0) {
			xx = abs(x1 * sin(angle * PI / 180.0));
			yy = abs(y1 * cos(angle * PI / 180.0));

			copyLine(x1, y1, xx, yy, nwidth, image->format, dst, &src);

			/* calc next step */
			if (p > 0) {
				x1 += xi;
				y1 += yi;
				p += dpru;
			} else {
				y1 += yi;
				p += dpr;
			}
		}
	}

	return img;
}
