#include 
#include 
#include 
#include 
#include 
using namespace std;

struct Grid {
	int number = 0;
	bool is_fixed = false;
};

class Sudoku {
public:
	Sudoku() {
		init();
	}
	void init() {
		for (int row = 0; row < 9; ++row) {
			for (int col = 0; col < 9; ++col) {
				data[row][col] = Grid();
			}
		}
	}
	void set_fixed(int r, int c, int n) {
		Grid& g = data[r][c];
		g.number = n;
		g.is_fixed = true;
	}
	bool set(int r, int c, int n) {
		if (data[r][c].is_fixed) {
			return false;
		}
		bool valid = true;
		auto validator = [&](Grid g) {
			if (g.number == n) {
				valid = false;
			}
		};
		if (n != 0) {
			get_each_in_row(r, c, validator);
			get_each_in_col(r, c, validator);
			get_each_in_block(r, c, validator);
		}
		if (valid) {
			data[r][c].number = n;
		}
		return valid;
	}
	bool finished() {
		for (int r = 0; r < 9; ++r) {
			for (int c = 0; c < 9; ++c) {
				if (data[r][c].number == 0) {
					return false;
				}
			}
		}
		return true;
	}
	Grid get(int r, int c) {
		return data[r][c];
	}
private:
	Grid data[9][9];
	template
	void get_each_in_row(int r, int c, Fn f) {
		for (int i = 0; i < 9; ++i) {
			if (i != c) {
				f(data[r][i]);
			}
		}
	}
	template
	void get_each_in_col(int r, int c, Fn f) {
		for (int i = 0; i < 9; ++i) {
			if (i != r) {
				f(data[i][c]);
			}
		}
	}
	template
	void get_each_in_block(int r, int c, Fn f) {
		int block_rn = r / 3;
		int block_cn = c / 3;
		for (int br = 0; br < 3; ++br) {
			for (int bc = 0; bc < 3; ++bc) {
				int data_r = block_rn * 3 + br;
				int data_c = block_cn * 3 + bc;
				if (data_r != r && data_c != c) {
					f(data[data_r][data_c]);
				}
			}
		}
	}
};

void move_to(int x, int y) {
	HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD coord;
	coord.X = x;
	coord.Y = y;
	SetConsoleCursorPosition(hStdOut, coord);
}

template
void print(const T& arg) {
	wstringstream ss;
	ss << arg;
	HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	DWORD charsWritten;
	wstring str = ss.str();
	WriteConsoleW(hStdOut, str.c_str(), str.size(), &charsWritten, NULL);
}

template
void print(const T1& arg1, const T2& arg2, Ts... rest) {
	wstringstream ss;
	ss << arg1 << arg2;
	print(ss.str(), rest...);
}

void set_color(WORD color) {
	HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hStdOut, color);
}

void set_color() {
	set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

int main(int argc, char* argv[]) {
	Sudoku game;
	ifstream in("data.txt", ios::in);
	if (in) {
		int row = 0;
		string line;
		while (getline(in, line)) {
			int col = 0;
			for (char ch : line) {
				if (ch >= '1'&&ch <= '9') {
					game.set_fixed(row, col, ch - '0');
				}
				++col;
				if (col >= 9) {
					break;
				}
			}
			++row;
			if (row >= 9) {
				break;
			}
		}
		in.close();
	}
	HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
	DWORD mode;
	GetConsoleMode(hStdIn, &mode);
	mode &= ~ENABLE_QUICK_EDIT_MODE;
	SetConsoleMode(hStdIn, mode);
	DWORD recordsRead;
	INPUT_RECORD record;
	int sel_x = 0, sel_y = 0;
	bool invalidated = true;
	while (true) {
		if (invalidated) {
			for (int r = 0; r < 9; ++r) {
				for (int c = 0; c < 9; ++c) {
					move_to(c, r);
					Grid g = game.get(r, c);
					if (r == sel_y && c == sel_x) {
						set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
					}
					else {
						if (g.is_fixed) {
							set_color(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
						}
						else {
							set_color();
						}
					}
					if (g.number == 0) {
						print('.');
					}
					else {
						print(g.number);
					}
				}
			}
			if (game.finished()) {
				break;
			}
			invalidated = false;
		}
		ReadConsoleInputW(hStdIn, &record, 1, &recordsRead);
		if (record.EventType == MOUSE_EVENT) {
			auto evt = record.Event.MouseEvent;
			if (evt.dwEventFlags == 0) {
				auto pos = evt.dwMousePosition;
				if (evt.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED) {
					if (pos.X >= 0 && pos.X < 9 && pos.Y >= 0 && pos.Y < 9) {
						sel_x = pos.X;
						sel_y = pos.Y;
						invalidated = true;
					}
				}
			}
		}
		else if (record.EventType == KEY_EVENT) {
			auto evt = record.Event.KeyEvent;
			if (evt.bKeyDown) {
				if (evt.wVirtualKeyCode >= '1'&&evt.wVirtualKeyCode <= '9') {
					int num = evt.wVirtualKeyCode - '0';
					if (game.set(sel_y, sel_x, num)) {
						invalidated = true;
					}
				}
				else if (evt.wVirtualKeyCode == VK_BACK) {
					if (game.set(sel_y, sel_x, 0)) {
						invalidated = true;
					}
				}
				else {
					switch (evt.wVirtualKeyCode) {
					case VK_UP:
						if (sel_y > 0) {
							--sel_y;
							invalidated = true;
						}
						break;
					case VK_DOWN:
						if (sel_y < 8) {
							++sel_y;
							invalidated = true;
						}
						break;
					case VK_LEFT:
						if (sel_x > 0) {
							--sel_x;
							invalidated = true;
						}
						break;
					case VK_RIGHT:
						if (sel_x < 8) {
							++sel_x;
							invalidated = true;
						}
						break;
					}
				}
			}
		}
	}
	move_to(0, 9);
	set_color();
	print("Congratulations!\n");
	system("pause");
}