#include <math.h>
#include "aadraw.h"
#include "splines.h"
#include "defines.h"
using namespace std;


float node_dist(const Vector2D &n1, const Vector2D &n2) {
   float dx = (float)(n1.x() - n2.x());
   float dy = (float)(n1.y() - n2.y());

   return sqrt(dx*dx + dy*dy);
}


Vector2D dummy_node(const Vector2D &node, const Vector2D &prev) {
   Vector2D n;

   n.x(node.x() - (prev.x() - node.x()) / 8);
   n.y(node.y() - (prev.y() - node.y()) / 8);

   return n;
}


void calc_tangents(vector<Vector2D> &nodes, vector<float> &tangents, bool closePath) {
	int node_count = nodes.size();

	Vector2D dummy_first = closePath ? nodes[node_count-1] : dummy_node(nodes[0], nodes[1]);
	Vector2D dummy_last = closePath ? nodes[0] : dummy_node(nodes[node_count-1], nodes[node_count-2]);

	tangents.push_back(atan2(nodes[1].y() - dummy_first.y(), nodes[1].x() - dummy_first.x()));
	for (int i=1; i<node_count-1; i++) {
		tangents.push_back(atan2(nodes[i+1].y() - nodes[i-1].y(), nodes[i+1].x() - nodes[i-1].x()));
	}
	tangents.push_back(atan2(dummy_last.y() - nodes[node_count-2].y(), dummy_last.x() - nodes[node_count-2].x()));
}


void get_control_points(const Vector2D &n1, const Vector2D &n2, float tan1, float tan2, int *points, float curviness) {
	float dist = node_dist(n1, n2) * curviness;

	points[0] = (int)(n1.x());
	points[1] = (int)(n1.y());

	points[2] = (int)(n1.x() + cos(tan1) * dist);
	points[3] = (int)(n1.y() + sin(tan1) * dist);

	points[4] = (int)(n2.x() - cos(tan2) * dist);
	points[5] = (int)(n2.y() - sin(tan2) * dist);

	points[6] = (int)(n2.x());
	points[7] = (int)(n2.y());
}


void draw_spline(BITMAP *canvas, const Vector2D &n1, const Vector2D &n2, float tan1, float tan2, float curviness, int col) {
	int points[8];

	get_control_points(n1, n2, tan1, tan2, points, curviness);

	#define DIST(x, y) (sqrt((x) * (x) + (y) * (y)))
	int num_points = (int)(sqrt(DIST(points[2]-points[0], points[3]-points[1]) +
		DIST(points[4]-points[2], points[5]-points[3]) +
		DIST(points[6]-points[4], points[7]-points[5])) * 1.2);

   #define MAX_POINTS   64
	if (num_points > MAX_POINTS) {
		num_points = MAX_POINTS;
	}

	int xpts[MAX_POINTS], ypts[MAX_POINTS];
	calc_spline(points, num_points, xpts, ypts);

	for (int i=1; i<num_points; i++) {
		line(canvas, xpts[i-1], ypts[i-1], xpts[i], ypts[i], col);
	}
}


void draw_aa_spline(BITMAP *canvas, const Vector2D &n1, const Vector2D &n2, float tan1, float tan2, float curviness, float thickness, int col, float aa, int caps) {
	int points[8];

	get_control_points(n1, n2, tan1, tan2, points, curviness);

	#define DIST(x, y) (sqrt((x) * (x) + (y) * (y)))
	int num_points = (int)(sqrt(DIST(points[2]-points[0], points[3]-points[1]) +
		DIST(points[4]-points[2], points[5]-points[3]) +
		DIST(points[6]-points[4], points[7]-points[5])) * 1.2);

   #define MAX_POINTS   64
	if (num_points > MAX_POINTS) {
		num_points = MAX_POINTS;
	}

	int xpts[MAX_POINTS], ypts[MAX_POINTS];
	calc_spline(points, num_points, xpts, ypts);

	for (int i=1; i<num_points; i++) {
		int caps_line = VGFX_CAPS_NONE;
		if (i != 1 || (caps & VGFX_CAPS_FIRST)) {
			caps_line |= VGFX_CAPS_FIRST;
		}
		if (i != num_points-1 || (caps & VGFX_CAPS_LAST)) {
			caps_line |= VGFX_CAPS_LAST;
		}
		aa_line(canvas, xpts[i-1], ypts[i-1], xpts[i], ypts[i], thickness, col, aa, caps_line);
	}
}


void draw_splines(BITMAP *canvas, vector<Vector2D> &nodes, float curviness, int col, bool closePath) {
	if (nodes.size() >= 2) {
		vector<float> tangents;
		calc_tangents(nodes, tangents, closePath);

		int node_count = nodes.size();
		if (!closePath) --node_count;
		for (int i=0; i<node_count; i++) {
			int j = i+1;
			j %= nodes.size();
			draw_spline(canvas, nodes[i], nodes[j], tangents[i], tangents[j], curviness, col);
		}
	}
}


void draw_aa_splines(BITMAP *canvas, vector<Vector2D> &nodes, float curviness, float thickness, int col, float aa, bool closePath, int caps) {
	if (nodes.size() >= 2) {
		vector<float> tangents;
		calc_tangents(nodes, tangents, closePath);

		int node_count = closePath ? nodes.size() : nodes.size()-1;
		for (int i=0; i<node_count; i++) {
			int j = i+1;
			j %= nodes.size();
			int caps_line = VGFX_CAPS_NONE;
			if (i != 0 || (caps & VGFX_CAPS_FIRST)) {
				caps_line |= VGFX_CAPS_FIRST;
			}
			if (i != node_count-1 || (caps & VGFX_CAPS_LAST)) {
				caps_line |= VGFX_CAPS_LAST;
			}
			draw_aa_spline(canvas, nodes[i], nodes[j], tangents[i], tangents[j], curviness, thickness, col, aa, caps_line);
		}
	}
}
