// priority: 0

function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }
    const toHex = n => {
        return Math.round(255 * n).toString(16).padStart(2, '0');
    }
    // The JS engine seems to think that templates aren't actually strings??
    return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toString();
}

function Tier(name, colorValue) {
    this.name = name;
    this.colorValue = colorValue;
};

Tier.prototype.displayName = function() {
    return this.name.charAt(0).toUpperCase() + this.name.slice(1);
};

Tier.prototype.toString = function() {
    return this.name;
};

function Element(name, hue) {
    this.name = name;
    this.hue = hue;
};

Element.prototype.displayName = function() {
    return this.name.charAt(0).toUpperCase() + this.name.slice(1);
};

Element.prototype.toString = function() {
    return this.name;
};

function Grist(element, tier) {
    this.element = element;
    this.tier = tier;
    this.state = 'item';
    this.isExact = false;
};

Grist.Tier = Tier;
Grist.Element = Element;

// Hues are 0-255
// -1 means white, as special case
Grist.Types = {
    build: new Element('build', 153),
    agricultural: new Element('agricultural', 36),
    intelligent: new Element('intelligent', 77),
    energetic: new Element('energetic', 0),
    primordeal: new Element('primordeal', 203),
    universal: new Element('universal', -1)
};

Grist.Elements = [
    Grist.Types.build,
    Grist.Types.agricultural,
    Grist.Types.intelligent,
    Grist.Types.energetic
];

Grist.Primitives = [
    Grist.Types.primordeal,
    Grist.Types.universal
];

// Color values are 0-1 floats
Grist.Tiers = {
    basic: new Tier('basic', 0.5),
    improved: new Tier('improved', 0.75),
    radiant: new Tier('radiant', 1.0)
};

Grist.Tiers.forEach = function(f) {
    [Grist.Tiers.basic, Grist.Tiers.improved, Grist.Tiers.radiant].forEach(f);
}

Grist.forEach = function(f) {
    Grist.Tiers.forEach(tier => {
        Grist.Elements.forEach(element => {
            f(new Grist(element, tier));
        });
        Grist.Primitives.forEach(element => {
            f(new Grist(element, tier));
        });
    });
};

Grist.forEachBlock = function(f) {
    Grist.Tiers.forEach(tier => {
        Grist.Elements.forEach(element => {
            f(new Grist(element, tier).block());
        });
        Grist.Primitives.forEach(element => {
            f(new Grist(element, tier).block());
        });
    });
};

Grist.forEachFluid = function(f) {
    Grist.Tiers.forEach(tier => {
        Grist.Elements.forEach(element => {
            f(new Grist(element, tier).fluid());
        });
        Grist.Primitives.forEach(element => {
            f(new Grist(element, tier).fluid());
        });
    });
};

Grist.of = function(element, tier) {
    return new Grist(Grist.Types[element], Grist.Tiers[tier]);
};

Grist.prototype.toString = function() {
    return `Grist(${this.id()}, color: ${this.color()})`;
};

Grist.prototype.block = function() {
    this.state = 'block';
    return this;
};

Grist.prototype.fluid = function() {
    this.state = 'fluid';
    return this;
};

Grist.prototype.exact = function() {
    this.isExact = true;
    return this;
};

Grist.prototype.color = function() {
    //return '#0d6aff';
    if (this.element.hue == -1) {
        return HSVtoRGB(0, 0, this.tier.colorValue);
    } else {
        return HSVtoRGB(this.element.hue/255.0, 1.0, this.tier.colorValue);
    }
};

Grist.prototype.displayName = function() {
    switch(this.state) {
        case 'item': return `${this.tier.displayName()} ${this.element.displayName()} Grist`;
        case 'fluid': return `Liquid ${this.tier.displayName()} ${this.element.displayName()} Grist`;
        case 'block': return `Block of ${this.tier.displayName()} ${this.element.displayName()} Grist`;
    }
};

Grist.prototype.key = function() {
    switch(this.state) {
        case 'item': return `grist_${this.element.name}_${this.tier.name}`;
        case 'fluid': return `grist_${this.element.name}_${this.tier.name}_fluid`;
        case 'block': return `grist_${this.element.name}_${this.tier.name}_block`;
    }
};

Grist.prototype.tag = function() {
    switch(this.state) {
        case 'item': return `kubejs:grist/${this.element.name}/${this.tier.name}`;
        case 'fluid': return `kubejs:grist/${this.element.name}/${this.tier.name}/fluid`;
        case 'block': return `kubejs:grist/${this.element.name}/${this.tier.name}/block`;
    }
};

Grist.prototype.elementalTag = function() {
    switch(this.state) {
        case 'item': return `kubejs:grist/${this.element.name}`;
        case 'fluid': return `kubejs:grist/${this.element.name}/fluid`;
        case 'block': return `kubejs:grist/${this.element.name}/block`;
    }
};

Grist.prototype.tags = function() {
    return [
        'kubejs:grist',
        this.tag(),
        this.elementalTag()
    ];
};

Grist.prototype.id = function() {
    return `kubejs:${this.key()}`;
};

Grist.prototype.toJson = function() {
    if (this.isExact) {
        switch(this.state) {
            case 'item': return {item: this.id()};
            case 'fluid': return {fluid: this.id()};
            case 'block': return {item: this.id()};
        }
    } else {
        switch(this.state) {
            case 'item': return {tag: this.tag()};
            case 'fluid': return {fluid: this.id()};
            case 'block': return {tag: this.tag()};
        }
    }
};

Grist.prototype.itemTexture = function() {
    return `malloc:item/grist_${this.tier.name}_${this.element.name}`;
};

Grist.prototype.blockTexture = function() {
    return `malloc:block/grist_${this.tier.name}_${this.element.name}`;
};

global.Grist = Grist;