#include "ZImage.h"
#include <iostream>

void hsv2rgb(unsigned char* rgb, float h, float s, float v)
{
	int     Hi;
	float   f;
	unsigned char b;
	unsigned char p;
	unsigned char q;
	unsigned char t;

	Hi = (int)fmod(floor(h / 60.0f), 6.0f);
	f = h / 60.0f - Hi;
	b = (unsigned char)(v);
	p = (unsigned char)(v * (1.0f - s));
	q = (unsigned char)(v * (1.0f - f * s));
	t = (unsigned char)(v * (1.0f - (1.0f - f) * s));

	if (Hi == 0) {
		rgb[0] = b;
		rgb[1] = t;
		rgb[2] = p;
	}
	if (Hi == 1) {
		rgb[0] = q;
		rgb[1] = b;
		rgb[2] = p;
	}
	if (Hi == 2) {
		rgb[0] = p;
		rgb[1] = b;
		rgb[2] = t;
	}
	if (Hi == 3) {
		rgb[0] = p;
		rgb[1] = q;
		rgb[2] = b;
	}
	if (Hi == 4) {
		rgb[0] = t;
		rgb[1] = p;
		rgb[2] = b;
	}
	if (Hi == 5) {
		rgb[0] = b;
		rgb[1] = p;
		rgb[2] = q;
	}
}

void fwrite4b(unsigned int* data, int* counts, int count, FILE* file) {
	for (int i = 0; i < count; i++) {
		int value = data[i];
		for (int j = 0; j < counts[i]; j++) {
			fputc(value & 0xFF, file);
			value = value >> 8;
		}
	}
}

ZImage::ZImage(int w, int h, int z) {
	width = w;
	height = h;
	size = w * h;
	zoom = z;
	zoomedWidth = w * z;
	zoomedHeight = h * z;
	zoomedSize = zoomedWidth * zoomedHeight;

	imageBuffer = (unsigned char*)malloc(sizeof(unsigned char) * size * 3);
	effectBuffer = (unsigned char*)malloc(sizeof(unsigned char) * zoomedSize * 3);
	opacityBuffer = (double*)malloc(sizeof(double) * zoomedSize);
	resultBuffer = (unsigned char*)malloc(sizeof(unsigned char) * zoomedSize * 3);
	clearEffect();

	for (int i = 0; i < 1000; i++) hsv2rgb(randomPallet + static_cast<__int64>(i) * 3, (float)(rand() * 300.0), 1, 255);
}

void ZImage::close() {
	free(imageBuffer);
	free(effectBuffer);
	free(opacityBuffer);
	free(resultBuffer);
}

int ZImage::save(const char* name)
{
	unsigned char* position = resultBuffer;
	for (int y = 0; y < zoomedHeight; y++) {
		for (int x = 0; x < zoomedWidth; x++) {
			memcpy(position, imageBuffer + ((static_cast<__int64>(height) - 1 - y / zoom) * width + x / zoom) * 3, 3);
			position += 3;
		}
	}

	for (int y = 0; y < zoomedHeight; y++) {
		for (int x = 0; x < zoomedWidth; x++) {
			int i = y * zoomedWidth + x;
			double a = opacityBuffer[i];
			if (a > 0) {
				int position = (y / zoom * static_cast<__int64>(width) + x / zoom) * 3;
				int j = (zoomedHeight - 1 - y) * zoomedWidth + x;
				resultBuffer[j * 3 + 2] = (unsigned char)(imageBuffer[position + 2] * (1 - a) + effectBuffer[i * 3 + 2] * a);
				resultBuffer[j * 3 + 1] = (unsigned char)(imageBuffer[position + 1] * (1 - a) + effectBuffer[i * 3 + 1] * a);
				resultBuffer[j * 3 + 0] = (unsigned char)(imageBuffer[position + 0] * (1 - a) + effectBuffer[i * 3 + 0] * a);
			}
		}
	}

	FILE* file;
	fopen_s(&file, name, "wb");
	if (file == NULL || ferror(file)) return 1;

	unsigned int header_value[] = { 'B','M', 54 + (unsigned int)zoomedSize * 3, 0,0, 54, 40, (unsigned int)zoomedWidth, (unsigned int)zoomedHeight, 1, 24, 0, (unsigned int)zoomedSize * 3, 0,0,0,0, };
	int header_count[] = { 1,1,4,2,2,4,4,4,4,2,2,4,4,4,4,4,4 };
	fwrite4b(header_value, header_count, 17, file);
	fwrite(resultBuffer, sizeof(unsigned char), sizeof(unsigned char) * zoomedSize * 3, file);
	fclose(file);

	return 0;
}


