<?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 Machine implements JsonSerializable {

    private $machineid = "";
    private $icon = "fas fa-desktop";
    private $typeid = 14;
    private $typelabel = "Machine";
    private $machine = [];
    private $events = [];
    private $components = [];
    private $exists = false;

    public function __construct($machineid) {
        global $database;
        $this->machineid = $machineid;
        if (Machine::exists($machineid)) {
            $this->exists = true;
            $this->machine = $database->get('machines', ['type [Int]', 'model', 'condition [Number]', 'price [Number]', 'os', 'serial', 'manufacturer', 'clientid [Int]', 'privatenotes', 'publicnotes', 'deleted [Bool]'], ['machineid' => $machineid]);
            $typeinfo = $database->get("machine_types", ["machinetypeid (id) [Int]", "typename (label)", "icon"], ["machinetypeid" => $this->machine["type"]]);
            $this->icon = $typeinfo["icon"];
            $this->typeid = $typeinfo["id"];
            $this->typelabel = $typeinfo["label"];
            $events = $database->select('events', 'historyid', ['machineid' => $machineid, "ORDER" => ["date" => "DESC"]]);
            foreach ($events as $e) {
                $this->events[] = new Event($e);
            }
            $components = $database->select("components", "compid", ["machineid" => $machineid]);
            foreach ($components as $c) {
                $this->components[] = new Component($c);
            }
        }
    }

    /**
     * Get a shorter array without editing data, events, or components.
     * @return type
     */
    public function toArrayLite() {
        global $Strings;
        if ($this->exists) {
            $info = $this->machine;
            // only show deleted if true
            if (!$this->isDeleted()) {
                unset($info["deleted"]);
            }
            return [
                "status" => "OK",
                "id" => $this->machineid,
                "icon" => $this->icon,
                "type" => [
                    "id" => $this->typeid,
                    "label" => $this->typelabel
                ],
                "info" => $info,
                "formdata" => [
                    "labels" => [
                        "model" => $Strings->get("Model", false),
                        "condition" => $Strings->get("Condition", false),
                        "price" => $Strings->get("Price", false),
                        "os" => $Strings->get("OS/Software", false),
                        "serial" => $Strings->get("Serial", false),
                        "manufacturer" => $Strings->get("Manufacturer", false),
                        "clientid" => $Strings->get("Client", false),
                        "privatenotes" => $Strings->get("Private Notes", false),
                        "publicnotes" => $Strings->get("Public Notes", false),
                        "type" => $Strings->get("Type", false)
                    ]
                ]
            ];
        }
        return [];
    }

    public function toArray() {
        global $Strings;
        if ($this->exists) {
            $info = $this->machine;
            // only show deleted if true
            if (!$this->isDeleted()) {
                unset($info["deleted"]);
            }
            $clientinfo = [];
            if (!empty($this->getClientID())) {
                $client = Clients::getClient($this->getClientID());
                $clientinfo = [
                    "name" => $client->getName(),
                    "phone" => $client->getPhone(),
                    "billingaddress" => $client->getBillingAddress(),
                    "mailingaddress" => $client->getMailingAddress()
                ];
            }
            return [
                "status" => "OK",
                "id" => $this->machineid,
                "icon" => $this->icon,
                "type" => [
                    "id" => $this->typeid,
                    "label" => $this->typelabel
                ],
                "clientinfo" => $clientinfo,
                "info" => $info,
                "events" => $this->events,
                "components" => $this->components,
                "formdata" => [
                    "options" => [
                        "clientid" => ["" => ""] + Clients::getAllAsIDNameArray(),
                        "type" => Machine::getTypeList()
                    ],
                    "inputtypes" => [
                        "model" => "text",
                        "condition" => "number",
                        "price" => "number",
                        "os" => "text",
                        "serial" => "text",
                        "manufacturer" => "text",
                        "clientid" => "select",
                        "privatenotes" => "textarea",
                        "publicnotes" => "textarea",
                        "type" => "select"
                    ],
                    "labels" => [
                        "model" => $Strings->get("Model", false),
                        "condition" => $Strings->get("Condition", false),
                        "price" => $Strings->get("Price", false),
                        "os" => $Strings->get("OS/Software", false),
                        "serial" => $Strings->get("Serial", false),
                        "manufacturer" => $Strings->get("Manufacturer", false),
                        "clientid" => $Strings->get("Client", false),
                        "privatenotes" => $Strings->get("Private Notes", false),
                        "publicnotes" => $Strings->get("Public Notes", false),
                        "type" => $Strings->get("Type", false)
                    ],
                    "icons" => [
                        "model" => "fas fa-hashtag",
                        "condition" => "fas fa-star-half-alt",
                        "price" => "fas fa-money-bill-wave",
                        "os" => "fas fa-hdd",
                        "serial" => "fas fa-barcode",
                        "manufacturer" => "fas fa-industry",
                        "privatenotes" => "fas fa-comment-dots",
                        "publicnotes" => "far fa-comment-dots"
                    ]
                ]
            ];
        }
        return [
            "status" => "OK",
            "id" => "",
            "icon" => "",
            "type" => [
                "id" => "",
                "label" => ""
            ],
            "clientinfo" => [],
            "info" => [],
            "events" => [],
            "components" => [],
            "formdata" => [
                "options" => [
                    "clientid" => ["" => ""] + Clients::getAllAsIDNameArray(),
                    "type" => Machine::getTypeList()
                ],
                "inputtypes" => [
                    "model" => "text",
                    "condition" => "number",
                    "price" => "number",
                    "os" => "text",
                    "serial" => "text",
                    "manufacturer" => "text",
                    "clientid" => "select",
                    "privatenotes" => "textarea",
                    "publicnotes" => "textarea",
                    "type" => "select"
                ],
                "labels" => [
                    "model" => $Strings->get("Model", false),
                    "condition" => $Strings->get("Condition", false),
                    "price" => $Strings->get("Price", false),
                    "os" => $Strings->get("OS/Software", false),
                    "serial" => $Strings->get("Serial", false),
                    "manufacturer" => $Strings->get("Manufacturer", false),
                    "clientid" => $Strings->get("Client", false),
                    "privatenotes" => $Strings->get("Private Notes", false),
                    "publicnotes" => $Strings->get("Public Notes", false),
                    "type" => $Strings->get("Type", false)
                ],
                "icons" => [
                    "model" => "fas fa-hashtag",
                    "condition" => "fas fa-star-half-alt",
                    "price" => "fas fa-money-bill-wave",
                    "os" => "fas fa-hdd",
                    "serial" => "fas fa-barcode",
                    "manufacturer" => "fas fa-industry",
                    "privatenotes" => "fas fa-comment-dots",
                    "publicnotes" => "far fa-comment-dots"
                ]
            ]
        ];
    }

    public function toHTMLListGroup(bool $public = true): string {
        global $Strings;
        $func = function($param) {
            return $param;
        };
        $line = function($type) use ($public): string {
            global $Strings, $SETTINGS;
            $publicsettings = json_decode(file_get_contents(__DIR__ . "/../custom/public.json"), true);
            if ($type == "Client") {
                if (!empty($this->getClientID()) && $public == false) {
                    $client = Clients::getClient($this->getClientID());
                    $rtn = "<b>" . $Strings->get("Client", false) . ":</b> " . htmlspecialchars($client->getName()) . "<br>";
                    $rtn .= "<div class=\"ml-3\">";
                    if (!empty($client->getPhone())) {
                        $rtn .= "<b>Phone:</b> " . $client->getPhone() . "<br>";
                    }
                    if (!empty($client->getEmail())) {
                        $rtn .= "<b>Email:</b> " . $client->getEmail() . "<br>";
                    }
                    if (!empty($client->getBillingAddress())) {
                        $rtn .= "<b>" . $Strings->get("Billing Address", false)
                                . ":</b><br>"
                                . str_replace("\n", "\n<br>", htmlspecialchars($client->getBillingAddress()))
                                . "<br>";
                    }
                    if (!empty($client->getMailingAddress())) {
                        $rtn .= "<b>" . $Strings->get("Mailing Address", false)
                                . ":</b><br>"
                                . str_replace("\n", "\n<br>", htmlspecialchars($client->getMailingAddress()))
                                . "<br>";
                    }
                    $rtn .= "</div>";
                    return $rtn;
                }
                return "";
            }
            if ($type == "Condition") {
                if (!empty($this->getCondition())) {
                    $rtn = "<b>" . $Strings->get("Condition", false) . "</b>: ";
                    $val = $this->getCondition();
                    $filled = floor($val);
                    $empty = 10;
                    while ($filled > 0) {
                        $filled--;
                        $empty--;
                        $rtn .= "<i class=\"fas fa-star\"></i> ";
                    }
                    if ($val - floor($val) > 0.75) {
                        $empty--;
                        $rtn .= "<i class=\"fas fa-star\"></i> ";
                    } else if ($val - floor($val) > 0.25) {
                        $empty--;
                        $rtn .= "<i class=\"fas fa-star-half-alt\"></i> ";
                    }
                    while ($empty > 0) {
                        $empty--;
                        $rtn .= "<i class=\"far fa-star\"></i> ";
                    }
                    $rtn .= " ($val/10)";
                    return $rtn;
                }
                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 "<b>" . ($public ? $Strings->get("Notes", false) : $Strings->get("Public Notes", false)) . ":</b><br /><div class=\"ml-3\">" . htmlspecialchars($this->getPublicNotes()) . "</div>";
                }
                return "";
            }
            if ($type == "PrivateNotes") {
                if (!empty($this->getPrivateNotes()) && $public == false) {
                    return "<b>" . $Strings->get("Private Notes", false) . ":</b><br /><div class=\"ml-3\">" . htmlspecialchars($this->getPrivateNotes()) . "</div>";
                }
                return "";
            }
            if ($type == "ID") {
                return "<b>" . $publicsettings["ID number label"] . ":</b> " . htmlspecialchars($this->getID()) . "<br />";
            }
            if (empty($this->{"get$type"}())) {
                return "";
            }
            return "<b>" . $Strings->get($type, false) . ":</b> " . htmlspecialchars($this->{"get$type"}()) . "<br />";
        };

        $props = ["Client", "OS", "Serial", "Manufacturer", "Model", "Condition", "Price", "PrivateNotes", "PublicNotes"];

        if ($public) {
            $props = ["ID"] + $props;
        }

        $html = '<div class="list-group">';

        foreach ($props as $prop) {
            $l = $line($prop);
            if (empty($l)) {
                continue;
            }
            $html .= '<div class="list-group-item">';
            $html .= $l;
            $html .= '</div>';
        }

        $html .= '</div>';

        return $html;
    }

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

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

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

    /**
     * Check if a given serial number can identify exactly one machine.
     * @global $database $database
     * @param type $serial
     * @return bool
     */
    public static function serialExists(string $serial): bool {
        global $database;
        return $database->has('machines', ['serial' => $serial]) && $database->count('machines', ['serial' => $serial]) == 1;
    }

    /**
     * Convert a serial into a machine ID.
     * @global $database $database
     * @param string $serial
     * @return string|bool machine ID if found, otherwise false.
     */
    public static function getIDFromSerial(string $serial) {
        global $database;
        if (Machine::serialExists($serial)) {
            return $database->get('machines', 'machineid', ['serial' => $serial]) . "";
        }
        return false;
    }

    public static function getTypeList(): array {
        global $database;
        $typelist = [];
        foreach ($database->select("machine_types", ["machinetypeid (id)", "typename (name)"]) as $t) {
            $typelist[$t["id"]] = $t["name"];
        }
        return $typelist;
    }

    public static function getTypeListWithIcons(): array {
        global $database;
        $typelist = [];
        foreach ($database->select("machine_types", ["machinetypeid (id)", "typename (name)", "icon"]) as $t) {
            $typelist[$t["id"]] = [
                "name" => $t["name"],
                "icon" => $t["icon"]
            ];
        }
        return $typelist;
    }

    public function save() {
        global $database;
        if ($this->exists) {
            $database->update("machines", $this->machine, ["machineid" => $this->machineid]);
        } else {
            $data = $this->machine;
            $data["machineid"] = $this->machineid;
            $database->insert("machines", $data);
            $this->exists = true;
            // Insert event for machine creation
            Event::create($data["machineid"], date("Y-m-d H:i:s"), "Device ID Generated");
        }
    }

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

    public function getClientID() {
        if (!empty($this->machine["clientid"])) {
            return $this->machine["clientid"];
        }
        return "";
    }

    public function getType(): int {
        return $this->typeid;
    }

    public function getTypeLabel(): string {
        return $this->typelabel;
    }

    public function getIcon(): string {
        return $this->icon;
    }

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

    public function getCondition(): float {
        if (!empty($this->machine["condition"])) {
            return $this->machine["condition"] * 1.0;
        }
        return 0.0;
    }

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

    public function getOS(): string {
        if (!empty($this->machine["os"])) {
            return $this->machine["os"];
        }
        return "";
    }

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

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

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

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

    public function getEvents() {
        return $this->events;
    }

    public function setClientID($id) {
        $this->machine["clientid"] = $id;
    }

    public function setType(int $typeid) {
        global $database;
        $this->typeid = $typeid;
        $this->machine["type"] = $typeid;

        $typeinfo = $database->get("machine_types", ["machinetypeid (id)", "typename (label)", "icon"], ["machinetypeid" => $typeid]);
        $this->icon = $typeinfo["icon"];
        $this->typelabel = $typeinfo["label"];
    }

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

    public function setCondition(float $condition) {
        if ($condition < 0) {
            $condition = 0;
        }
        if ($condition > 10) {
            $condition = 10;
        }
        $condition = round($condition);
        $this->machine["condition"] = $condition;
    }

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

    public function setOS(string $os) {
        $this->machine["os"] = $os;
    }

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

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

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

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

    public function getComponents(): array {
        return $this->components;
    }

    public function setDeleted(bool $deleted = true) {
        $this->machine["deleted"] = $deleted;
    }

    public function isDeleted(): bool {
        return $this->machine["deleted"] == true;
    }

    public function addEvent(string $date, string $event, string $techuid = "", string $publicnotes = "", string $privatenotes = "") {
        $evt = Event::create($this->machineid, $date, $event, $techuid, $publicnotes, $privatenotes);

        $this->events[] = $evt;
    }

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

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

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

        return $id;
    }

}
