import { socket } from "./main.js";
import { Packet } from "./packet.js";

const REGIONS = {};

class Continent {
    constructor(name) {
        this.name = name;
        this.yield = 0;
    }
}

class Strength {
    constructor(cipherText, regionName) {
        this.cipherText = cipherText;
        this.assumedStrength = null;

        this.prover = null;
        document.addEventListener("PROOF", (ev) => {
            const data = ev.detail;

            if (
                data.region === regionName &&
                data.stage === "CHALLENGE" &&
                this.prover !== null
            ) {
                let z = this.prover.prove(BigInt(data.challenge));

                socket.emit(
                    "message",
                    Packet.createProof(regionName, "0x" + z.toString(16))
                );
            }
        });

        this.verifier = null;
        document.addEventListener("PROOF", (ev) => {
            const data = ev.detail;

            if (
                data.region === regionName &&
                data.stage === "PROOF" &&
                this.verifier !== null
            ) {
                let result = this.verifier.verify(BigInt(data.z));
                if (result > 0) {
                    this.assumedStrength = this.verifier.plainText;
                    document.dispatchEvent(new CustomEvent("updateStrengths"));
                } else {
                    console.warn(`Failed to verify ciphertext! ${result}`);
                }
            }
        });
    }

    transparentUpdate(value) {
        this.cipherText.update(new Ciphertext(this.cipherText.pubKey, value, 0n));

        if (this.assumedStrength !== null) {
            this.assumedStrength += value;
        }
    }

    update(cipherText) {
        if (this.cipherText === null) {
            this.cipherText = cipherText;
        } else {
            this.cipherText.update(cipherText);
        }

        this.assumedStrength = null;
    }

    prove(region) {
        if (this.cipherText.readOnly) {
            return;
        }

        this.prover = this.cipherText.prove();

        socket.emit(
            "message",
            Packet.createProofConjecture(
                region,
                "0x" + this.cipherText.plainText.toString(),
                "0x" + this.prover.a.toString(16)
            )
        );
    }

    verify(region, plainText, a) {
        if (!this.cipherText.readOnly) {
            return;
        }

        this.verifier = this.cipherText.prove(plainText, a);

        socket.emit(
            "message",
            Packet.createProofChallenge(
                region,
                "0x" + this.verifier.challenge.toString(16)
            )
        );
    }
}

export class Region {
    constructor(name, continent) {
        this.name = name;
        this.owner = null;
        this.strength = new Strength(null, name);
        this.neighbours = new Set();
        this.continent = continent;

        REGIONS[name] = this;
    }

    static setNeighbours(region1, region2) {
        region1.neighbours.add(region2);
        region2.neighbours.add(region1);
    }

    static allRegionsClaimed() {
        return (
            Object.values(REGIONS).find((region) => region.owner === null) === undefined
        );
    }

    static getRegion(name) {
        return REGIONS[name];
    }

    static getAllRegions() {
        return Object.values(REGIONS);
    }

    claim(player, cipherText) {
        this.owner = player;
        this.strength.update(cipherText);
        this.strength.assumedStrength = 1n;
    }

    reinforce(cipherText) {
        this.strength.update(cipherText);
    }

    isClaimed() {
        return this.strength.cipherText !== null;
    }

    displayStrength() {
        if (this.owner === null) {
            return "";
        } else if (!this.strength.cipherText.readOnly) {
            return this.strength.cipherText.plainText.toString();
        } else if (this.strength.assumedStrength !== null) {
            return this.strength.assumedStrength.toString();
        } else {
            return "?";
        }
    }

    prove() {
        this.strength.prove(this.name);
    }

    requestProof() {
        socket.emit("message", Packet.createProofRequest(this.name));
    }

    verify(plainText, a) {
        this.strength.verify(this.name, plainText, a);
    }
}

const EAST = new Continent("East");
const WEST = new Continent("West");

const A = new Region("A", EAST);
const B = new Region("B", EAST);
const C = new Region("C", EAST);
const D = new Region("D", EAST);
const J = new Region("J", EAST);

const F = new Region("F", WEST);
const G = new Region("G", WEST);
const H = new Region("H", WEST);
const I = new Region("I", WEST);
const E = new Region("E", WEST);

Region.setNeighbours(A, B);
Region.setNeighbours(A, C);
Region.setNeighbours(B, C);
Region.setNeighbours(B, J);
Region.setNeighbours(C, D);
Region.setNeighbours(C, F);
Region.setNeighbours(E, J);
Region.setNeighbours(E, I);
Region.setNeighbours(E, H);
Region.setNeighbours(F, J);
Region.setNeighbours(F, G);
Region.setNeighbours(G, H);
Region.setNeighbours(G, I);
Region.setNeighbours(H, I);
