#include "vectorimage.h"


void VectorImage::AddStroke() {
	VectorImage::Stroke s; strokes.push_back(s);
}


void VectorImage::RemoveStroke(int which) {
	std::vector<VectorImage::Stroke>::iterator iter = strokes.begin();
	for (int i=0; i<which; i++, ++iter);
	strokes.erase(iter);
}


VectorImage::Stroke &VectorImage::GetStroke(int which) {
	return strokes[which];
}


int VectorImage::GetStrokeCount() {
	return strokes.size();
}


void VectorImage::AddGradient() {
	VectorImage::Gradient g; gradients.push_back(g);
}


void VectorImage::RemoveGradient(int which) {
	std::vector<VectorImage::Gradient>::iterator iter = gradients.begin();
	for (int i=0; i<which; i++, ++iter);
	gradients.erase(iter);
}


VectorImage::Gradient &VectorImage::GetGradient(int which) {
	return gradients[which];
}


int VectorImage::GetGradientCount() {
	return gradients.size();
}


void VectorImage::SetBackground(int which, int col) {
	background[which] = col;
}


int VectorImage::GetBackground(int which) {
	return background[which];
}


void VectorImage::RenderBackground(BITMAP *canvas, float zoom, int offset_x, int offset_y) {
	int r1 = getr32(background[0]);
	int g1 = getg32(background[0]);
	int b1 = getb32(background[0]);
	int r2 = getr32(background[1]);
	int g2 = getg32(background[1]);
	int b2 = getb32(background[1]);
	int r3 = getr32(background[2]);
	int g3 = getg32(background[2]);
	int b3 = getb32(background[2]);
	int r4 = getr32(background[3]);
	int g4 = getg32(background[3]);
	int b4 = getb32(background[3]);

	float w = canvas->w * zoom;
	float h = canvas->h * zoom;

	float dx, dy;
	for (int j=canvas->ct; j<canvas->cb; j++) {
		dy = ((float)(j + offset_y)/zoom)/(float)canvas->h;
		for (int i=canvas->cl; i<canvas->cr; i++) {
			dx = ((float)(i + offset_x)/zoom)/(float)canvas->w;

			int r5 = (int)(r1 + dx*(r2-r1));
			int r6 = (int)(r3 + dx*(r4-r3));
			int r  = (int)(r5 + dy*(r6-r5));
			int g5 = (int)(g1 + dx*(g2-g1));
			int g6 = (int)(g3 + dx*(g4-g3));
			int g  = (int)(g5 + dy*(g6-g5));
			int b5 = (int)(b1 + dx*(b2-b1));
			int b6 = (int)(b3 + dx*(b4-b3));
			int b  = (int)(b5 + dy*(b6-b5));

			_putpixel32(canvas, i, j, makecol(r,g,b));
		}
	}
}


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

	BITMAP *tmp = create_bitmap(w, h);
	clear_to_color(tmp, makecol(255,0,255));
	for (int i=0; i<strokes.size(); i++) {
		strokes[i].Render(tmp, zoom, offset_x, offset_y, 0, false);
	}
	for (int i=0; i<gradients.size(); i++) {
		gradients[i].Render(tmp, zoom, offset_x, offset_y, i==selectedGradient ? mode | VGFX_GRADIENTS : mode, false);
	}

	if (mode & VGFX_BACKGROUND) {
		RenderBackground(canvas, zoom, offset_x, offset_y);
	}
	else {
		clear_to_color(canvas, makecol(222,222,222));
	}

	masked_blit(tmp, canvas, 0, 0, 0, 0, w, h);
	for (int i=0; i<strokes.size(); i++) {
		strokes[i].Render(canvas, zoom, offset_x, offset_y, mode, false);
	}

	for (int i=0; i<strokes.size(); i++) {
		if (i==selectedStroke) {
			strokes[i].Render(canvas, zoom, offset_x, offset_y, mode, true);
			break;
		}
	}

	for (int i=0; i<gradients.size(); i++) {
		if (i==selectedGradient) {
			gradients[i].Render(canvas, zoom, offset_x, offset_y, mode, true);
			break;
		}
	}

	destroy_bitmap(tmp);
}


