<?php

namespace SmartData;

require_once( __DIR__ . '/Exception.php');
require_once( __DIR__ . '/Packer.php');
require_once( __DIR__ . '/Unit.php');

use SmartData\Utility\Pack;
use SmartData\Utility\Unpack;

class Series
{
    public $version;
    private $unit;
    public $x;
    public $y;
    public $z;
    public $r;
    public $t0;
    public $t1;
    public $dev;
    public $signature;

    public function __construct($v, $u, $x,$y,$z, $r, $t0, $t1, $dev, $signature=0, $workflow=0)
    {
        // Conditions for a valid series.
        //TODO: Check the maximum value comparison
        //if ((gmp_cmp(gmp_init($t0), gmp_init($t1)) > 0) or ($r < 0)){
        if (($t0 > $t1) or ($t0 < 0) or ($t1 < 0) or ($r < 0)){
            throw new Exception\BadRequestException("Invalid series");
        }

        $tmp_v = $v;
        if(is_numeric($tmp_v) && is_int($tmp_v+0)){
            // nothing
        } else {
            $tmp_v = explode(".", $tmp_v);
            $tmp_v = ($tmp_v[0]<<4) + $tmp_v[1];
        }

        $this->version = $tmp_v;
        $this->unit = ($u instanceof Unit) ? $u : Unit::interpret($u);
        $this->x         = $x;
        $this->y         = $y;
        $this->z         = $z;
        $this->r         = $r;
        $this->t0        = $t0;
        $this->t1        = $t1;
        $this->dev       = $dev;
        $this->signature = $signature;
        $this->workflow  = $workflow;
    }

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

    public function contains_point($x, $y, $z)
    {
        return ( sqrt(pow($this->x - $x, 2) + pow($this->y - $y, 2) + pow($this->z - $z, 2)) <= $this->r );
    }

    public function contains_time($time)
    {
        return ( $this->t0 <= $time && $time <= $this->t1 );
    }

    public function __get($property)
    {
        if (property_exists($this, $property)) {
            switch ($property) {
                case 'unit':
                    if($this->unit instanceof Unit)
                        return $this->unit->cod;
                    else
                        return $this->unit;
                default:
                    return $this->$property;
            }
        }
    }

    //public function create()
    //{
    //    return API::create($this->pack());
    //}

    //public function attach()
    //{
    //    return API::attach($this->pack());
    //}

    //public function get()
    //{
    //    return API::get($this->pack());
    //}

    public function pack(array $suppress = array())
    {
        $bin  = '';
        if(!in_array("version", $suppress))
            $bin .= Pack::uInt8($this->version);
        if(!in_array("unit", $suppress))
            $bin .= Pack::uInt32($this->unit->cod);
        if(!in_array("x", $suppress))
            $bin .= Pack::int32($this->x);
        if(!in_array("y", $suppress))
            $bin .= Pack::int32($this->y);
        if(!in_array("z", $suppress))
            $bin .= Pack::int32($this->z);
        if(!in_array("r", $suppress))
            $bin .= Pack::int32($this->r);
        if(!in_array("t0", $suppress))
            $bin .= Pack::uInt64($this->t0);
        if(!in_array("t1", $suppress))
            $bin .= Pack::uInt64($this->t1);
        if(!in_array("dev", $suppress))
            $bin .= Pack::uint32($this->dev);
        return $bin;
    }

    public static function unpack(& $bin, array $compl = array())
    {
        $ver = $compl['version'] ?? Unpack::uInt8($bin, true);
        if(array_key_exists('unit', $compl))
            $u = Unit::interpret($compl['unit']);
        else
            $u = Unit::unpack($bin);
        $x   = $compl['x'] ?? Unpack::int32($bin, true);
        $y   = $compl['y'] ?? Unpack::int32($bin, true);
        $z   = $compl['z'] ?? Unpack::int32($bin, true);
        $r   = $compl['r'] ?? Unpack::int32($bin, true);
        $t0  = $compl['t0'] ?? Unpack::uInt64($bin, true);
        if($t0<0)
           $t0 = 0;
        $t1  = $compl['t1'] ?? Unpack::uInt64($bin, true);
        if($t1<0)
           $t1 = PHP_INT_MAX;
        $d = $compl['dev'] ?? Unpack::uint32($bin, true);
        return new self($ver, $u, $x, $y, $z, $r, $t0, $t1, $d);
    }

    public function toArray()
    {
        $json = array();
        $json['version'] = $this->version;
        $json['unit']    = $this->unit->cod;
        $json['x']       = $this->x;
        $json['y']       = $this->y;
        $json['z']       = $this->z;
        $json['r']       = $this->r;
        $json['t0']      = $this->t0;
        $json['t1']      = $this->t1;
        $json['dev']     = $this->dev;
        return $json;
    }


    public function toJson()
    {
        $json = json_encode($this->toArray());
        $json = preg_replace('/:"([0-9]*)"/',':$1',$json);
        //$json = preg_replace('/\"t0\":\"/', "\"t0\":", $json);
        //$json = preg_replace('/\"t1\":\"/', "\"t1\":", $json);
        return $json;
    }

    public static function fromJson(\stdClass $json)
    {
        return new self(
                $json->version,
                $json->unit,
                $json->x,
                $json->y,
                $json->z,
                $json->r,
                $json->t0,
                $json->t1,
                $json->dev??0,
                $json->signature??0,
                $json->workflow??0
            );
    }

    public function __toString()
    {
        $string  = "{";
        $string .= "v={$this->version}, ";
        $string .= "u={$this->unit}, ";
        $string .= "x={$this->x}, ";
        $string .= "y={$this->y}, ";
        $string .= "z={$this->z}, ";
        $string .= "r={$this->r}, ";
        $string .= "t0={$this->t0}, ";
        $string .= "t1={$this->t1}, ";
        $string .= "d={$this->dev}";
        if($this->version == SmartData::MOBILE_VERSION) // Remove.. Now we have the classs "Tracker" for mobile version
            $string .= ",sig={$this->signature}";
        $string .= '}';
        return $string;
    }
}