/*
    PIP - Platform Independent Primitives
    Console output/input
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "piscreendrawer.h"

// comment for use ascii instead of unicode symbols
#define USE_UNICODE

using namespace PIScreenTypes;


PIScreenDrawer::PIScreenDrawer(PIVector<PIVector<Cell>> & c): cells(c) {
	arts_[LineVertical] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("│");
#else
		PIChar('|');
#endif

	arts_[LineHorizontal] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("─");
#else
		PIChar('-');
#endif

	arts_[Cross] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("┼");
#else
		PIChar('+');
#endif

	arts_[CornerTopLeft] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("┌");
#else
		PIChar('+');
#endif

	arts_[CornerTopRight] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("┐");
#else
		PIChar('+');
#endif

	arts_[CornerBottomLeft] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("└");
#else
		PIChar('+');
#endif

	arts_[CornerBottomRight] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("┘");
#else
		PIChar('+');
#endif

	arts_[Unchecked] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("☐");
#else
		PIChar('O');
#endif

	arts_[Checked] =
#ifdef USE_UNICODE
		PIChar::fromUTF8("☑");
#else
		PIChar('0');
#endif
}


void PIScreenDrawer::clear() {
	clear(cells);
}


void PIScreenDrawer::drawPixel(int x, int y, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
	if (x < 0 || x >= width || y < 0 || y >= height) return;
	cells[y][x].symbol            = c;
	cells[y][x].format.color_char = col_char;
	cells[y][x].format.color_back = col_back;
	cells[y][x].format.flags      = flags_char;
}


void PIScreenDrawer::drawLine(int x0, int y0, int x1, int y1, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
	if (x0 == x1 && y0 == y1) drawPixel(x0, y0, c, col_char, col_back, flags_char);
	Cell cc;
	cc.symbol            = c;
	cc.format.color_char = col_char;
	cc.format.color_back = col_back;
	cc.format.flags      = flags_char;
	int x = 0, y = 0;
	if (piAbsi(x1 - x0) >= piAbsi(y1 - y0)) {
		float dy = (y1 - y0) / float(piAbsi(x1 - x0)), cy = y0;
		int dx = x0 < x1 ? 1 : -1;
		for (int i = x0; i != x1; i += dx) {
			x = i;
			y = piRound(cy);
			if (x >= 0 && x < width && y >= 0 && y < height) cells[y][x] = cc;
			cy += dy;
		}
		y = piRound(cy);
		if (x1 >= 0 && x1 < width && y >= 0 && y < height) cells[y][x1] = cc;
	} else {
		float dx = (x1 - x0) / float(piAbsi(y1 - y0)), cx = x0;
		int dy = y0 < y1 ? 1 : -1;
		for (int i = y0; i != y1; i += dy) {
			x = piRound(cx);
			y = i;
			if (x >= 0 && x < width && y >= 0 && y < height) cells[y][x] = cc;
			cx += dx;
		}
		x = piRound(cx);
		if (x >= 0 && x < width && y1 >= 0 && y1 < height) cells[y1][x] = cc;
	}
}


void PIScreenDrawer::drawRect(int x0, int y0, int x1, int y1, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
	if (x0 == x1 && y0 == y1) drawPixel(x0, y0, c, col_char, col_back, flags_char);
	Cell cc;
	cc.symbol            = c;
	cc.format.color_char = col_char;
	cc.format.color_back = col_back;
	cc.format.flags      = flags_char;
	int dx               = x0 < x1 ? 1 : -1;
	int dy               = y0 < y1 ? 1 : -1;
	int xs[2]            = {x0, x1};
	int ys[2]            = {y0, y1};
	for (int k = 0; k < 2; ++k) {
		int j = ys[k];
		if (j >= 0 && j < height) {
			PIVector<Cell> & cv(cells[j]);
			for (int i = x0; i != x1; i += dx)
				if (i >= 0 && i < width) cv[i] = cc;
		}
		j = xs[k];
		if (j >= 0 && j < width) {
			for (int i = y0; i != y1; i += dy)
				if (i >= 0 && i < height) cells[i][j] = cc;
		}
	}
	int i = x1, j = y1;
	if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
}


void PIScreenDrawer::drawFrame(int x0, int y0, int x1, int y1, Color col_char, Color col_back, CharFlags flags_char) {
	if (x0 == x1 && y0 == y1) return;
	Cell cc;
	cc.format.color_char = col_char;
	cc.format.color_back = col_back;
	cc.format.flags      = flags_char;
	int dx               = x0 < x1 ? 1 : -1;
	int dy               = y0 < y1 ? 1 : -1;
	int xs[2]            = {x0, x1};
	int ys[2]            = {y0, y1};
	for (int k = 0; k < 2; ++k) {
		int j = ys[k];
		if (j >= 0 && j < height) {
			PIVector<Cell> & cv(cells[j]);
			cc.symbol = artChar(LineHorizontal);
			for (int i = x0 + 1; i != x1; i += dx)
				if (i >= 0 && i < width) cv[i] = cc;
		}
		j = xs[k];
		if (j >= 0 && j < width) {
			cc.symbol = artChar(LineVertical);
			for (int i = y0 + 1; i != y1; i += dy)
				if (i >= 0 && i < height) cells[i][j] = cc;
		}
	}
	int i = x0, j = y0;
	cc.symbol = artChar(CornerTopLeft);
	if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
	i = x1, j = y0;
	cc.symbol = artChar(CornerTopRight);
	if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
	i = x0, j = y1;
	cc.symbol = artChar(CornerBottomLeft);
	if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
	i = x1, j = y1;
	cc.symbol = artChar(CornerBottomRight);
	if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
}


void PIScreenDrawer::fillRect(int x0, int y0, int x1, int y1, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
	if (x0 == x1 && y0 == y1) drawPixel(x0, y0, c, col_char, col_back, flags_char);
	Cell cc;
	cc.symbol            = c;
	cc.format.color_char = col_char;
	cc.format.color_back = col_back;
	cc.format.flags      = flags_char;
	int dx               = x0 < x1 ? 1 : -1;
	int dy               = y0 < y1 ? 1 : -1;
	for (int j = y0; j != y1; j += dy)
		if (j >= 0 && j < height) {
			PIVector<Cell> & cv(cells[j]);
			for (int i = x0; i != x1; i += dx)
				if (i >= 0 && i < width) cv[i] = cc;
		}
}


void PIScreenDrawer::fillRect(int x0, int y0, int x1, int y1, PIVector<PIVector<Cell>> & content) {
	if (x0 > x1) piSwap(x0, x1);
	if (y0 > y1) piSwap(y0, y1);
	int w = x1 - x0;
	int h = y1 - y0;
	for (int j = 0; j < h; ++j)
		if (j < piMini(height, content.size_s())) {
			if ((j + y0) >= 0 && (j + y0) < height) {
				PIVector<Cell> & cv(cells[y0 + j]);
				PIVector<Cell> & contv(content[j]);
				for (int i = 0; i < piMini(w, contv.size_s()); ++i)
					if ((i + x0) >= 0 && (i + x0) < width) cv[x0 + i] = contv[i];
			}
		}
}


void PIScreenDrawer::clear(PIVector<PIVector<Cell>> & cells) {
	for (int i = 0; i < cells.size_s(); ++i)
		cells[i].fill(Cell());
}


void PIScreenDrawer::drawText(int x, int y, const PIString & s, Color col_char, Color col_back, CharFlags flags_char) {
	if (x < 0 || x >= width || y < 0 || y >= height) return;
	PIVector<Cell> & cv(cells[y]);
	Cell cc;
	cc.format.color_char = col_char;
	cc.format.color_back = col_back;
	cc.format.flags      = flags_char;
	for (int i = 0; i < s.size_s(); ++i) {
		int j = i + x;
		if (j >= 0 && j < width) {
			cc.symbol = s[i];
			cv[j]     = cc;
		}
	}
}
