#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aocdailies.h"
#include "aocutils.h"

struct LampGrid {
    unsigned lit:1;
    unsigned bright:31;
};

static void gridchange(struct LampGrid *grid, int v,
                       unsigned r1, unsigned c1, unsigned r2, unsigned c2) {
    unsigned firstrow = (r1 < r2) ? r1 : r2;
    unsigned lastrow = (r1 < r2) ? r2 : r1;
    unsigned firstcol = (c1 < c2) ? c1 : c2;
    unsigned lastcol = (c1 < c2) ? c2 : c1;
    for (unsigned row = firstrow; row <= lastrow; row++) {
        for (unsigned col = firstcol; col <= lastcol; col++) {
            size_t index = 1000*row + col;
            switch (v) {
                default: fprintf(stderr, "bad value detected\n");
                         exit(EXIT_FAILURE);
                         //  break; // never reached
                case -1: grid[index].lit = 0;
                         if (grid[index].bright > 0) grid[index].bright -= 1;
                         break;
                case 1: grid[index].lit = 1;
                        grid[index].bright += 1;
                        break;
                case 2: grid[index].lit = 1 - grid[index].lit;
                        grid[index].bright += 2;
                        break;
            }
        }
    }
}

void aoc201506(char *data, size_t len) {
    (void)len; // unused argument
    struct LampGrid *grid_ptr = calloc(1000 * 1000, sizeof *grid_ptr);
    char *line = strtok(data, "\n");
    while (line) {
        unsigned x1, x2, y1, y2;
        char *err = line;
        while (!isdigit((unsigned char)*err)) err++;
        y1 = strtoul(err, &err, 10); err++;
        x1 = strtoul(err, &err, 10); while (!isdigit((unsigned char)*err)) err++;
        y2 = strtoul(err, &err, 10); err++;
        x2 = strtoul(err, &err, 10);
        if (line[6] == 'n') gridchange(grid_ptr, 1, y1, x1, y2, x2);
        if (line[6] == 'f') gridchange(grid_ptr, -1, y1, x1, y2, x2);
        if (line[6] == ' ') gridchange(grid_ptr, 2, y1, x1, y2, x2);
        line = strtok(NULL, "\n");
    }
    unsigned lit = 0, brightness = 0;
    for (unsigned row = 0; row < 1000; row++) {
        for (unsigned col = 0; col < 1000; col++) {
            lit += grid_ptr[1000*row + col].lit;
            brightness += grid_ptr[1000*row + col].bright;
        }
    }
    free(grid_ptr);
    printf("After following the instructions, there are %u lights lit.\n", lit);
    printf("Total brightness is {%u}.\n", brightness);
}

void aoc201505(char *data, size_t len) {
    (void)len; // unused argument
    unsigned nice = 0, nice2 = 0;
    char *line = strtok(data, "\n"), *curr;
    while (line) {
        unsigned vowels = 0;
        bool twice = false;
        bool hasab, hascd, haspq, hasxy;
        bool pair = false, hugged = false; // Part Two

        curr = line;
        if (strchr("aeiou", *curr)) vowels++;
        if (curr[0] == curr[1]) twice = true;
        for (curr = line + 1; *curr; curr++) {
            if (strchr("aeiou", *curr)) vowels++;
            if (curr[0] == curr[1]) twice = true;
            if (curr[-1] == curr[1]) hugged = true;
            char pp[3] = {curr[-1], curr[0], 0};
            if (strstr(curr + 1, pp)) pair = true;
        }
        hasab = (strstr(line, "ab") != NULL);
        hascd = (strstr(line, "cd") != NULL);
        haspq = (strstr(line, "pq") != NULL);
        hasxy = (strstr(line, "xy") != NULL);

        if ((vowels >= 3) && twice && !hasab && !hascd && !haspq && !hasxy) {
            nice++;
        }
        if (pair && hugged) nice2++;

        line = strtok(NULL, "\n");
    }
    printf("There are %u nice strings.\n", nice);
    printf("There are %u nice strings under the new rules.\n", nice2);
}

void aoc201504(char *data, size_t len) {
    data[--len] = 0; // remove newline, adjust `len`
    char longkey[128];
    unsigned extra = 1;
    for (;;) {
        sprintf(longkey, "%s%u", data, extra);
        unsigned char tmp[16];
        md5mini(tmp, longkey);
        if ((tmp[0] == 0) && (tmp[1] == 0) && (tmp[2] < 16)) break;
        extra++;
    }
    printf("Follow your key with %u to obtain five zeroes\n", extra);
    for (;;) {
        sprintf(longkey, "%s%u", data, extra);
        unsigned char tmp[16];
        md5mini(tmp, longkey);
        if ((tmp[0] == 0) && (tmp[1] == 0) && (tmp[2] == 0)) break;
        extra++;
    }
    printf("Follow your key with %u to obtain six zeroes\n", extra);
}

