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

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

#define NUM_STACKS 9
#define INITIAL_STACK_HEIGHT 8
#define MAX_STACK_HEIGHT 60

typedef struct Stack {
  char crates[MAX_STACK_HEIGHT];
  int height;
} Stack;

enum ParseStage { CRATES, COLUMNS, MOVES };

void print_stack(Stack *stack) {
  for (int i = 0; i < stack->height; i++) {
    printf("%c", stack->crates[i]);
  }
  printf(":%d\n", stack->height);
}

void print_stack_head(Stack *stack) {
  printf("%c", stack->crates[stack->height - 1]);
}

char pop(Stack *stack) {
  stack->height--;
  return stack->crates[stack->height];
}

void push(char crate, Stack *stack) {
  stack->crates[stack->height] = crate;
  stack->height++;
}

void move(int count, Stack *from, Stack *to) {
  for (int i = 0; i < count; i++) {
    push(pop(from), to);
  }
}

void move2(int count, Stack *from, Stack *to) {
  char crates[count];
  int index = 0;
  for (int i = 0; i < count; i++) {
    crates[index++] = pop(from);
  }
  for (int i = 0; i < count; i++) {
    push(crates[(index--) - 1], to);
  }
}

int main() {
  enum ParseStage p = CRATES;

  char *line;
  int stack_height = INITIAL_STACK_HEIGHT - 1;

  Stack stacks[NUM_STACKS + 1];
  for (int i = 1; i <= NUM_STACKS; i++) {
    for (int j = 0; j < MAX_STACK_HEIGHT; j++) {
      stacks[i].crates[j] = '\0';
    }
    stacks[i].height = -1;
  }

  while ((line = aoc_read_line()) != NULL) {
    switch(p) {
    case CRATES:
      if (line[1] == '1') {
	p = COLUMNS;
	break;
      }
      for (int i = 1; i <= NUM_STACKS; i++) {
	stacks[i].crates[stack_height] = line[(i * 4) - 3];
	if (stacks[i].crates[stack_height] != ' ' && stacks[i].height == -1) {
	  stacks[i].height = stack_height + 1;
	}
      }
      stack_height--;
      break;
    case COLUMNS:
      p = MOVES;
      break;
    case MOVES:
      int count, from, to;
      sscanf(line, "move %d from %d to %d", &count, &from, &to);
      move(count, &(stacks[from]), &(stacks[to]));
      break;
    }
  }

  printf("Part 1: ");
  for (int i = 1; i < NUM_STACKS + 1; i++) {
    print_stack_head(&(stacks[i]));
  }
  printf("\nPart 2: ");

  for (int i = 1; i <= NUM_STACKS; i++) {
    for (int j = 0; j < MAX_STACK_HEIGHT; j++) {
      stacks[i].crates[j] = '\0';
    }
    stacks[i].height = -1;
  }

  aoc_reset_read_line();
  p = CRATES;
  stack_height = INITIAL_STACK_HEIGHT - 1;
  
  while ((line = aoc_read_line()) != NULL) {
    switch(p) {
    case CRATES:
      if (line[1] == '1') {
	p = COLUMNS;
	break;
      }

      for (int i = 1; i <= NUM_STACKS; i++) {
	stacks[i].crates[stack_height] = line[(i * 4) - 3];
	if (stacks[i].crates[stack_height] != ' ' && stacks[i].height == -1) {
	  stacks[i].height = stack_height + 1;
	}
      }
      stack_height--;
      break;
    case COLUMNS:
      p = MOVES;
      break;
    case MOVES:
      int count, from, to;
      sscanf(line, "move %d from %d to %d", &count, &from, &to);
      move2(count, &(stacks[from]), &(stacks[to]));
      break;
    }
  }
  for (int i = 1; i < NUM_STACKS + 1; i++) {
    print_stack_head(&(stacks[i]));
  }
  printf("\n");

  aoc_free();
  return 0;
}