<?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 MultiSmartData
{
    public function __construct() { } 

    public static function unpackMultiValue(& $bin)
    {
        $datapoints = array();
        $ver = Unpack::uInt8($bin, true);
        $u   = Unit::unpack($bin);
        $x   = Unpack::int32($bin, true);
        $y   = Unpack::int32($bin, true);
        $z   = Unpack::int32($bin, true);
        $t0  = Unpack::uInt64($bin, true);
        $dev   = Unpack::uint32($bin, true);
        $flags = Unpack::uInt8($bin, true);
	$period = 0;
        $all_uncert = 0;
        if (($flags & 0x01) != 0) {
            $period = Unpack::int32($bin, true);
        }
        if (($flags & 0x02) != 0) {
            $uncertainty = Unpack::int32($bin, true);
            $all_uncert = 1;
	}
        $n = 0;
        while ($bin) {
            if ($period == 0) {
               $offset = Unpack::uint32($bin, true);
            } else {
	       $offset = $n * $period;
            }
	    $n = $n + 1;
            $v  = Unpack::d64($bin, true);
            if ( $all_uncert == 1 ) {
                $uncert = $uncertainty;
            } else {
                $uncert = Unpack::uint32($bin, true);
            }
            $e = ( $uncert & 0xFF000000 ) >> 24;
            $c = ( $uncert & 0x00FF0000 ) >> 16;
            $t = $t0 + $offset;
            $sd = new StaticSmartData( $u, $v, $e, $c, $x, $y, $z, $t, $dev);
            array_push($datapoints, $sd);
        }
        return $datapoints;
    }

    public static function unpackMultiDevice(& $bin)
    {
        $datapoints = array();
        $ver = Unpack::uInt8($bin, true);
        $u   = Unit::unpack($bin);
        $x   = Unpack::int32($bin, true);
        $y   = Unpack::int32($bin, true);
        $z   = Unpack::int32($bin, true);
        $t0  = Unpack::uInt64($bin, true);
        while ($bin) {
            $offset = Unpack::uint32($bin, true);
            $v      = Unpack::d64($bin, true);
            $dev    = Unpack::uint32($bin, true);
            $uncert = Unpack::uint32($bin, true);
            $e = ( $uncert & 0xFF000000 ) >> 24;
            $c = ( $uncert & 0x00FF0000 ) >> 16;
            $t = $t0 + $offset;
            $sd = new StaticSmartData( $u, $v, $e, $c, $x, $y, $z, $t, $dev);
            array_push($datapoints, $sd);
        }
        return $datapoints;
    }

    public static function unpackMultiUnit(& $bin)
    {
        $datapoints = array();
        $ver = Unpack::uInt8($bin, true);
        $x   = Unpack::int32($bin, true);
        $y   = Unpack::int32($bin, true);
        $z   = Unpack::int32($bin, true);
        $t0  = Unpack::uInt64($bin, true);
        while ($bin) {
            $u      = Unpack::uint32($bin, true);
            $set    = array();
            if (array_key_exists($u, $datapoints)) {
               $set = $datapoints[$u];
            }
            $offset = Unpack::uint32($bin, true);
            $v      = Unpack::d64($bin, true);
            $dev    = Unpack::uint32($bin, true);
            $uncert = Unpack::uint32($bin, true);
            $e = ( $uncert & 0xFF000000 ) >> 24;
            $c = ( $uncert & 0x00FF0000 ) >> 16;
            $t = $t0 + $offset;
            $sd = new StaticSmartData( $u, $v, $e, $c, $x, $y, $z, $t, $dev); 
            array_push($set, $sd);
            $datapoints [ $u ] = $set;
        }
        return $datapoints;
    }

    public static function parseJson(\stdClass $json) 
    {
        
    }

    public static function multi_values(\stdClass $json)
    {
     	// echo "Testando... ";
        $header = $json->MultiValueSmartData;
        $version = $header->version;
        if (!(is_numeric($version) && is_int($version+0))){
            $version = explode(".", $version);
            $version = ($version[0]<<4) + $version[1];
        }
	$u = Unit::interpret($header->unit);
        $x = $header->x;
        $y = $header->y;
        $z = $header->z;
        $t0 = $header->t0;
        $dev = $header->dev  ?? 0;            // TODO: remove this
        $data = $header->datapoints;
        $period = -1;
        if (isset($header->period)) {
            echo 'Found period in header';
	    $period = $header->period;
        } else {
            echo "No period in header!";
        }
        $uncertainty = -1;
        if (isset($header->uncertainty)) {
            echo "Foun uncertainty in header.";
            $uncertainty = $header->uncertainty;
        } else {
            echo "No UNCERT in header";
        }
        $n = 0;
        $smartdatas = array();
        foreach($data as $point) {
            $val = $point->value;
            if ($period == -1) {
                $offset = $point->offset;
            } else {
                $offset = $n * $period;
                $n = $n + 1;
            }
            $t = $t0 + $offset;
            if (isset($point->uncertainty)) {
                $uncert = $point->uncertainty;
            } else {
                $uncert = 0;
            }
            $e = ( $uncertainty & 0xFF000000 ) >> 24;
            $c = ( $uncertainty & 0x00FF0000 ) >> 16;
            $sd = new StaticSmartData($u, $val, $e, $c, $x, $y, $z, $t, $dev);
            array_push($smartdatas, $sd);
        }
	return $smartdatas;
    }

    public static function multi_devices(\stdClass $json)
    {
     	$header = $json->MultiDeviceSmartData;
        $version = $header->version;
        if (!(is_numeric($version) && is_int($version+0))) {
            $version = explode(".", $version);
            $version = ($version[0]<<4) + $version[1];
        }

	$u = Unit::interpret($header->unit);
        $x = $header->x;
        $y = $header->y;
        $z = $header->z;
        $t0 = $header->t0;
        $data = $header->datapoints;
        $smartdatas = array();
        foreach($data as $point) {
            $val = $point->value;
            $offset = $point->offset;
            $t = $t0 + $offset;
            $uncertainty = $point->uncertainty;
            $e = ( $uncertainty & 0xFF000000 ) >> 24;
            $c = ( $uncertainty & 0x00FF0000 ) >> 16;
            $dev = $point->dev;
            $sd = new StaticSmartData($u, $val, $e, $c, $x, $y, $z, $t, $dev);
            array_push($smartdatas, $sd);
        }
	return $smartdatas;
    }

    // returns a map, with each unit refering to a list of all datapoints of this unit.
    public static function multi_units(\stdClass $json)
    {
     	$header = $json->MultiUnitSmartData;
        $version = $header->version;
        if(is_numeric($version) && is_int($version+0)){
            // nothing
        } else {
            $version = explode(".", $version);
            $version = ($version[0]<<4) + $version[1];
        }
        $x = $header->x;
        $y = $header->y;
        $z = $header->z;
        $t0 = $header->t0;
        $data = $header->datapoints;
        $smartdatas = array();
        foreach($data as $point) {
            $u = $point->unit;
            $set = array();
            if (array_key_exists($u, $smartdatas)) {
               $set = $smartdatas[$u];
            }
            $val = $point->value;
            $offset = $point->offset;
            $t = $t0 + $offset;
            $uncertainty = $point->uncertainty;
            $e = ( $uncertainty & 0xFF000000 ) >> 24;
            $c = ( $uncertainty & 0x00FF0000 ) >> 16;
            $dev = $point->dev;
            $sd = new StaticSmartData($u, $val, $e, $c, $x, $y, $z, $t, $dev);
            array_push($set, $sd);
            $smartdatas[ $u ] = $set; 
        }
        return $smartdatas;
    }
}