import { Coll, Collection, Collection2D } from "util/collection";

type MapFormat = {
  destStart: number;
  sourceStart: number;
  length: number;
};

function fillColl(iter: Iterable<string>): Collection<MapFormat> {
  const result = Coll<MapFormat>();

  for (const opts of iter) {
    if (opts === "") {
      break;
    }

    const [destStart, sourceStart, length] = opts.split(" ", 3).map(
      Number,
    );

    result.push({
      destStart,
      sourceStart,
      length,
    });
  }

  return result;
}

type MappedInputs = [seeds: Collection<number>, maps: Collection2D<MapFormat>];

// If blank strings/ints is not enough
export const inputMapper = (
  inputs: Collection<string>,
): MappedInputs => {
  const result: Collection2D<MapFormat> = Coll();

  const [_, seedStr] = inputs[0].split(": ", 2);
  const seeds = Coll(...seedStr.split(" ").map(Number));

  const iter = inputs.iter();

  for (const row of iter) {
    if (row.startsWith("seed-to-soil")) {
      result.push(fillColl(iter));
      continue;
    }

    if (row.startsWith("soil-to-fertilizer")) {
      result.push(fillColl(iter));
      continue;
    }

    if (row.startsWith("fertilizer-to-water")) {
      result.push(fillColl(iter));
      continue;
    }

    if (row.startsWith("water-to-light")) {
      result.push(fillColl(iter));
      continue;
    }

    if (row.startsWith("light-to-temperature")) {
      result.push(fillColl(iter));
      continue;
    }

    if (row.startsWith("temperature-to-humidity")) {
      result.push(fillColl(iter));
      continue;
    }

    if (row.startsWith("humidity-to-location")) {
      result.push(fillColl(iter));
      continue;
    }
  }

  return [seeds, result];
};

export function solution1([seeds, maps]: MappedInputs): number {
  const result = seeds.map((seed) =>
    maps.reduce((prev, curr) => {
      for (
        const { destStart, sourceStart, length } of curr
      ) {
        if (prev >= sourceStart && prev < sourceStart + length) {
          return destStart + (prev - sourceStart);
        }
      }

      return prev;
    }, seed)
  ).x.min();

  return result;
}

type SeedTuple = [from: number, length: number];

export function solution2([seeds, maps]: MappedInputs): number {
  const multiSeed = seeds.partition(2) as unknown as Collection<
    SeedTuple
  >;

  let result = Number.MAX_SAFE_INTEGER;

  for (const [from, length] of multiSeed) {
    for (let i = 0; i < length; i++) {
      const currSeed = from + i;
      const currResult = solution1([Coll(currSeed), maps]);

      if (currResult < result) {
        result = currResult;
      }
    }
  }

  return result;
}
