export type Direction = "R" | "L" | "U" | "D";

export type Instruction = {
    direction: Direction;
    steps: number;
    color: number;
}

export function parseInput(content: string): Instruction[] {
    const lines = content.split("\n").map(x => x.trim()).filter(x => x.length > 0);
    return lines.map(line => {
        const m = /^([RDLU])\s+(\d+)\s+\(#([0-9a-f]+)\)$/.exec(line);
        if (!m) {
            throw new Error(`unknown line ${line}`);
        }
        return {
            direction: m[1] as Direction,
            steps: parseInt(m[2]),
            color: parseInt(m[3], 16),
        }
    });
}

export async function readInput(filename: string): Promise<Instruction[]> {
    const input = await Deno.readTextFile(filename);
    return parseInput(input);
}


/**
 * Clockwise direction
 */
const CW = [
    "R", "D", "L", "U"
]
/**
 * generate a line of edge
 * @param insts array of instructions
 */
export function transformEdgeLine(insts: Instruction[]) {
    const newInst = [];
    let nextInstBonus = 1;
    for (let i = 0; i < insts.length - 1; i++) {
        const inst = insts[i];
        const nextInst = insts[i + 1];
        const dir = CW.indexOf(inst.direction);
        const nextDir = CW.indexOf(nextInst.direction);
        if (dir === nextDir) {
            nextInstBonus += inst.steps;
        }
        else if ((dir + 2) % CW.length === nextDir) {
            throw new Error("invalid direction");
        }
        else if ((dir + 1) % CW.length === nextDir) {
            newInst.push({
                direction: inst.direction,
                steps: inst.steps + nextInstBonus,
                color: inst.color
            });
            nextInstBonus = 1;
        }
        else if ((dir + CW.length - 1) % CW.length === nextDir) {
            newInst.push({
                direction: inst.direction,
                steps: inst.steps + nextInstBonus - 1,
                color: inst.color
            });
            nextInstBonus = 0;
        }
        else {
            console.log(`unknown direction ${inst.direction} ${nextInst.direction}`);
        }
    }
    newInst.push({
        direction: insts[insts.length - 1].direction,
        steps: insts[insts.length - 1].steps + nextInstBonus,
        color: insts[insts.length - 1].color
    });
    return newInst;
}

export function calcArea(insts: Instruction[]) {
    let pos = [0, 0];
    let area = 0;
    transformEdgeLine(insts).forEach(inst => {
        if (inst.direction === "R") {
            pos = [pos[0] + inst.steps, pos[1]];
            area += inst.steps * pos[1];
        }
        if (inst.direction === "L") {
            pos = [pos[0] - inst.steps, pos[1]];
            area -= inst.steps * pos[1];
        }
        if (inst.direction === "U") {
            pos = [pos[0], pos[1] - inst.steps];
        }
        if (inst.direction === "D") {
            pos = [pos[0], pos[1] + inst.steps];
        }
    });
    return Math.abs(area);
}