using Core.Entities;
using Core.Utility;
using Google.OrTools.Sat;
namespace Core.Calculation;
public class TeamScheduler_Prototype
{
	public void Solve()
	{
		// Model.
		var model = new CpModel();

		// Data
		int[,] m =
		{
			{ 1, 0, 0, 0, 0, 1 }, // 1
			{ 0, 0, 1, 0, 0, 0 }, // 2
			{ 0, 0, 1, 0, 1, 0 }, // 3
			{ 0, 0, 0, 0, 1, 1 }, // 4
			{ 0, 0, 1, 0, 0, 0 }, // 5
			{ 1, 0, 1, 0, 0, 0 }, // 6
			{ 0, 0, 0, 0, 0, 1 }, // 7
			{ 0, 1, 0, 1, 0, 0 }, // 8
			{ 0, 0, 0, 0, 0, 0 }, // 9
			{ 1, 1, 0, 0, 1, 0 }, // 10
			{ 1, 1, 0, 0, 1, 1 }, // 11
			{ 0, 0, 0, 0, 1, 1 }, // 12
			{ 1, 0, 0, 0, 0, 1 }, // 13
			{ 0, 1, 1, 1, 0, 0 }, // 14
			{ 1, 1, 0, 0, 0, 0 }, // 15
			{ 0, 0, 0, 0, 0, 0 }, // 16
			{ 1, 0, 1, 0, 1, 1 }, // 17
			{ 0, 0, 0, 1, 0, 0 }, // 18
			{ 1, 0, 0, 1, 0, 0 }, // 19
			{ 0, 0, 1, 0, 1, 0 }, // 20
		};

		// Variables.
		int[] individuals = Enumerable.Range(0, 20).ToArray();
		int[] teams = Enumerable.Range(0, 6).ToArray();
		int[] timeSlots = Enumerable.Range(0, 3).ToArray();

		// x - 1 if meeting of team t takes place at time slot s, else 0
		var x = new IntVar[teams.Length, timeSlots.Length];
		foreach (var t in teams)
		foreach (var s in timeSlots)
			x[t, s] = model.NewIntVar(0, 1,$"team time slots[{t},{s}]");

		// y - 1 if individual i has meetings at time slot s, 0 otherwise
		var y = new IntVar[individuals.Length, timeSlots.Length];
		foreach (var i in individuals)
		foreach (var s in timeSlots)
			y[i, s] = model.NewIntVar(0, 1, $"individual time slots[{i},{s}]");

		// each team meets exactly one time
		foreach (var t in teams)
			model.AddLinearConstraint(LinearExpr.Sum(timeSlots.Select(s => x[t, s])), 1L, 1L);

		//individual must have at least one team meeting at the given time slot to attend
		foreach (var i in individuals)
		foreach (var s in timeSlots)
			model.Add(
				new BoundedLinearExpression(
					y[i, s],
					LinearExpr.Sum(teams.Select(t => m[i, t] * x[t, s])),
					false));

		// maximize number of times individuals meet
		var indTimeSlotVars = LinearExpr.NewBuilder();
		foreach (var i in individuals)
		foreach (var s in timeSlots)
			indTimeSlotVars.Add(y[i, s]);
		model.Minimize(indTimeSlotVars);

		var solver = new CpSolver();
		var cpSolverStatus = solver.Solve(model);
		Console.WriteLine($"Solver status: {cpSolverStatus}");

		if (cpSolverStatus is CpSolverStatus.Optimal or CpSolverStatus.Feasible)
		{
			Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n");

			Console.WriteLine("Team's Timeslots");
			TextUtil.ConsoleWriteTable((t, s) => solver.Value(x[t, s]) > 0, "team", teams, "timeslot", timeSlots);

			Console.WriteLine("Individual's Timeslots");
			TextUtil.ConsoleWriteTable((i, s) => solver.Value(y[i, s]) > 0, "ind", individuals, "timeslot", timeSlots);
		}
		else
		{
			Console.WriteLine("No solution found.");
		}
	}
}