struct House {
    int row, col;
};

void aoc201503(char *data, size_t len) {
    (void)len; // unused argument
    char *data2 = data; // save it
    struct House *house = malloc(512 * sizeof *house); // assume it worked
    unsigned mhouse = 512, nhouse = 1;
    house[0].row = house[0].col = 0; // house at [0, 0] visited
    int x = 0, y = 0;
    while (*data) {
        switch (*data) {
            default: break;
            case '^': y++; break;
            case 'v': y--; break;
            case '>': x++; break;
            case '<': x--; break;
        }
        data++;
        bool visited = false;
        for (size_t k = 0; k < nhouse; k++) {
            if ((house[k].row == x) && (house[k].col == y)) {
                visited = true;
                break;
            }
        }
        if (!visited) {
            if (nhouse == mhouse) {
                // grow the array (by golden ratio)
                struct House *tmp = realloc(house, (13*mhouse) / 8 * sizeof *house);
                if (!tmp) {
                    fprintf(stderr, "no memory. program aborted\n");
                    exit(EXIT_FAILURE);
                }
                house = tmp;
                mhouse = (13*mhouse) / 8;
            }
            house[nhouse].row = x;
            house[nhouse].col = y;
            nhouse += 1;
        }
    }
    printf("Santa delivered at least 1 present to %u houses.\n", nhouse);

    // reuse house array for part 2; don't worry about its contents
    nhouse = 1; // house at [0, 0] still visited :)
    struct House p[2] = {0}; // coordinates of santa and bot
    int who = 0; // 0: santa; 1: santa bot
    // go around from the beginning
    while (*data2) {
        switch (*data2) {
            default: break;
            case '^': p[who].row++; break;
            case 'v': p[who].row--; break;
            case '>': p[who].col++; break;
            case '<': p[who].col--; break;
        }
        data2++;
        bool visited = false;
        for (size_t k = 0; k < nhouse; k++) {
            if ((house[k].row == p[who].row) && (house[k].col == p[who].col)) {
                visited = true;
                break;
            }
        }
        if (!visited) {
            if (nhouse == mhouse) {
                // grow the array (by golden ratio)
                struct House *tmp = realloc(house, (13*mhouse) / 8 * sizeof *house);
                if (!tmp) {
                    fprintf(stderr, "no memory. program aborted\n");
                    exit(EXIT_FAILURE);
                }
                house = tmp;
                mhouse = (13*mhouse) / 8;
            }
            house[nhouse].row = p[who].row;
            house[nhouse].col = p[who].col;
            nhouse += 1;
        }
        who = 1 - who; // santa; bot; santa; bot; ..., ..., ...
    }
    free(house);
    printf("Santa and santa bot delivered at least 1 present to %u houses.\n", nhouse);
}

void aoc201502(char *data, size_t len) {
    (void)len; // unused argument
    unsigned sqf = 0, f = 0;
    for (;;) {
        char *err;
        unsigned l = strtoul(data, &err, 10);
        if (*err == 0) break;
        data = err + 1; // skip 'x'
        unsigned w = strtoul(data, &err, 10);
        data = err + 1; // skip 'x'
        unsigned h = strtoul(data, &err, 10);
        data = err + 1; // skip newline
        unsigned lw = l * w;
        unsigned wh = w * h;
        unsigned hl = h * l;
        unsigned m = min3u(lw, wh, hl);
        sqf += 2*(lw + wh + hl) + m;
        unsigned M = max3u(l, w, h);
        unsigned feet = 2*(l+w+h - M) + l*h*w;
        f += feet;
    }
    printf("The elves need %u square feet of paper.\n", sqf);
    printf("The elves need %u feet of ribbon.\n", f);
}

void aoc201501(char *data, size_t len) {
    (void)len; // unused argument
    int floor = 0, basementsteps = -1;
    char *ddata = data;                            // save start
    while (*data) {
        if (*data == '(') floor++;
        else if (*data == ')') floor--;
        data++;
        // if entered the basement, part 2 done
        if (floor == -1) {
            basementsteps = data - ddata;          // steps taken
            break;
        }
    }
    // continue with no regard to basement
    while (*data) {
        if (*data == '(') floor++;
        else if (*data == ')') floor--;
        data++;
    }
    printf("Santa is taken to floor {%d}.\n", floor);
    printf("Santa first goes to the basement at step {%d}.\n", basementsteps);
}