int VectorImage::Load(const char *filename) {
	Clear();
	PACKFILE *f = pack_fopen(filename, "r");
	if (!f) {
		return 1;
	}

	char tmp[4];
	pack_fread(tmp, 4*sizeof(char), f);
	if (ustrncmp(tmp, "VGFX", 4) != 0) {
		pack_fclose(f);
		return 2;
	}

	char vMajor = 1;
	char vMinor = 0;
	pack_fread(&vMajor, sizeof(char), f);
	pack_fread(&vMinor, sizeof(char), f);
	int fileVersion = vMajor*100+vMinor;
	int VGFX_VERSION = VGFX_VERSION_MAJOR*100 + VGFX_VERSION_MINOR;
	if (fileVersion > VGFX_VERSION || fileVersion < 101) {
		pack_fclose(f);
		return 3;
	}

	int n;
	pack_fread(&n, sizeof(int), f);
	for (int i=0; i<n; i++) {
		AddStroke();
		pack_fread(&(strokes[i].flags), sizeof(int), f);
		if (fileVersion == 101) strokes[i].flags |= VGFX_CAPS_BOTH;
		pack_fread(&(strokes[i].thickness), sizeof(float), f);
		pack_fread(&(strokes[i].curviness), sizeof(float), f);
		int n2;
		pack_fread(&n2, sizeof(int), f);
		for (int j=0; j<n2; j++) {
			float x, y;
			pack_fread(&x, sizeof(float), f);
			pack_fread(&y, sizeof(float), f);
			strokes[i].Add(Vector2D(x,y));
		}
		int col;
		pack_fread(&col, sizeof(int), f);
		strokes[i].SetColour(col);
	}

	pack_fread(&n, sizeof(int), f);
	for (int i=0; i<n; i++) {
		AddGradient();

		int type;
		pack_fread(&type, sizeof(int), f);
		gradients[i].SetType(type);

		int nAnchors = 1;
		if (type > 0) nAnchors = 2;

		for (int j=0; j<nAnchors; j++) {
			int col;
			pack_fread(&col, sizeof(int), f);
			gradients[i].SetColour(j, col);
		}

		for (int j=0; j<nAnchors; j++) {
			float x, y;
			pack_fread(&x, sizeof(float), f);
			pack_fread(&y, sizeof(float), f);
			gradients[i].SetAnchor(j, Vector2D(x,y));
		}

		int n2;
		pack_fread(&n2, sizeof(int), f);
		for (int j=0; j<n2; j++) {
			float x, y;
			pack_fread(&x, sizeof(float), f);
			pack_fread(&y, sizeof(float), f);
			gradients[i].AddPoint(Vector2D(x,y));
		}
	}

	pack_fread(background, 4*sizeof(int), f);

	pack_fclose(f);

	return 0;
}


int VectorImage::Save(const char *filename) {
	PACKFILE *f = pack_fopen(filename, "w");
	if (!f) {
		return 1;
	}

	pack_fwrite("VGFX", 4*sizeof(char), f);
	char vMajor = VGFX_VERSION_MAJOR;
	char vMinor = VGFX_VERSION_MINOR;
	pack_fwrite(&vMajor, sizeof(char), f);
	pack_fwrite(&vMinor, sizeof(char), f);

	int n = strokes.size();
	pack_fwrite(&n, sizeof(int), f);
	for (int i=0; i<n; i++) {
		pack_fwrite(&(strokes[i].flags), sizeof(int), f);
		pack_fwrite(&(strokes[i].thickness), sizeof(float), f);
		pack_fwrite(&(strokes[i].curviness), sizeof(float), f);
		int n2 = strokes[i].GetLength();
		pack_fwrite(&n2, sizeof(int), f);
		for (int j=0; j<n2; j++) {
			float x = strokes[i].GetPoint(j).x();
			float y = strokes[i].GetPoint(j).y();
			pack_fwrite(&x, sizeof(float), f);
			pack_fwrite(&y, sizeof(float), f);
		}

		int col = strokes[i].GetColour();
		pack_fwrite(&col, sizeof(int), f);
	}

	n = gradients.size();
	pack_fwrite(&n, sizeof(int), f);
	for (int i=0; i<n; i++) {
		int type = gradients[i].GetType();
		pack_fwrite(&type, sizeof(int), f);

		int nAnchors = 1;
		if (type > 0) nAnchors = 2;

		for (int j=0; j<nAnchors; j++) {
			int col = gradients[i].GetColour(j);
			pack_fwrite(&col, sizeof(int), f);
		}

		for (int j=0; j<nAnchors; j++) {
			float x = gradients[i].GetAnchor(j).x();
			float y = gradients[i].GetAnchor(j).y();
			pack_fwrite(&x, sizeof(float), f);
			pack_fwrite(&y, sizeof(float), f);
		}

		int n2 = gradients[i].GetPointCount();
		pack_fwrite(&n2, sizeof(int), f);
		for (int j=0; j<n2; j++) {
			float x = gradients[i].GetPoint(j).x();
			float y = gradients[i].GetPoint(j).y();
			pack_fwrite(&x, sizeof(float), f);
			pack_fwrite(&y, sizeof(float), f);
		}
	}

	pack_fwrite(background, 4*sizeof(int), f);

	pack_fclose(f);

	return 0;
}


void VectorImage::Clear() {
	strokes.clear();
	gradients.clear();
}


VectorImage::VectorImage() {
	background[0] = makecol(255,222,222);
	background[1] = makecol(222,255,222);
	background[2] = makecol(222,222,255);
	background[3] = makecol(255,255,222);
}


VectorImage::~VectorImage() {
	Clear();
}
