#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#define DEBUG
#include "board.h"
#include "debug.h"
#include "mq.h"
#include "tactics/2lib.h"
#include "tactics/selfatari.h"


/* Whether to avoid capturing/atariing doomed groups (this is big
 * performance hit and may reduce playouts balance; it does increase
 * the strength, but not quite proportionally to the performance). */
//#define NO_DOOMED_GROUPS


static bool
miai_2lib(struct board *b, group_t group, enum stone color)
{
	bool can_connect = false, can_pull_out = false;
	/* We have miai if we can either connect on both libs,
	 * or connect on one lib and escape on another. (Just
	 * having two escape routes can be risky.) We must make
	 * sure that we don't consider following as miai:
	 * X X X O
	 * X . . O
	 * O O X O - left dot would be pull-out, right dot connect */
	foreach_neighbor(b, board_group_info(b, group).lib[0], {
		enum stone cc = board_at(b, c);
		if (cc == S_NONE && cc != board_at(b, board_group_info(b, group).lib[1])) {
			can_pull_out = true;
		} else if (cc != color) {
			continue;
		}

		group_t cg = group_at(b, c);
		if (cg && cg != group && board_group_info(b, cg).libs > 1)
			can_connect = true;
	});
	foreach_neighbor(b, board_group_info(b, group).lib[1], {
		enum stone cc = board_at(b, c);
		if (c == board_group_info(b, group).lib[0])
			continue;
		if (cc == S_NONE && can_connect) {
			return true;
		} else if (cc != color) {
			continue;
		}

		group_t cg = group_at(b, c);
		if (cg && cg != group && board_group_info(b, cg).libs > 1)
			return (can_connect || can_pull_out);
	});
	return false;
}

static bool
defense_is_hopeless(struct board *b, group_t group, enum stone owner,
			enum stone to_play, coord_t lib, coord_t otherlib,
			bool use)
{
	/* If we are the defender not connecting out, do not
	 * escape with moves that do not gain liberties anyway
	 * - either the new extension has just single extra
	 * liberty, or the "gained" liberties are shared. */
	/* XXX: We do not check connecting to a short-on-liberty
	 * group (e.g. ourselves). */
	if (DEBUGL(7))
		fprintf(stderr, "\tif_check %d and defending %d and uscount %d ilcount %d\n",
			use, to_play == owner,
			neighbor_count_at(b, lib, owner),
			immediate_liberty_count(b, lib));
	if (!use)
		return false;
	if (to_play == owner && neighbor_count_at(b, lib, owner) == 1) {
		if (immediate_liberty_count(b, lib) == 1)
			return true;
		if (immediate_liberty_count(b, lib) == 2
		    && coord_is_adjecent(lib, otherlib, b))
			return true;
	}
	return false;
}

