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

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

struct Map;

typedef struct Region {
  struct Map *map;
  char plant;
  int positions[1000];
  int positions_count;
} Region;

typedef struct Map {
  char *data;
  int width;
  int height;
  int input_length;
  Region regions[1000];
  int regions_count;
} Map;

int region_perimeter(Region *r) {
  int perimeter = 0;
  for (int i = 0; i < r->positions_count; i++) {
    int position = r->positions[i];
    if (position - r->map->width < 0 || r->map->data[position - r->map->width] != r->plant - 'A' + 'a') {
      perimeter++;
    }

    if (position - 1 < 0 || r->map->data[position - 1] != r->plant - 'A' + 'a') {
      perimeter++;
    }

    if (position + 1 > r->map->input_length || r->map->data[position + 1] != r->plant - 'A' + 'a') {
      perimeter++;
    }
    if (position + r->map->width > r->map->input_length || r->map->data[position + r->map->width] != r->plant - 'A' + 'a') {
      perimeter++;
    }
  }
  return perimeter;
}

int region_inner_corner_count(Region *r) {
  int inner_corners = 0;
  for (int i = 0; i < r->positions_count; i++) {
    int position = r->positions[i];
    bool up = false, down = false, left = false, right = false;
    bool up_left = false, down_left = false, up_right = false, down_right = false;

    if (position - r->map->width - 1 >= 0 && r->map->data[position - r->map->width - 1] == r->plant - 'A' + 'a') {
      up_left = true;
    }

    if (position - r->map->width >= 0 && r->map->data[position - r->map->width] == r->plant - 'A' + 'a') {
      up = true;
    }

    if (position - r->map->width + 1 >= 0 && r->map->data[position - r->map->width + 1] == r->plant - 'A' + 'a') {
      up_right = true;
    }

    if (position - 1 > 0 && r->map->data[position - 1] == r->plant - 'A' + 'a') {
      left = true;
    }

    if (position + 1 < r->map->input_length && r->map->data[position + 1] == r->plant - 'A' + 'a') {
      right = true;
    }

    if (position + r->map->width - 1 < r->map->input_length && r->map->data[position + r->map->width - 1] == r->plant - 'A' + 'a') {
      down_left = true;
    }

    if (position + r->map->width < r->map->input_length && r->map->data[position + r->map->width] == r->plant - 'A' + 'a') {
      down = true;
    }

    if (position + r->map->width + 1 < r->map->input_length && r->map->data[position + r->map->width + 1] == r->plant - 'A' + 'a') {
      down_right = true;
    }


    if (up && left && !up_left) inner_corners++;
    if (up && right && !up_right) inner_corners++;
    if (down && left && !down_left) inner_corners++;
    if (down && right && !down_right) inner_corners++;
  }

  return inner_corners;
}

int region_outer_corner_count(Region *r) {
  int outer_corners = 0;
  for (int i = 0; i < r->positions_count; i++) {
    int position = r->positions[i];
    int fences = 0;
    bool top_fence = false, left_fence = false, right_fence = false, bottom_fence = false;
    if (position - r->map->width < 0 || r->map->data[position - r->map->width] != r->plant - 'A' + 'a') {
      top_fence = true;
    }

    if (position - 1 < 0 || r->map->data[position - 1] != r->plant - 'A' + 'a') {
      left_fence = true;
    }

    if (position + 1 > r->map->input_length || r->map->data[position + 1] != r->plant - 'A' + 'a') {
      right_fence = true;
    }
    if (position + r->map->width > r->map->input_length || r->map->data[position + r->map->width] != r->plant - 'A' + 'a') {
      bottom_fence = true;
    }

    if (top_fence && left_fence) outer_corners++;
    if (bottom_fence && left_fence) outer_corners++;
    if (top_fence && right_fence) outer_corners++;
    if (bottom_fence && right_fence) outer_corners++;
  }
  return outer_corners;
}

int region_price(Region *r) {
  return region_perimeter(r) * r->positions_count;
}

int region_price_2(Region *r) {
  int roc = region_outer_corner_count(r);
  int ric = region_inner_corner_count(r);
  return (roc + ric) * r->positions_count;
}

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

  Map m;
  fflush(stdout);
  m.data = input;
  m.width = strchr(input, '\n') - input + 1;
  m.height = input_length / m.width;
  m.regions_count = 0;
  m.input_length = input_length;

  // initialize regions
  for (int i = 0; i < input_length; i++) {
    if (m.data[i] == '\n' || m.data[i] < 'A' || m.data[i] > 'Z') continue;

    Region *r = &(m.regions[m.regions_count++]);
    r->plant = m.data[i];
    r->positions_count = 0;
    r->map = &m;

    int next_positions[1000];
    next_positions[0] = i;
    int next_positions_count = 1;

    int nn_positions[1000];
    int nn_positions_count = 0;

    while (next_positions_count > 0) {
      for (int j = 0; j < next_positions_count; j++) {
	int next_position = next_positions[j];
	r->positions[r->positions_count++] = next_position;
	m.data[next_position] = m.data[next_position] - 'A' + 'a';
      }

      for (int j = 0; j < next_positions_count; j++) {
	int next_position = next_positions[j];
	if (next_position - m.width > 0 && m.data[next_position - m.width] == r->plant) {
	  nn_positions[nn_positions_count++] = next_position - m.width;
	}

	if (next_position - 1 > 0 && m.data[next_position - 1] == r->plant) {
	  nn_positions[nn_positions_count++] = next_position - 1;
	}

	if (next_position + 1 < input_length && m.data[next_position + 1] == r->plant) {
	  nn_positions[nn_positions_count++] = next_position + 1;
	}

	if (next_position + m.width < input_length && m.data[next_position + m.width] == r->plant) {
	  nn_positions[nn_positions_count++] = next_position + m.width;
	}
      }

      qsort(nn_positions, nn_positions_count, sizeof(int), aoc_sort_int);
      if (nn_positions_count == 0) break;

      next_positions_count = 1;
      next_positions[0] = nn_positions[0];
      for (int j = 1; j < nn_positions_count; j++) {
	if (nn_positions[j] != next_positions[next_positions_count - 1]) {
	  next_positions[next_positions_count++] = nn_positions[j];
	}
      }

      nn_positions_count = 0;
    }
  }

  int cost = 0;
  int cost_2 = 0;

  for (int i = 0; i < m.regions_count; i++) {
    cost += region_price(&m.regions[i]);
    cost_2 += region_price_2(&m.regions[i]);
  }

  printf("Part 1: %d\n", cost);
  printf("Part 2: %lld\n", cost_2);
  
  aoc_free();
  return 0;
}