/*
 * $QNXLicenseC:
 * Copyright 2009, QNX Software Systems. All Rights Reserved.
 * 
 * You must obtain a written license from and pay applicable license fees to QNX 
 * Software Systems before you may reproduce, modify or distribute this software, 
 * or any work that includes all or part of this software.   Free development 
 * licenses are available for evaluation and non-commercial purposes.  For more 
 * information visit http://licensing.qnx.com or email licensing@qnx.com.
 *  
 * This file may contain contributions from others.  Please review this entire 
 * file for other proprietary rights or license notices, as well as the QNX 
 * Development Suite License Guide at http://licensing.qnx.com/license-guide/ 
 * for other information.
 * $
 */

#include "VG/openvg.h"

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

static VGfloat rotationIncrements = 1.0;
static int max_iterations = 15;

static VGfloat rotation;
static VGfloat relativeSquareSetDimensions;
static VGfloat scale;

static VGfloat largeSquareSize;
static VGfloat smallSquareSize;
static VGfloat originalOffset;

static VGfloat blueColor[] = {0, 0, 1, 1};
static VGfloat whiteColor[] = {1, 1, 1, 1};
static VGfloat redColor[] = {1, 0, 0, 1};
static VGfloat greenColor[] = {0, 1, 0, 1};
static VGPaint redPaint;
static VGPaint bluePaint;
static VGPaint whitePaint;
static VGPath smallSquarePath;
static VGPath largeSquarePath;

int killApp()
{
	// clean up the paths & paints we've created
	vgDestroyPath(largeSquarePath);
	vgDestroyPath(smallSquarePath);
	vgDestroyPaint(redPaint);
	vgDestroyPaint(whitePaint);
	vgDestroyPaint(bluePaint);

	return 0;
}

int initApp(int layer, int width, int height)
{
	rotation = 0.0;
	relativeSquareSetDimensions = 2.0 + 0.5 * sqrtf(2.0);
	scale = (relativeSquareSetDimensions - 1.0) / relativeSquareSetDimensions;

	// maximize the size of our drawing, considering we'll be rotating it...
	largeSquareSize = min(height, width) / (relativeSquareSetDimensions * sqrtf(2.0));
	smallSquareSize = largeSquareSize / 3.0;
	originalOffset = 0.5 * min(height, width) * (1 - 1 / sqrtf(2.0));

	// setup our paints
	bluePaint = vgCreatePaint();
	vgSetParameterfv(bluePaint, VG_PAINT_COLOR, 4, blueColor);
	redPaint = vgCreatePaint();
	vgSetParameterfv(redPaint, VG_PAINT_COLOR, 4, redColor);
	whitePaint = vgCreatePaint();
	vgSetParameterfv(whitePaint, VG_PAINT_COLOR, 4, whiteColor);

	{
		VGubyte smallSquareCmds[] = {VG_MOVE_TO_ABS, VG_LINE_TO_ABS, VG_LINE_TO_ABS, VG_LINE_TO_ABS, VG_CLOSE_PATH};
		VGfloat smallSquareCoords[] = {0.0, 0.0, 0.0, smallSquareSize, smallSquareSize, smallSquareSize, smallSquareSize, 0.0};
		VGubyte largeSquareCmds[] = {VG_MOVE_TO_ABS, VG_LINE_TO_ABS, VG_LINE_TO_ABS, VG_LINE_TO_ABS, VG_CLOSE_PATH};
		VGfloat largeSquareCoords[] = {0.0, 0.0, 0.0, largeSquareSize, largeSquareSize, largeSquareSize, largeSquareSize, 0.0};

		// setup the small square's path
		smallSquarePath = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1, 0, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
		vgAppendPathData(smallSquarePath, 4, smallSquareCmds, smallSquareCoords);

		// setup the large square's path
		largeSquarePath = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1, 0, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
		vgAppendPathData(largeSquarePath, 4, largeSquareCmds, largeSquareCoords);
	}

	return 0;
}

int drawSquareSet(VGPaint bluePaint, VGPath smallSquarePath, VGPaint redPaint, VGPath largeSquarePath, VGfloat largeSquareSize);