void
can_atari_group(struct board *b, group_t group, enum stone owner,
		  enum stone to_play, struct move_queue *q,
		  int tag, bool use_def_no_hopeless)
{
	bool have[2] = { false, false };
	bool preference[2] = { true, true };
	for (int i = 0; i < 2; i++) {
		coord_t lib = board_group_info(b, group).lib[i];
		assert(board_at(b, lib) == S_NONE);
		if (!board_is_valid_play(b, to_play, lib))
			continue;

		if (DEBUGL(6))
			fprintf(stderr, "- checking liberty %s of %s %s, filled by %s\n",
				coord2sstr(lib, b),
				stone2str(owner), coord2sstr(group, b),
				stone2str(to_play));

		/* Don't play at the spot if it is extremely short
		 * of liberties... */
		/* XXX: This looks harmful, could significantly
		 * prefer atari to throwin:
		 *
		 * XXXOOOOOXX
		 * .OO.....OX
		 * XXXOOOOOOX */
#if 0
		if (neighbor_count_at(b, lib, stone_other(owner)) + immediate_liberty_count(b, lib) < 2)
			continue;
#endif

		/* Prevent hopeless escape attempts. */
		if (defense_is_hopeless(b, group, owner, to_play, lib,
					board_group_info(b, group).lib[1 - i],
					use_def_no_hopeless))
			continue;

#ifdef NO_DOOMED_GROUPS
		/* If the owner can't play at the spot, we don't want
		 * to bother either. */
		if (is_bad_selfatari(b, owner, lib))
			continue;
#endif

		/* Of course we don't want to play bad selfatari
		 * ourselves, if we are the attacker... */
		if (
#ifdef NO_DOOMED_GROUPS
		    to_play != owner &&
#endif
		    is_bad_selfatari(b, to_play, lib)) {
			if (DEBUGL(7))
				fprintf(stderr, "\tliberty is selfatari\n");
			coord_t coord = pass;
			group_t bygroup = 0;
			if (to_play != owner) {
				/* Okay! We are attacker; maybe we just need
				 * to connect a false eye before atari - this
				 * is very common in the corner. */
				coord = selfatari_cousin(b, to_play, lib, &bygroup);
			}
			if (is_pass(coord))
				continue;
			/* Ok, connect, but prefer not to. */
			enum stone byowner = board_at(b, bygroup);
			if (DEBUGL(7))
				fprintf(stderr, "\treluctantly switching to cousin %s (group %s %s)\n",
					coord2sstr(coord, b), coord2sstr(bygroup, b), stone2str(byowner));
			/* One more thing - is the cousin sensible defense
			 * for the other group? */
			if (defense_is_hopeless(b, bygroup, byowner, to_play,
						coord, lib,
						use_def_no_hopeless))
				continue;
			lib = coord;
			preference[i] = false;

		/* By now, we must be decided we add the move to the
		 * queue!  [comment intentionally misindented] */

		}

		have[i] = true;

		/* If the move is too "lumpy", prefer the alternative:
		 *
		 * #######
		 * ..O.X.X <- always play the left one!
		 * OXXXXXX */
		if (neighbor_count_at(b, lib, to_play) + neighbor_count_at(b, lib, S_OFFBOARD) >= 3) {
			if (DEBUGL(7))
				fprintf(stderr, "\tlumpy: mine %d + edge %d\n",
					neighbor_count_at(b, lib, to_play),
					neighbor_count_at(b, lib, S_OFFBOARD));
			preference[i] = false;
		}

		if (DEBUGL(6))
			fprintf(stderr, "+ liberty %s ready with preference %d\n", coord2sstr(lib, b), preference[i]);

		/* If we prefer only one of the moves, pick that one. */
		if (i == 1 && have[0] && preference[0] != preference[1]) {
			if (!preference[0]) {
				if (q->move[q->moves - 1] == board_group_info(b, group).lib[0])
					q->moves--;
				/* ...else{ may happen, since we call
				 * mq_nodup() and the move might have
				 * been there earlier. */
			} else {
				assert(!preference[1]);
				continue;
			}
		}

		/* Tasty! Crispy! Good! */
		mq_add(q, lib, tag);
		mq_nodup(q);
	}

	if (DEBUGL(7)) {
		char label[256];
		snprintf(label, 256, "= final %s %s liberties to play by %s",
			stone2str(owner), coord2sstr(group, b),
			stone2str(to_play));
		mq_print(q, b, label);
	}
}

void
group_2lib_check(struct board *b, group_t group, enum stone to_play, struct move_queue *q, int tag, bool use_miaisafe, bool use_def_no_hopeless)
{
	enum stone color = board_at(b, group_base(group));
	assert(color != S_OFFBOARD && color != S_NONE);

	if (DEBUGL(5))
		fprintf(stderr, "[%s] 2lib check of color %d\n",
			coord2sstr(group, b), color);

	/* Do not try to atari groups that cannot be harmed. */
	if (use_miaisafe && miai_2lib(b, group, color))
		return;

	can_atari_group(b, group, color, to_play, q, tag, use_def_no_hopeless);

	/* Can we counter-atari another group, if we are the defender? */
	if (to_play != color)
		return;
	foreach_in_group(b, group) {
		foreach_neighbor(b, c, {
			if (board_at(b, c) != stone_other(color))
				continue;
			group_t g2 = group_at(b, c);
			if (board_group_info(b, g2).libs == 1) {
				/* We can capture a neighbor. */
				mq_add(q, board_group_info(b, g2).lib[0], tag);
				mq_nodup(q);
				continue;
			}
			if (board_group_info(b, g2).libs != 2)
				continue;
			can_atari_group(b, g2, stone_other(color), to_play, q, tag, use_def_no_hopeless);
		});
	} foreach_in_group_end;
}
