<?php

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

class Component implements JsonSerializable {

    private $componentid = "";
    private $component = [];
    private $exists = false;

    public function __construct($componentid) {
        global $database;
        $this->componentid = $componentid;
        if (Component::exists($componentid)) {
            $this->exists = true;
            $this->component = $database->get("components", ["machineid", "serial", "typeid [Int]", "tested", "capacity", "model", "price [Number]", "manufacturer", "publicnotes", "privatenotes"], ["compid" => $componentid]);
        }
    }

    public static function create(): Component {
        return new Component(Component::generateId());
    }

    public static function exists($id): bool {
        global $database;
        return $database->has('components', ['compid' => $id]);
    }

    public static function getTypes() {
        global $database;
        $types = $database->select("component_types", ["typeid (id)", "typename (name)"]);

        $list = [];
        foreach ($types as $t) {
            $list[$t['id']] = $t['name'];
        }
        return $list;
    }

    public function toArray() {
        global $Strings;
        if ($this->exists) {
            return [
                "info" => [
                    "id" => $this->getID(),
                    "machineid" => $this->getMachineID(),
                    "serial" => $this->getSerial(),
                    "type" => $this->getTypeID(),
                    "tested" => $this->getTestedDate(),
                    "capacity" => $this->getCapacity(),
                    "model" => $this->getModel(),
                    "price" => $this->getPrice(),
                    "manufacturer" => $this->getManufacturer(),
                    "publicnotes" => $this->getPublicNotes(),
                    "privatenotes" => $this->getPrivateNotes()
                ],
                "formdata" => [
                    "types" => $this->getTypes(),
                    "inputtypes" => [
                        "machineid" => "text",
                        "serial" => "text",
                        "type" => "number",
                        "tested" => "datetime",
                        "capacity" => "text",
                        "model" => "text",
                        "price" => "number",
                        "manufacturer" => "text",
                        "privatenotes" => "textarea",
                        "publicnotes" => "textarea"
                    ],
                    "labels" => [
                        "machineid" => $Strings->get("Machine ID", false),
                        "serial" => $Strings->get("Serial", false),
                        "type" => $Strings->get("Type", false),
                        "tested" => $Strings->get("Tested", false),
                        "capacity" => $Strings->get("Capacity", false),
                        "model" => $Strings->get("Model", false),
                        "price" => $Strings->get("Price", false),
                        "manufacturer" => $Strings->get("Manufacturer", false),
                        "privatenotes" => $Strings->get("Private Notes", false),
                        "publicnotes" => $Strings->get("Public Notes", false)
                    ],
                    "icons" => []
                ]
            ];
        }
        return [];
    }

    /**
     * Render the component's info to HTML
     * @param bool $public
     */
    public function toHTML(bool $public = true): string {
        global $Strings;
        $func = function($param) {
            return $param;
        };
        $line = function($type) use ($public): string {
            global $Strings, $SETTINGS;
            if ($type == "TestedDate") {
                if (!empty($this->getTestedDate())) {
                    return "<b>" . $Strings->get("Tested On", false) . ":</b> " . date($SETTINGS["date_format"], strtotime($this->getTestedDate())) . "<br />";
                }
                return "";
            }
            if ($type == "Price") {
                if (!empty($this->getPrice())) {
                    return "<b>" . $Strings->get("Price", false) . ":</b> $" . number_format($this->getPrice(), 2) . "<br />";
                }
                return "";
            }
            if ($type == "PublicNotes") {
                if (!empty($this->getPublicNotes())) {
                    return "<div><b>" . ($public ? $Strings->get("Notes", false) : $Strings->get("Public Notes", false)) . ":</b><br /><div class=\"ml-3\">" . htmlspecialchars($this->getPublicNotes()) . "</div></div>";
                }
                return "";
            }
            if ($type == "PrivateNotes") {
                if (!empty($this->getPrivateNotes()) && $public == false) {
                    return "<div><b>" . $Strings->get("Private Notes", false) . ":</b><br /><div class=\"ml-3\">" . htmlspecialchars($this->getPublicNotes()) . "</div></div>";
                }
                return "";
            }
            if (empty($this->{"get$type"}())) {
                return "";
            }
            return "<b>" . $Strings->get($type, false) . ":</b> " . htmlspecialchars($this->{"get$type"}()) . "<br />";
        };
        $html = "";
        if ($public == false) {
            $html .= <<<HTML
    <div class="float-right">
        <a class="btn btn-danger btn-sm" href="./action.php?action=unlinkcomponent&source=viewmachine&id={$func($this->getID())}&machine={$func($this->getMachineID())}"><i class="fas fa-unlink"></i> Detach</a>
        <a class="btn btn-primary btn-sm" href="./app.php?page=editcomponent&id={$func($this->getID())}"><i class="fas fa-edit"></i> Edit</a>
    </div>
HTML;
        }
        $html .= <<<HTML
    <b>{$func($this->getTypeName())}</b><br />
    {$line("Model")}
    {$line("Capacity")}
    {$line("Serial")}
    {$line("TestedDate")}
    {$line("Price")}
    {$line("Manufacturer")}
    {$line("PrivateNotes")}
    {$line("PublicNotes")}
HTML;
        return $html;
    }

