<?php


namespace App\Utils;


use JsonSerializable;
use MightyPork\Exceptions\NotApplicableException;
use MightyPork\Utils\Utils;

/**
 * Helper class representing one column in a data table.
 *
 * @property-read string $name
 * @property-read string $title
 * @property-read string $type
 */
class Column implements JsonSerializable
{
    const colTypes = [
        'int', 'bool', 'float', 'string'
    ];

    private $name;
    private $title;
    private $type;

    public static function columnsFromJson($columns)
    {
        return array_map(function ($x) {
            return new Column($x);
        }, $columns);
    }

    public function __get($name)
    {
        if (property_exists($this, $name)) {
            return $this->$name;
        }

        throw new NotApplicableException("No such column property");
    }

    /**
     * Create from object or array
     *
     * @param $obj
     */
    public function __construct($obj)
    {
        $b = new \objBag($obj);
        $this->name = $b->name;
        $this->title = $b->title;
        $this->type = $b->type;

        if (!in_array($this->type, self::colTypes)) {
            throw new NotApplicableException("\"$this->type\" is not a valid column type.");
        }
    }

    /**
     * @return array with keys {name, title, type}
     */
    public function toArray()
    {
        return [
            'name' => $this->name,
            'title' => $this->title,
            'type' => $this->type,
        ];
    }

    /**
     * Convert a value to the target type, validating it in the process
     *
     * @param mixed $value
     * @return bool|float|int|string
     */
    public function cast($value)
    {
        switch ($this->type) {
            case 'int':
                if (is_int($value)) return $value;
                if (is_float($value)) return round($value);
                if (is_numeric($value)) return intval($value);
                throw new NotApplicableException("Could not convert value \"$value\" to int!");

            case 'float':
                if (is_int($value) || is_float($value)) return (float)$value;
                if (is_numeric($value)) return floatval($value);
                throw new NotApplicableException("Could not convert value \"$value\" to float!");

            case 'bool':
                return Utils::parseBool($value);

            case 'string':
                return "$value";

            default:
                throw new \LogicException("Illegal column type: \"$this->type\"");
        }
    }

    /**
     * Specify data which should be serialized to JSON
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     * @since 5.4.0
     */
    public function jsonSerialize()
    {
        return $this->toArray();
    }
}