int ZImage::save(const char* name, int ox, int oy, int w, int h)
{
	//ox = ox * zoom;
	//oy = (height - h - oy) * zoom;
	unsigned char* position = resultBuffer;
	for (int y = 0; y < h * zoom; y++) {
		for (int x = 0; x < w * zoom; x++) {
			memcpy(position, imageBuffer + ((static_cast<__int64>(h) - 1 - y / zoom + oy) * width + x / zoom + ox) * 3, 3);
			position += 3;
		}
	}

	for (int y = 0; y < h * zoom; y++) {
		for (int x = 0; x < w * zoom; x++) {
			int i = (y + oy * zoom) * zoomedWidth + (x + ox * zoom);
			double a = opacityBuffer[i];
			if (a > 0) {
				int j = (h * zoom - 1 - y) * (w * zoom) + x;
				int position = ((static_cast<__int64>(h) - 1 - y / zoom + oy) * width + x / zoom + ox) * 3; // TODO : 合っているか分からない
				resultBuffer[j * 3 + 2] = (unsigned char)(imageBuffer[position + 2] * (1 - a) + effectBuffer[i * 3 + 2] * a);
				resultBuffer[j * 3 + 1] = (unsigned char)(imageBuffer[position + 1] * (1 - a) + effectBuffer[i * 3 + 1] * a);
				resultBuffer[j * 3 + 0] = (unsigned char)(imageBuffer[position + 0] * (1 - a) + effectBuffer[i * 3 + 0] * a);
			}
		}
	}

	FILE* file;
	fopen_s(&file, name, "wb");
	if (file == NULL || ferror(file)) return 1;

	unsigned int header_value[] = { 'B','M', 54 + (unsigned int)w * h * zoom * zoom * 3, 0,0, 54, 40, (unsigned int)w * zoom, (unsigned int)h * zoom, 1, 24, 0, (unsigned int)w * h * zoom * zoom * 3, 0,0,0,0, };
	int header_count[] = { 1,1,4,2,2,4,4,4,4,2,2,4,4,4,4,4,4 };
	fwrite4b(header_value, header_count, 17, file);
	fwrite(resultBuffer, sizeof(unsigned char), sizeof(unsigned char) * w * h * zoom * zoom * 3, file);
	fclose(file);

	return 0;
}

void ZImage::clearEffect() {
	memset(opacityBuffer, 0, sizeof(double) * zoomedSize);
}

void ZImage::drawGrayscaleImage(unsigned char* image, int offset) {
	unsigned char* position = imageBuffer;
	for (int i = 0; i < size; i++) {
		memset(position, image[i + offset], 3);
		position += 3;
	}
}

void ZImage::setPixel(int position, unsigned char r, unsigned char g, unsigned char b) {
	imageBuffer[position * 3 + 2] = r;
	imageBuffer[position * 3 + 1] = g;
	imageBuffer[position * 3 + 0] = b;
}

void ZImage::setPixel(int position, int index) {
	memcpy(imageBuffer + static_cast<__int64>(position) * 3, randomPallet + (static_cast<__int64>(index) % 1000) * 3, 3);
}