int drawScene(int layer, int width, int height)
{
	VGfloat planeMatrix[9];
	int iteration;

	// first load the identity matrix for the user to surface path
	vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
	vgLoadIdentity();

	// set the drawing to the center of the screen
	if (width < height)
		vgTranslate(originalOffset, originalOffset + (width - height) / 2);
	else
		vgTranslate(originalOffset + (width - height) / 2, originalOffset);

	// Rotate the whole plane, with the center of origin at the center of the screen.
	// We don't use 0.5 * height and 0.5 * width because our coordinate system has
	// already been modified by the translation above.
	vgTranslate(relativeSquareSetDimensions * largeSquareSize * 0.5, relativeSquareSetDimensions * largeSquareSize * 0.5);
	vgRotate(rotation);
	vgTranslate(-relativeSquareSetDimensions * largeSquareSize * 0.5, -relativeSquareSetDimensions * largeSquareSize * 0.5);

	if (fabsf(rotation) >= 180)
		rotationIncrements = -rotationIncrements;

	rotation += rotationIncrements;

	// first clear the whole surface
	// note that vgClear isn't affected by the PATH_USER_TO_SURFACE matrix transformations
	vgSetfv(VG_CLEAR_COLOR, 4, greenColor);
	vgClear(0, 0, width, height);

	// draw a big square where we'll be drawing our smaller ones...
	vgScale(relativeSquareSetDimensions, relativeSquareSetDimensions);
	vgSetPaint(whitePaint, VG_FILL_PATH);
	vgDrawPath(largeSquarePath, VG_FILL_PATH);
	vgScale(1.0 / relativeSquareSetDimensions, 1.0 / relativeSquareSetDimensions);

	for (iteration = 0; iteration < max_iterations; ++iteration)
	{
		// save the current coordinate system matrix so we can come back to it once we've drawn
		// our squares with drawSquareSet
		vgGetMatrix(planeMatrix);

		// Draw a set of 3 squares
		drawSquareSet(bluePaint, smallSquarePath, redPaint, largeSquarePath, largeSquareSize);

		// Now prepare the next iteration so that it can draw another set of 
		// 3 squares just like we've done above, but this
		// time, make them smaller by scaling them by a factor of "scale".

		// The call to drawSquareSet has modified the coordinate system, so we want to
		// restore it and come back to our original plane's coordinate system
		// so we want to set the original plane matrix we have saved earlier.
		vgLoadMatrix(planeMatrix);

		// now position the new sets of squares above our first square from the previous
		// set, such that the top of the last square drawn will be flush to the top of 
		// the last one we had previously drawn.  Yes, that's just for fun ;-)
		// It's left as the reader's exercise to determine how to come up with the right
		// translation here since it isn't the point of the sample code...
		vgTranslate(0.0, relativeSquareSetDimensions * largeSquareSize * (1 - scale));

		// now scale everything we'll do by a factor of "scale" in both directions
		vgScale(scale, scale);
	}

	return 0;
}

int drawSquareSet(VGPaint bluePaint, VGPath smallSquarePath, VGPaint redPaint, VGPath largeSquarePath, VGfloat largeSquareSize)
{
	// draw our first square (really two squares, one inside the other in the bottom
	// left corner of the first one) in the bottom-left corner of the coordinate system,
	// whatever it happens to be set to at this point.
	vgSetPaint(redPaint, VG_FILL_PATH);
	vgDrawPath(largeSquarePath, VG_FILL_PATH);
	vgSetPaint(bluePaint, VG_FILL_PATH);
	vgDrawPath(smallSquarePath, VG_FILL_PATH);

	// Now we want to draw another square, but this time, rotated 45 degrees
	// and place it so that the top left corner of the rotated square touches
	// the upper right corner of our first square.
	// To do this, move the origin to the upper right corner of our square,
	// then the rotation (always counter-clockwise) will be applied from that
	// point of origin.  After the rotation, the coordinate system will be
	// rotated, so anything we do is relative to it... (i.e. we're not dealing
	// with screen coordinates anymore).  So to bring down the upper left corner
	// of our square, we simply have to translate downwards the size of our square.
	vgTranslate(largeSquareSize, largeSquareSize); // place center of rotation
	vgRotate(45.0); // rotate
	vgTranslate(0.0, -largeSquareSize); // translate downwards

	// we're now ready to draw our second square
	vgSetPaint(redPaint, VG_FILL_PATH);
	vgDrawPath(largeSquarePath, VG_FILL_PATH);
	vgSetPaint(bluePaint, VG_FILL_PATH);
	vgDrawPath(smallSquarePath, VG_FILL_PATH);

	// now let's do a third one with the same relative position to the
	// second one as the second one had to the first one.
	vgTranslate(largeSquareSize, largeSquareSize); // place center of rotation
	vgRotate(45.0); // rotate
	vgTranslate(0.0, -largeSquareSize); // translate downwards

	// we're now ready to draw our third square
	vgSetPaint(redPaint, VG_FILL_PATH);
	vgDrawPath(largeSquarePath, VG_FILL_PATH);
	vgSetPaint(bluePaint, VG_FILL_PATH);
	vgDrawPath(smallSquarePath, VG_FILL_PATH);

	return 0;
}
