/* b01902062 藍ĉŒşç‘‹ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "logger.h"
#include "xwrap.h"

#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

typedef struct {
	pid_t pid;
	int   read;
	int   write;
	bool  working;
	XBuf  buf;
} JudgeData;

typedef struct {
	int   id;
	long  score;
} PlayerData;

static int player_sort_cb (const void* aa, const void* bb) {
	const PlayerData* a = aa;
	const PlayerData* b = bb;
	if (a->score > b->score) {
		return -1;
	} else if (a->score < b->score) {
		return 1;
	} else {
		if (a->id > b->id) {
			return 1;
		} else if (a->id < b->id) {
			return -1;
		}
	}
	return 0;
}

static bool comb4 (int players[], long player_num) {
	for (int i = 3; i >= 0; i--) {
		if (players[i] + 1 <= player_num &&
			players[i] + 1 + (3 - i) <= player_num) {
			players[i]++;
			for (int j = i + 1; j < 4; j++) {
				players[j] = players[j - 1] + 1;
			}
			return true;
		}
	}
	return false;
}

static int nextjudge (JudgeData* jd, long judge_num) {
	for (int i = 0; i < judge_num; i++) {
		if (!(jd[i].working)) {
			return i;
		}
	}
	return -1;
}

static int nextscore (Comp135* comp, JudgeData* jd, long judge_num,
	PlayerData* pd, long player_num, struct pollfd* jpoll) {

repoll:
	if (poll (jpoll, judge_num, -1) <= 0) {
		fprintf (stderr, "Poll fail: %s\n", strerror (errno));
		return -1;
	}

	for (int i = 0; i < judge_num; i++) {
		if (jpoll[i].revents & POLLIN || jpoll[i].revents & POLLPRI) {
			comp135_log (comp, "Waiting for judge %d input", i + 1);
			for (int j = 0; j < 4; j++) {
				char* r;
				while (
					(r = xgetline (jpoll[i].fd, &jd[i].buf, '\n')) == NULL &&
					!(jd[i].buf.buf_error) && !(jd[i].buf.buf_eof));
				if (r == NULL) {
					goto repoll;
				}

				long id, rank, scr;
				if (sscanf (r, "%ld %ld", &id, &rank) < 2) {
					comp135_log (comp, "Judge %d send bad input %s", i + 1, r);
					j--;
					continue;
				}
				free (r);

				if (id > player_num || id <= 0) {
					comp135_log (comp, "Bad player ID: %ld", player_num);
					j--;
					continue;
				}
				if (rank > 4 || rank < 1) {
					comp135_log (comp, "Bad rank: %ld", rank);
					j--;
					continue;
				}

				scr = 4 - rank;
				comp135_log (comp, "Player %ld gets %ld points", id, scr);
				pd[id - 1].score += scr;
				comp135_log (comp, "Player %ld has %ld points now", id, pd[id - 1].score);
			}
			comp135_log (comp, "Judge %d is available now", i + 1);
			jd[i].working = false;
			break;
		}
	}

	return 0;
}

int main (int argc, char* argv[]) {
	if (argc < 3) {
		fprintf (stderr, "Usage: %s judge_num player_num\n", argv[0]);
		return 1;
	}

	long judge_num, player_num;

	if (xatol (argv[1], &judge_num) < 0) {
		fprintf (stderr, "%s: `%s\' is not a number\n", argv[0], argv[1]);
		return 2;
	}
	if (judge_num < 0) {
		fprintf (stderr, "%s: judge_num must be a positive number\n", argv[0]);
		return 3;
	}

	if (xatol (argv[2], &player_num) < 0) {
		fprintf (stderr, "%s: `%s\' is not a number\n", argv[0], argv[2]);
		return 2;
	}
	if (player_num < 4) {
		fprintf (stderr, "%s: player_num must be greater than 4\n", argv[0]);
		return 3;
	}

	struct sigaction sa = {
		.sa_handler = SIG_IGN,
		.sa_flags = 0
	};
	sigemptyset (&sa.sa_mask);
	sigaction (SIGPIPE, &sa, NULL);

	Comp135 comp_struct, *comp;
	comp = &comp_struct;

	comp135_init (comp, argv[0], false);

	JudgeData* jd = xmalloc (sizeof (JudgeData) * judge_num);
	PlayerData* pd = xmalloc (sizeof (PlayerData) * player_num);
	struct pollfd* jpoll = xmalloc (sizeof (struct pollfd) * judge_num);

	for (int i = 0; i < player_num; i++) {
		pd[i].id = i + 1;
		pd[i].score = 0;
	}
	for (int i = 0; i < judge_num; i++) {
		int fdr[2], fdw[2];
		if (pipe (fdr) < 0) {
			fprintf (stderr, "%s: pipe: %s\n", argv[0], strerror (errno));
			return 4;
		}
		if (pipe (fdw) < 0) {
			fprintf (stderr, "%s: pipe: %s\n", argv[0], strerror (errno));
			return 4;
		}

		jd[i].pid = fork ();
		if (jd[i].pid < 0) {
			fprintf (stderr, "%s: fork: %s\n", argv[0], strerror (errno));
			return 5;
		} else if (jd[i].pid > 0) {
			close (fdr[1]);
			close (fdw[0]);
		} else {
			close (fdr[0]);
			close (fdw[1]);
			dup2 (fdw[0], STDIN_FILENO);
			close (fdw[0]);
			dup2 (fdr[1], STDOUT_FILENO);
			close (fdr[1]);
			char* myjudge = xgetres ("judge");
			execl (myjudge, "judge", xsprintf ("%d", i + 1), (char*)NULL);
			fprintf (stderr, "%s: execl: %s: %s\n", argv[0], myjudge, strerror (errno));
			_exit (1);
		}

		jpoll[i].fd = fdr[0];
		jpoll[i].events = POLLIN | POLLPRI;
		jpoll[i].revents = 0;
		jd[i].read = fdr[0];
		jd[i].write = fdw[1];
		jd[i].working = false;
		xbufinit (&jd[i].buf);
		comp135_log (comp, "Judge %d created: PID = %u, read = %d, write = %d",
			i + 1, jd[i].pid, jd[i].read, jd[i].write);
	}

	int players[4] = {1, 2, 3, 4};
	long completed_jobs = 0;
	long sent_jobs = 0;
	do {
		char msg[20];
		int msglen;
		int jnext;
		msglen = snprintf (msg, 20, "%d %d %d %d\n",
			players[0], players[1], players[2], players[3]);

		while ((jnext = nextjudge (jd, judge_num)) < 0) {
			if (nextscore (comp, jd, judge_num, pd, player_num, jpoll) < 0) {
				continue;
			}
			completed_jobs++;
		}

		comp135_log (comp, "Sending %d %d %d %d to judge %d",
			players[0], players[1], players[2], players[3], jnext + 1);
		write (jd[jnext].write, msg, msglen);
		comp135_log (comp, "Judge %d is working now", jnext + 1);
		jd[jnext].working = true;
		sent_jobs++;
	} while (comb4 (players, player_num));

	for (;
		completed_jobs < sent_jobs &&
		nextscore (comp, jd, judge_num, pd, player_num, jpoll) >= 0;
		completed_jobs++);

	for (int i = 0; i < judge_num; i++) {
		const char msg[] = "-1 -1 -1 -1\n";
		const int msglen = STATIC_STRLEN (msg);
		write (jd[i].write, msg, msglen);
	}

	qsort (pd, player_num, sizeof (PlayerData), player_sort_cb);

	printf ("%d", pd[0].id);
	for (int i = 1; i < player_num; i++) {
		printf (" %d", pd[i].id);
	}

	for (int i = 0; i < player_num; i++) {
		comp135_log (comp, "Rank %d = Player %d with %ld points",
			i + 1, pd[i].id, pd[i].score);
	}

	free (jd);
	free (pd);
	free (jpoll);

	comp135_destroy (comp);

	return 0;
}