void ZImage::setPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b) {
	int position = width * y + x;
	imageBuffer[position * 3 + 2] = r;
	imageBuffer[position * 3 + 1] = g;
	imageBuffer[position * 3 + 0] = b;
}

void ZImage::setPixel(int x, int y, int index) {
	memcpy(imageBuffer + (width * static_cast<__int64>(y) + x) * 3, randomPallet + (static_cast<__int64>(index) % 1000) * 3, 3);
}

void ZImage::setNumber(int i, int text, unsigned char r, unsigned char g, unsigned char b) {
	setNumber(i % width, i / width, text, r, g, b, 0, 0);
}

void ZImage::setNumber(int x, int y, int text, unsigned char r, unsigned char g, unsigned char b) {
	setNumber(x, y, text, r, g, b, 0, 0);
}

void ZImage::setNumber(int x, int y, int text, unsigned char r, unsigned char g, unsigned char b, int offsetX, int offsetY) {
	int now = text;
	int p = 0;
	int cx = x * zoom;
	int cy = y * zoom;
	int dx, dy;
	bool deltab[10][15]{
		{ true, true, true, true, false, true, true, false, true, true, false, true, true, true, true, },
		{ false, true, false, false, true, false, false, true, false, false, true, false, false, true, false, },
		{ true, true, true, false, false, true, true, true, true, true, false, false, true, true, true, },
		{ true, true, true, false, false, true, true, true, true, false, false, true, true, true, true, },
		{ true, false, true, true, false, true, true, true, true, false, false, true, false, false, true, },
		{ true, true, true, true, false, false, true, true, true, false, false, true, true, true, true, },
		{ true, false, false, true, false, false, true, true, true, true, false, true, true, true, true, },
		{ true, true, true, false, false, true, false, false, true, false, false, true, false, false, true, },
		{ true, true, true, true, false, true, true, true, true, true, false, true, true, true, true, },
		{ true, true, true, true, false, true, true, true, true, false, false, true, false, false, true, },
	};
	while (now > 0)
	{
		p = now % 10;
		for (dy = 0; dy < 5; dy++)
		{
			for (dx = 0; dx < 3; dx++)
			{
				int px = cx + dx + offsetX;
				int py = cy + dy + offsetY;

				if (px < 0 || px >= zoomedWidth || py < 0 || py >= zoomedHeight) continue;
				if (!deltab[p][dy * 3 + dx]) continue;

				int position = py * zoomedWidth + px;
				effectBuffer[position * 3 + 2] = r;
				effectBuffer[position * 3 + 1] = g;
				effectBuffer[position * 3 + 0] = b;
				opacityBuffer[position] = 1;
			}
		}
		cx -= 4;
		now = now / 10;
	}
}

void ZImage::effectLine(int i, int j, unsigned char r, unsigned char g, unsigned char b) {
	effectLine(i % width * zoom, i / width * zoom, j % width * zoom, j / width * zoom, r, g, b);
}

void ZImage::effectLine(int x1, int y1, int x2, int y2, unsigned char r, unsigned char g, unsigned char b) {
	int xn = -1;
	int yn = -1;
	int xc = x1;
	int yc = y1;
	for (double p = 0; p <= 1; p += 0.001) {
		xc = (int)(x1 * (1 - p) + x2 * p);
		yc = (int)(y1 * (1 - p) + y2 * p);
		if (xn != xc || yn != yc) {
			xn = xc;
			yn = yc;
			effectPixel(xc, yc, r, g, b);
		}
	}
}

void ZImage::effectGrid(int i, bool left, bool top, bool right, bool bottom, unsigned char r, unsigned char g, unsigned char b) {
	effectGrid(i % width, i / width, left, top, right, bottom, r, g, b);
}

