import std.algorithm;
import std.typecons;

import dayutil;

struct ParsedInput {
	long timestamp;
	long[] busses;
}

Variant run(int part, File input, bool bigboy, string[] args) {
	ParsedInput pInput = input.byLineCopy.array.parseInput();
	Variant result = parts!long(part, 
			() => part1(pInput),
			() => part2(pInput));
	return result;
}

 ParsedInput parseInput(Range)(Range range) if (isInputRange!Range && isSomeString!(ElementType!Range)) {
	import std.conv;
	ParsedInput input;
	input.timestamp = to!int(range[0]);

	input.busses = range[1].splitter(',').map!(c => c == "x" ? -1 : c.to!long).array;
	return input;
}

long part1(ParsedInput input) {
	long earliestBus = 0;
	long earliestBusTimestamp = long.max;
	foreach(long bus; input.busses.filter!(x => x != -1)) {
		long closestTimestamp = (input.timestamp / bus + 1 ) * bus;
		//writeln(closestTimestamp);
		if (closestTimestamp < earliestBusTimestamp) {
			earliestBusTimestamp = closestTimestamp;
			earliestBus = bus;
		}
	}
	// debug writefln("Bus %d at %d", earliestBus, earliestBusTimestamp);
	return earliestBus * (earliestBusTimestamp - input.timestamp);
}

long part2(ParsedInput input) {
	/+long[] remainders;
	long[] divisors;

	// Too much, but we're not short on memory
	remainders.reserve(input.busses.length);
	divisors.reserve(input.busses.length);
	foreach (remainder, divisor; input.busses) {
		if (divisor < 0) continue;
		remainders ~= [remainder % divisor];
		divisors ~= [divisor];
	}

	writeln(remainders);
	writeln(divisors);

	long N = divisors.fold!((a,b) => a * b);
	writeln(N);
	long[] Ns; Ns.length = divisors.length;
	long[] Xs; Xs.length = divisors.length;
	long[] BNXs; BNXs.length = divisors.length;

	foreach(i, divisor; divisors) {
		Ns[i] = N / divisor;
	}

	foreach(ref Xi, Ni, Di; lockstep(Xs, Ns, divisors)) {
		long a = Ni % Di;
		long b;
		for(b = 0; (a * b) % Di != 1; b++) {}
		Xi = b;
	}
	writeln(Xs);

	foreach(ref BNXi, Ri, Ni, Xi; lockstep(BNXs, remainders, Ns, Xs)) {
		BNXi = Ri * Ni * Xi;
	}
	long result = BNXs.sum() % N;+/
	Tuple!(size_t, "index", long, "value")[] busses = input.busses.enumerate.filter!((e) => e.value != -1).array;

	long time = 0;
	long step = busses[0].value;

	foreach(bus; busses[1..$]) {
		for(; (time + bus.index) % bus.value != 0; time += step) {}
		step *= bus.value;
	}
	return time;
}

unittest {
	string[] input = [
		"939",
		"7,13,x,x,59,x,31,19"
	];
	
	ParsedInput pInput = parseInput(input);

	assert(part1(pInput) == 295);
	ParsedInput p2;
	p2.busses = [-1, 7, -1, 5, -1, -1, 8];
	//assert(part2(p2) == 78);

	assert(part2(pInput) == 1068781);
}
