#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "../../lib/aoc.h"

typedef int Position[2];

typedef struct Map {
  char *grid;
  int width;
  int height;
  char signal;
  int antennas[1000];
  int antenna_count;
} Map;

void position_from_index(Map *map, int index, Position *p) {
  (*p)[0] = index % map->width;
  (*p)[1] = index / map->width;
}

int index_from_position(Map *map, Position p) {
  return p[0] + p[1] * map->width;
}

bool position_inbounds(Map *map, Position p) {
  return
    p[0] >= 0 &&
    p[1] >= 0 &&
    p[0] < map->width - 1 &&
    p[1] < map->height;
}

void calculate_antinodes(Map *map, char *antinodes_grid, bool resonant) {
  int index;
  for (int i = 0; i < map->antenna_count; i++) {
    Position first_antenna;
    position_from_index(map, map->antennas[i], &first_antenna);
    for (int j = 0; j < i; j++) {
      Position second_antenna;
      position_from_index(map, map->antennas[j], &second_antenna);

      if (resonant) {
	Position antinode;

	antinode[0] = first_antenna[0];
	antinode[1] = first_antenna[1];

	while (position_inbounds(map, antinode)) {
	  index = index_from_position(map, antinode);
	  antinodes_grid[index] = '#';
	  antinode[0] = antinode[0] + first_antenna[0] - second_antenna[0];
	  antinode[1] = antinode[1] + first_antenna[1] - second_antenna[1];
	}

	antinode[0] = second_antenna[0];
	antinode[1] = second_antenna[1];

	while (position_inbounds(map, antinode)) {
	  index = index_from_position(map, antinode);
	  antinodes_grid[index] = '#';
	  antinode[0] = antinode[0] + second_antenna[0] - first_antenna[0];
	  antinode[1] = antinode[1] + second_antenna[1] - first_antenna[1];
	}
	
      } else {
	Position first_antinode, second_antinode;
	first_antinode[0] = first_antenna[0] + first_antenna[0] - second_antenna[0];
	first_antinode[1] = first_antenna[1] + first_antenna[1] - second_antenna[1];

	if (position_inbounds(map, first_antinode)) {
	  index = index_from_position(map, first_antinode);
	  antinodes_grid[index] = '#';
	}

	second_antinode[0] = second_antenna[0] + second_antenna[0] - first_antenna[0];
	second_antinode[1] = second_antenna[1] + second_antenna[1] - first_antenna[1];

	if (position_inbounds(map, second_antinode)) {
	  index = index_from_position(map, second_antinode);
	  antinodes_grid[index] = '#';
	}
      }
    }
  }
}

int main() {
  char *input = aoc_read_input();
  int input_size = strlen(input);

  Map master_map;
  master_map.grid = input;
  master_map.width = strchr(input, '\n') - input + 1;
  master_map.height = input_size / master_map.width;

  char *empty_grid = malloc(input_size + 1);
  for (int i = 0; i < input_size; i++) {
    empty_grid[i] = '.';
    if (i % master_map.width == master_map.width - 1) empty_grid[i] = '\n';
  }
  empty_grid[input_size] = '\0';

  char *antinode_grid = malloc(input_size + 1);
  strcpy(antinode_grid, empty_grid);

  char *resonant_antinode_grid = malloc(input_size + 1);
  strcpy(resonant_antinode_grid, empty_grid);

  Map maps[64];
  for (int i = 0; i < 64; i++) {
    maps[i].grid = malloc(input_size + 1);
    strcpy(maps[i].grid, empty_grid);
    maps[i].width = master_map.width;
    maps[i].height = master_map.height;
    if (i < 26) {
      maps[i].signal = i + 'a';
    } else if (i < 52) {
      maps[i].signal = i - 26 + 'A';
    } else {
      maps[i].signal = i - 52 + '0';
    }
  }

  for (int i = 0; i < input_size; i++) {
    char c = master_map.grid[i];
    if (c == '.' || c == '\n') continue;

    int map_index;
    if (c >= 'a' && c <= 'z') map_index = c - 'a';
    else if (c >= 'A' && c <= 'Z') map_index = c + 26 - 'A';
    else map_index = c + 52 - '0';

    maps[map_index].antennas[maps[map_index].antenna_count++] = i;
  }

  for (int i = 0; i < 64; i++) {
    calculate_antinodes(&(maps[i]), antinode_grid, false);
  }

  int antinode_count = 0;
  for (int i = 0; i < input_size; i++) {
    if (antinode_grid[i] == '#') antinode_count++;
  }

  printf("Part 1: %d\n", antinode_count);

  for (int i = 0; i < 64; i++) {
    calculate_antinodes(&(maps[i]), resonant_antinode_grid, true);
  }

  int resonant_antinode_count = 0;
  for (int i = 0; i < input_size; i++) {
    if (resonant_antinode_grid[i] == '#') resonant_antinode_count++;
  }
  printf("Part 2: %lld\n", resonant_antinode_count);

  free(empty_grid);
  free(antinode_grid);
  free(resonant_antinode_grid);
  for (int i = 0; i < 64; i++) free(maps[i].grid);
  
  aoc_free();
  return 0;
}