    public function jsonSerialize() {
        return $this->toArray();
    }

    public function save() {
        global $database;
        if ($this->exists) {
            $database->update("components", $this->component, ["compid" => $this->componentid]);
        } else {
            $data = $this->component;
            $data["compid"] = $this->componentid;
            $database->insert("components", $data);
            $this->exists = true;
        }
    }

    public function delete() {
        global $database;
        $database->delete("components", ["compid" => $this->componentid]);
    }

    public function getID(): string {
        return $this->componentid . "";
    }

    public function getMachineID(): string {
        if (!empty($this->component["machineid"])) {
            return $this->component["machineid"];
        }
        return "";
    }

    public function getSerial(): string {
        if (!empty($this->component["serial"])) {
            return $this->component["serial"];
        }
        return "";
    }

    public function getTypeID(): int {
        if (!empty($this->component["typeid"])) {
            return $this->component["typeid"] * 1;
        }
        return 0;
    }

    public function getTypeName(): string {
        return Component::getTypes()[$this->getTypeID()];
    }

    public function getTestedDate(): string {
        if (!empty($this->component["tested"])) {
            return $this->component["tested"];
        }
        return "";
    }

    public function getCapacity(): string {
        if (!empty($this->component["capacity"])) {
            return $this->component["capacity"];
        }
        return "";
    }

    public function getModel(): string {
        if (!empty($this->component["model"])) {
            return $this->component["model"];
        }
        return "";
    }

    public function getPrice(): float {
        if (!empty($this->component["price"])) {
            return $this->component["price"] * 1.0;
        }
        return 0.0;
    }

    public function getManufacturer(): string {
        if (!empty($this->component["manufacturer"])) {
            return $this->component["manufacturer"];
        }
        return "";
    }

    public function getPublicNotes(): string {
        if (!empty($this->component["publicnotes"])) {
            return $this->component["publicnotes"];
        }
        return "";
    }

    public function getPrivateNotes(): string {
        if (!empty($this->component["privatenotes"])) {
            return $this->component["privatenotes"];
        }
        return "";
    }

    public function setMachineID($id) {
        $this->component["machineid"] = $id;
    }

    public function setSerial(string $serial) {
        $this->component["serial"] = $serial;
    }

    public function setTypeID(int $id) {
        if (!empty(Component::getTypes()[$id])) {
            $this->component["typeid"] = $id;
        }
    }

    public function setTestedDate(string $tested) {
        $this->component["tested"] = date("Y-m-d H:i:s", strtotime($tested));
    }

    public function clearTestedDate() {
        $this->component["tested"] = null;
    }

    public function setCapacity(string $capacity) {
        $this->component["capacity"] = $capacity;
    }

    public function setModel(string $model) {
        $this->component["model"] = $model;
    }

    public function setPrice(float $price) {
        $this->component["price"] = $price;
    }

    public function setManufacturer(string $manufacturer) {
        $this->component["manufacturer"] = $manufacturer;
    }

    public function setPrivateNotes(string $notes) {
        $this->component["privatenotes"] = $notes;
    }

    public function setPublicNotes(string $notes) {
        $this->component["publicnotes"] = $notes;
    }

    /**
     * Generate a random ID number that is not in use.
     * Default: 681#####[check digit]
     * @global $database
     * @param int $min Optional minimum number.
     * @param int $max Optional maximum number.
     * @return int
     */
    public static function generateId(int $min = 68100000, int $max = 68199999): int {
        global $database;

        do {
            $id = random_int($min, $max);

            // If default gen add check digit
            if ($id >= 68100000 && $id <= 68199999) {
                $id = ("$id" . CheckDigit::S10($id)) * 1;
            }
        } while ($database->has('components', ['compid' => $id]) || $database->has('machines', ['machineid' => $id]));

        return $id;
    }

}
