<?php
namespace Misuzu;

use InvalidArgumentException;

class Colour {
    private const FLAG_INHERIT = 0x40000000;

    private const READABILITY_THRESHOLD = 186;
    private const LUMINANCE_WEIGHT_RED = .299;
    private const LUMINANCE_WEIGHT_GREEN = .587;
    private const LUMINANCE_WEIGHT_BLUE = .114;

    private int $raw = 0;

    public function __construct(?int $raw = 0) {
        $this->raw = ($raw ?? 0) & 0x7FFFFFFF;
    }

    public static function none(): self {
        return new Colour(self::FLAG_INHERIT);
    }

    public static function fromRgb(int $red, int $green, int $blue): self {
        $raw = (($red & 0xFF) << 16)
            | (($green & 0xFF) << 8)
            | ($blue & 0xFF);
        return new Colour($raw);
    }
    public static function fromHex(string $hex): self {
        if($hex[0] === '#')
            $hex = mb_substr($hex, 1);

        if(!ctype_xdigit($hex))
            throw new InvalidArgumentException('Argument contains invalid characters.');

        $length = mb_strlen($hex);

        if($length === 3) {
            $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
        } elseif($length !== 6) {
            throw new InvalidArgumentException('Argument is not a hex string.');
        }

        return new Colour(hexdec($hex));
    }

    public function getRaw(): int {
        return $this->raw;
    }

    public function getInherit(): bool {
        return ($this->getRaw() & self::FLAG_INHERIT) > 0;
    }

    public function getRed(): int {
        return ($this->getRaw() & 0xFF0000) >> 16;
    }

    public function getGreen(): int {
        return ($this->getRaw() & 0xFF00) >> 8;
    }

    public function getBlue(): int {
        return ($this->getRaw() & 0xFF);
    }

    public function getLuminance(): float {
        return self::LUMINANCE_WEIGHT_RED   * $this->getRed()
             + self::LUMINANCE_WEIGHT_GREEN * $this->getGreen()
             + self::LUMINANCE_WEIGHT_BLUE  * $this->getBlue();
    }

    public function getHex(): string {
        return str_pad(dechex($this->getRaw() & 0xFFFFFF), 6, '0', STR_PAD_LEFT);
    }

    public function getCSS(): string {
        if($this->getInherit())
            return 'inherit';
        return '#' . $this->getHex();
    }

    public function extractCSSContract(
        string $dark = 'dark', string $light = 'light', bool $inheritIsDark = true
    ): string {
        if($this->getInherit())
            return $inheritIsDark ? $dark : $light;

        return $this->getLuminance() > self::READABILITY_THRESHOLD ? $dark : $light;
    }

    public function __toString() {
        return $this->getCSS();
    }
}