void ZImage::effectGrid(int x, int y, bool left, bool top, bool right, bool bottom, unsigned char r, unsigned char g, unsigned char b) {
	int px = x * zoom;
	int py = y * zoom;
	if (left) {
		for (int d = 0; d < zoom; d++) {
			if (opacityBuffer[(py + d) * zoomedWidth + (px + 0)] > 0)continue;
			effectBuffer[((py + d) * zoomedWidth + (px + 0)) * 3 + 2] = r;
			effectBuffer[((py + d) * zoomedWidth + (px + 0)) * 3 + 1] = g;
			effectBuffer[((py + d) * zoomedWidth + (px + 0)) * 3 + 0] = b;
			opacityBuffer[(py + d) * zoomedWidth + (px + 0)] = 1;
		}
	}

	if (top) {
		for (int d = 0; d < zoom; d++) {
			if (opacityBuffer[(py + 0) * zoomedWidth + (px + d)] > 0)continue;
			effectBuffer[((py + 0) * zoomedWidth + (px + d)) * 3 + 2] = r;
			effectBuffer[((py + 0) * zoomedWidth + (px + d)) * 3 + 1] = g;
			effectBuffer[((py + 0) * zoomedWidth + (px + d)) * 3 + 0] = b;
			opacityBuffer[(py + 0) * zoomedWidth + (px + d)] = 1;
		}
	}

	if (right) {
		for (int d = 0; d < zoom; d++) {
			if (opacityBuffer[(py + d) * zoomedWidth + (px + zoom - 1)] > 0)continue;
			effectBuffer[((py + d) * zoomedWidth + (px + zoom - 1)) * 3 + 2] = r;
			effectBuffer[((py + d) * zoomedWidth + (px + zoom - 1)) * 3 + 1] = g;
			effectBuffer[((py + d) * zoomedWidth + (px + zoom - 1)) * 3 + 0] = b;
			opacityBuffer[(py + d) * zoomedWidth + (px + zoom - 1)] = 1;
		}
	}

	if (bottom) {
		for (int d = 0; d < zoom; d++) {
			if (opacityBuffer[(py + zoom - 1) * zoomedWidth + (px + d)] > 0)continue;
			effectBuffer[((py + zoom - 1) * zoomedWidth + (px + d)) * 3 + 2] = r;
			effectBuffer[((py + zoom - 1) * zoomedWidth + (px + d)) * 3 + 1] = g;
			effectBuffer[((py + zoom - 1) * zoomedWidth + (px + d)) * 3 + 0] = b;
			opacityBuffer[(py + zoom - 1) * zoomedWidth + (px + d)] = 1;
		}
	}
}

void ZImage::effectEllipse(double cx, double cy, double radius, unsigned char r, unsigned char g, unsigned char b) {
	int xn = -1;
	int yn = -1;
	int xc = -1;
	int yc = -1;
	for (double p = 0; p <= 1; p += 0.001) {
		xc = cx * zoom + cos(3.14 * p * 2.0) * radius * zoom;
		yc = cy * zoom + sin(3.14 * p * 2.0) * radius * zoom;
		if (xn != xc || yn != yc) {
			xn = xc;
			yn = yc;
			if (0 <= xn && xn < zoomedWidth && 0 <= yn && yn < zoomedHeight) {
				if (opacityBuffer[yn * zoomedWidth + xn] > 0)continue;
				effectBuffer[(yn * zoomedWidth + xn) * 3 + 2] = r;
				effectBuffer[(yn * zoomedWidth + xn) * 3 + 1] = g;
				effectBuffer[(yn * zoomedWidth + xn) * 3 + 0] = b;
				opacityBuffer[yn * zoomedWidth + xn] = 1.0;
			}
		}
	}
}

void ZImage::effectPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b) {
	if (opacityBuffer[y * zoomedWidth + x] > 0) return;
	effectBuffer[(y * zoomedWidth + x) * 3 + 2] = r;
	effectBuffer[(y * zoomedWidth + x) * 3 + 1] = g;
	effectBuffer[(y * zoomedWidth + x) * 3 + 0] = b;
	opacityBuffer[y * zoomedWidth + x] = 1.0;
}