/* Copyright 2018 Romain "Artefact2" Dal Maso <artefact2@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "gumble.h"

static const char* pstr[] = { " ", " ", "P", "p", "B", "b", "N", "n", "R", "r", "Q", "q", "K", "k" };
static const unsigned char bpr = 8; /* boards per row, for print_highlights */

static cch_board_t board;



static unsigned char find_highlights(cch_board_bitmap_t* highlights, unsigned char start, cch_square_t* rsq, cch_board_bitmap_t* rhl) {
	unsigned char i = 0;
	memset(rsq, 0, bpr * sizeof(cch_square_t));
	memset(rhl, 0, bpr * sizeof(cch_board_bitmap_t));
	while(i < 8 && start < 64) {
		if(!highlights[start]) {
			++start;
			continue;
		}
		rsq[i] = start;
		rhl[i] = highlights[start];
		++i;
		++start;
	}
	return start;
}

/* XXX: lots of duplicate code :-| */
static void print_highlights(cch_board_bitmap_t* highlights) {
	cch_square_t rsq[bpr];
	cch_board_bitmap_t rhl[bpr];
	unsigned char start = 0, file, i, rank;
	cch_square_t sq;

	while(start < 64) {
		start = find_highlights(highlights, start, rsq, rhl);

		for(rank = 7; rank < 8; --rank) {
			for(i = 0; i < bpr && rhl[i]; ++i) {
				printf("%d  ", rank + 1);

				for(file = 0; file < 8; ++file) {
					sq = CCH_SQUARE(file, rank);
					printf(
						"%c[38;5;%dm%c[48;5;%dm%s",
						27,
						232,
						27,
						sq == rsq[i] ? 202 : (((rhl[i] >> sq) & 1) ? ((file + rank) % 2 ? 214 : 220) : ((file + rank) % 2 ? 151 : 231)),
						pstr[CCH_GET_SQUARE(&board, sq)]
						);
				}
				printf("%c[0m  ", 27);
			}

			putchar('\n');
		}

		putchar('\n');
		for(i = 0; i < bpr && rhl[i]; ++i) {
			putchar(' '); putchar(' '); putchar(' ');
			for(char x = 'a'; x <= 'h'; ++x) putchar(x);
			putchar(' '); putchar(' ');
		}
		putchar('\n'); putchar('\n');
	}
}

static void prompt_command(const char* prompt) {
	char* line = 0;
	size_t n;
	cch_move_t move;
	cch_return_t r;

	while(1) {
		fputs(prompt, stdout);
		if(getline(&line, &n, stdin) == -1) {
			exit(1);
		}

		if(!strcmp("h\n", line)) {
			puts("h: show this help message");
			puts("s: dump FEN of current board");
			puts("m: print all possible moves");
			puts("z: print board hash");
			puts("Enter moves in algebraic notation (a1a2, e5, Ng3, Rab1, etc.)");
			return;
		}
		if(!strcmp("s\n", line)) {
			char fen[SAFE_FEN_LENGTH];
			cch_return_t ret = cch_save_fen(&board, fen, SAFE_FEN_LENGTH);
			assert(ret == CCH_OK);
			puts(fen);
			return;
		}
		if(!strcmp("m\n", line)) {
			cch_movelist_t ml;
			cch_board_bitmap_t highlights[64];
			memset(highlights, 0, 64 * sizeof(cch_board_bitmap_t));
			unsigned char stop = cch_generate_moves(&board, ml, CCH_LEGAL, 0, 64);
			for(unsigned char i = 0; i < stop; ++i) {
				highlights[ml[i].start] |= (1ULL << ml[i].end);
			}
			putchar('\n');
			print_highlights(highlights);
			return;
		}
		if(!strcmp("z\n", line)) {
			printf("%llx\n", board.hash);
			return;
		}

		r = cch_parse_san_move(&board, line, &move);
		if(r != CCH_OK) {
			r = cch_parse_lan_move(line, &move);
			if(r == CCH_OK) {
				r = cch_play_move(&board, &move, 0);
			}
		} else {
			r = cch_play_legal_move(&board, &move, 0);
		}

		switch(r) {
		case CCH_OK:
			return;

		case CCH_ILLEGAL_MOVE:
			puts("That is an illegal move.");
			continue;

		case CCH_PARSE_ERROR:
			puts("I do not understand that move.");
			continue;

		default:
			assert(0);
		}
	}
}

static void print_board(void) {
	char x, y;
	cch_square_t sq;

	/* XXX: this is barely usable */
	for(x = 0; x < 8; ++x) {
		printf("%d  ", 8 - x);

		for(y = 0; y < 8; ++y) {
			sq = CCH_SQUARE(y, 7 -x);
			printf("%c[38;5;%dm%c[48;5;%dm%s", 27, 232, 27, (x + y) % 2 ? 151 : 231, pstr[CCH_GET_SQUARE(&board, sq)]);
		}

		printf("%c[0m\n", 27);
	}

	fputs("\n   ", stdout);
	for(x = 'a'; x <= 'h'; ++x) putchar(x);
	putchar('\n');
}

int main(int argc, char** argv) {
	if(argc >= 2) {
		if(argc > 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
			printf("Usage: %s [FEN]\n", argv[0]);
			return 0;
		}
	}

	cch_game_state_t s;
	char prompt[32];

	if(argc == 1) {
		cch_init_board(&board);
		puts("Type h for a list of available commands.\n");
	} else {
		if(cch_load_fen(&board, argv[1]) != CCH_OK) {
			fputs("I can not parse this FEN.\n", stderr);
			return 1;
		}
	}

	do {
		print_board();
		putchar('\n');

		switch(s = cch_game_state(&board)) {
		case CCH_WHITE_TO_PLAY:
			snprintf(prompt, 32, "White to play. %u. ? ", board.turn);
			prompt_command(prompt);
			break;

		case CCH_BLACK_TO_PLAY:
			snprintf(prompt, 32, "Black to play. %u. ... ? ", board.turn);
			prompt_command(prompt);
			break;

		case CCH_CHECKMATE_WHITE:
			puts("Checkmate! 1-0");
			break;

		case CCH_CHECKMATE_BLACK:
			puts("Checkmate! 0-1");
			break;

		case CCH_STALEMATE:
			puts("Stalemate! 1/2-1/2");
			break;

		default: assert(0);
		}
	} while(s == CCH_WHITE_TO_PLAY || s == CCH_BLACK_TO_PLAY);
	return 0;
}
