#include "vectorimage.h"


int VectorImage::Gradient::GetFillColour(int x, int y) {
	static float alpha;
	static float one_minus_alpha;
	static int r,g,b;

	if (type == 0) {
		return colour[0];
	}

	if (type == 1) {
		alpha = ((x - p0x)*vx + (y - p0y)*vy) / d_2;
	}
	else {
		alpha = sqrt(((x - p0x)*(x - p0x) + (y - p0y)*(y - p0y))) / d;
	}

	one_minus_alpha = 1.0f - alpha;

	if (alpha < 0) {
		return colour[0];
	}
	else if (alpha > 1) {
		return colour[1];
	}

	r = (int)(r1*one_minus_alpha + r2*alpha);
	g = (int)(g1*one_minus_alpha + g2*alpha);
	b = (int)(b1*one_minus_alpha + b2*alpha);

	return makecol32(r,g,b);
}


void VectorImage::Gradient::Fill(BITMAP *canvas, int x, int y) {
	static int magenta = makecol32(255,0,255);

	if (x < minX || x >= maxX || y < minY || y >= maxY) return;

	if (_getpixel32(canvas, x, y) == GetFillColour(x, y)) {
		return;
	}

	if (_getpixel32(canvas, x, y) != magenta) {
		return;
	}

	int y1;

	//draw current scanline from start position to the top
	y1 = y;
	while (y1 < maxY && _getpixel32(canvas, x, y1) == magenta) {
		_putpixel32(canvas, x, y1, GetFillColour(x, y1));
		y1++;
	}

	//draw current scanline from start position to the bottom
	y1 = y - 1;
	while (y1 >= minY && _getpixel32(canvas, x, y1) == magenta) {
		_putpixel32(canvas, x, y1, GetFillColour(x, y1));
		y1--;
	}

	//test for new scanlines to the left
	y1 = y;
	while (y1 < maxY && _getpixel32(canvas, x, y1) == GetFillColour(x, y1)) {
		if (x > minX && _getpixel32(canvas, x-1, y1) == magenta) {
			Fill(canvas, x-1, y1);
		}
		y1++;
	}
	y1 = y - 1;
	while (y1 >= minY && _getpixel32(canvas, x, y1) == GetFillColour(x, y1)) {
		if (x > minX && _getpixel32(canvas, x-1, y1) == magenta) {
			Fill(canvas, x-1, y1);
		}
		y1--;
	}

	//test for new scanlines to the right
	y1 = y;
	while (y1 < maxY && _getpixel32(canvas, x, y1) == GetFillColour(x, y1)) {
		if (x < maxX-1 && _getpixel32(canvas, x+1, y1) == magenta) {
			Fill(canvas, x+1, y1);
		}
		y1++;
	}
	y1 = y - 1;
	while (y1 >= minY && _getpixel32(canvas, x, y1) == GetFillColour(x, y1)) {
		if (x < maxX-1 && _getpixel32(canvas, x+1, y1) == magenta) {
			Fill(canvas, x+1, y1);
		}
		y1--;
	}
}


void VectorImage::Gradient::SetColour(int which, int col) {
	colour[which] = col;
}


int VectorImage::Gradient::GetColour(int which) {
	return colour[which];
}


void VectorImage::Gradient::SetAnchor(int which, const Vector2D &p) {
	anchor[which] = p;
}


void VectorImage::Gradient::SetPoint(int which, const Vector2D &p) {
	points[which] = p;
}


const Vector2D &VectorImage::Gradient::GetAnchor(int which) {
	return anchor[which];
}


const Vector2D &VectorImage::Gradient::GetPoint(int which) {
	return points[which];
}


void VectorImage::Gradient::AddPoint(const Vector2D &p) {
	points.push_back(p);
}


void VectorImage::Gradient::RemovePoint(const Vector2D &p) {
	int which = Find(p);
	if (which >= 0) {
		std::vector<Vector2D>::iterator iter = points.begin();
		for (int i=0; i<which; i++, ++iter);
		points.erase(iter);
	}
}


int VectorImage::Gradient::GetPointCount() {
	return points.size();
}


void VectorImage::Gradient::SetType(int t) {
	type = t;
}


int VectorImage::Gradient::GetType() {
	return type;
}


// returns index of the point that is close enough to the passe point,
// or -2 or -3 if an anchor is close or -1 if no points are close
int VectorImage::Gradient::Find(const Vector2D &p, float bias) {
	for (int i=0; i<2; i++) {
		if (anchor[i].Distance(p) <= bias) {
			return -2-i;
		}
	}

	for (int i=0; i<points.size(); i++) {
		if (points[i].Distance(p) <= bias) {
			return i;
		}
	}

	return -1;
}


void VectorImage::Gradient::Render(BITMAP *canvas, float zoom, int offset_x, int offset_y, int mode, bool debug) {
	float w = canvas->w * zoom;
	float h = canvas->h * zoom;

	if (!debug) {
		if (mode & VGFX_GRADIENTS) {
			p0x = (int)(anchor[0].x()*w) - offset_x;
			p0y = (int)(anchor[0].y()*h) - offset_y;
			vx = (int)(anchor[1].x()*w) - offset_x - p0x;
			vy = (int)(anchor[1].y()*h) - offset_y - p0y;
			d_2 = vx*vx + vy*vy;
			if (d_2 == 0.0f) d_2 = 1.0f;
			d = sqrt(d_2);
			r1 = (float)getr32(colour[0]);
			g1 = (float)getg32(colour[0]);
			b1 = (float)getb32(colour[0]);
			r2 = (float)getr32(colour[1]);
			g2 = (float)getg32(colour[1]);
			b2 = (float)getb32(colour[1]);
			minX = canvas->cl;
			minY = canvas->ct;
			maxX = canvas->cr;
			maxY = canvas->cb;

			for (int i=0; i<points.size(); i++) {
				int x = (int)(points[i].x()*w) - offset_x;
				int y = (int)(points[i].y()*h) - offset_y;
				Fill(canvas, x, y);
			}
		}
	}
	else {
		int nAnchors = 1;
		if (type > 0) nAnchors = 2;
		for (int i=0; i<nAnchors; i++) {
			Vector2D p = anchor[i];
			circlefill(canvas, (int)(w*p.x()) - offset_x, (int)(h*p.y()) - offset_y, 4, colour[i]);
			circle(canvas, (int)(w*p.x()) - offset_x, (int)(h*p.y()) - offset_y, 4, makecol32(0,0,0));
		}
		for (int i=0; i<points.size(); i++) {
			Vector2D p = points[i];
			circlefill(canvas, (int)(w*p.x()) - offset_x, (int)(h*p.y()) - offset_y, 4, makecol32(255,255,192));
			circle(canvas, (int)(w*p.x()) - offset_x, (int)(h*p.y()) - offset_y, 4, makecol32(255,0,0));
		}
	}
}


VectorImage::Gradient::Gradient() {
	colour[0] = makecol32(192,192,192);
	colour[1] = makecol32(64,64,64);
	anchor[0] = Vector2D(0.5f, 0.5f);
	anchor[1] = Vector2D(0.5f, 0.25f);
	type = 0;
}
