<?php

/**
 * BlueWeather
 *
 * PHP version 7.2
 *
 * @category Tools
 * @package  BlueWeather
 * @author   Dorian Zedler <dorian@itsblue.de>
 * @license  GPLV3 gpl.com
 * @link     itsblue.de
 */

class BlueWeather
{
    private $_config;
    private $_con;

    /**
     * Constructor
     *
     * @param mixed $_config config array containing some stuff
     *
     * @return void
     */
    public function __construct($_config)
    {
        $this->_config = $_config;
        $this->_con = mysqli_connect(
            $_config['dbhost'],
            $_config['dbuser'],
            $_config['dbpassword'],
            $_config['dbname']
        );

        if (!$this->_con) {
            echo "Error connecting to database: " . mysqli_connect_error();
            http_response_code(500);
            die();
        }

    }

    // -------------------
    // - user management -
    // -------------------

    /**
     * Function to login users
     *
     * @param string $username username
     * @param string $password passowrd
     *
     * @return string '' or session token
     */
    public function loginUser($username, $password)
    {
        $sql = "SELECT * FROM `users`
        WHERE`username`=\"".$this->_con->real_escape_string($username)."\"";
        $result = $this->_con->query($sql);

        // only one row will be returned
        $data = $result->fetch_assoc();

        if (!password_verify($password, $data['password'])) {
            return "";
        }

        //generate random token
        $length = 10;
        $str = "";
        $characters = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9'));
        $max = count($characters) - 1;
        for ($i = 0; $i < $length; $i++) {
            $rand = mt_rand(0, $max);
            $str .= $characters[$rand];
        }
        $token_hash = password_hash($str, PASSWORD_DEFAULT);

        $sql = 'INSERT INTO `sessions` (userId, session)
                VALUES ("'. $data['id'] .'", "'. $token_hash .'")';

        if (!$this->_con->query($sql)) {
            return "";
        }

        return $token_hash;
    }

    /**
     * Function to check if a session token exists and get the corresponding user
     *
     * @param string $session session token
     *
     * @return int (-1: does not exist; x>0: userId)
     */
    public function checkSession($session)
    {
        $sql = "SELECT * FROM `sessions`
        WHERE`session`=\"".$this->_con->real_escape_string($session)."\"";
        $result = $this->_con->query($sql);

        if (!$result->num_rows > 0) {
            return -1;
        }

        // only one row will be returned
        $data = $result->fetch_assoc();

        return($data['userId']);
    }


    // --------------------
    // - getter functions -
    // --------------------

    /**
     * Function to get all locations from the database
     * 
     * @return mixed (array with all locations)
     */
    public function getAllLocations()
    {
        // get all locations
        $sql = "SELECT * FROM `locations`";
        $result = $this->_con->query($sql);

        //loop through the returned data
        while ($row = $result->fetch_assoc()) {
            $locations[] = $row;
        }

        return $locations;
    }

    /**
     * Function to get the data of a specific location
     * 
     * @param int   $locId   id of the location to return data of
     * @param mixed $range   contains 'from' and 'to' as unix timestamps
     * @param int   $maxVals maximum measvals to be transmitted; if more are 
     *                       present in the timespan, the avarage will be calculated
     * 
     * @return mixed object with all information about the location (see docs)
     */
    function getLocationData($locId, $range, $maxVals)
    {

        $locId = $this->_con->real_escape_string($locId);
        $range["from"] = $this->_con->real_escape_string($range["from"]);
        $range["to"] = $this->_con->real_escape_string($range["to"]);
        $maxVals = $this->_con->real_escape_string($maxVals);

        $sql = "SELECT * FROM `locations`
        WHERE`id`=$locId";
        $result = $this->_con->query($sql);
    
        // only one row will be returned
        $data = $result->fetch_assoc();
    
        if (!isset($range['from']) || $range['from'] === "") {
            $range['from'] = time() - 24 * 60 * 60;
        }
    
        if (!isset($range['to']) || $range['to'] === "") {
            $range['to'] = time();
        }
    
        // get all measvalues of given location
        $sql = "SELECT M.measvalue,M.sensorid,M.timestamp FROM measvalues M
        JOIN sensors S ON M.sensorid = S.id
        JOIN locations L ON S.locationid=L.id
        WHERE L.id=$locId AND M.timestamp > " . $range['from'] . 
                        " AND M.timestamp < " . $range['to'] . 
                        " ORDER BY timestamp ASC";
    
        $result = $this->_con->query($sql);
    
        while ($row = $result->fetch_assoc()) {
            $measvalues[] = $row;
        }
    
        // get all sensors of given location
        $sql = "SELECT * FROM `sensors`
        WHERE `locationid` = $locId";
    
        $result = $this->_con->query($sql);
    
        //loop through the returned data
        while ($row = $result->fetch_assoc()) {
            unset($row['locationid']); // remove locId as it is redundant
            $sensors[] = $row;
        }
    
        // get all value types
        $sql = "SELECT * FROM `valuetypes`";
    
        $result = $this->_con->query($sql);
    
        //loop through the returned data
        while ($row = $result->fetch_assoc()) {
            foreach ($sensors as $sensor) {
                if ($sensor['valuetypeid'] == $row['id']) {
                    $valuetypes[] = $row;
                    break;
                }
            }
        }
    
        if (isset($maxVals) && $maxVals > 0) {
            // build the new measvalues array with respect to maxVals for each sensor
    
            $finalMeasvals = array();
    
            foreach ($sensors as $sensor) {
                
                // get all measvalues of the current sensor
                $rawMeasvalsOfThisSensor = array();
                $finalMeasvalsOfThisSensor = array();
    
                foreach ($measvalues as $measvalue) {
                    if ($measvalue["sensorid"] === $sensor["id"]) {
                        array_push($rawMeasvalsOfThisSensor, $measvalue);
                    }
                }

                if (count($rawMeasvalsOfThisSensor) <= $maxVals) {
                    $finalMeasvals = array_merge(
                        $finalMeasvals, $rawMeasvalsOfThisSensor
                    );
                    continue;
                }
    
                // always sum up the same amount of values to get a new array
                // which doesn't have more than $maxVals values
                $countOfValuesForAvarage = intval(
                    round(count($rawMeasvalsOfThisSensor) / $maxVals, 0)
                );
    
                $takenValuesForNextSum = 0;
                $tmpMeasvalueSum = 0;
                $tmpTimestampSum = 0;
    
                for ($i = 0; $i < count($rawMeasvalsOfThisSensor); $i++) {
                    // loop through all measvals of the sensor
                    $measvalue = $rawMeasvalsOfThisSensor[$i];
                    
                    if ($measvalue["sensorid"] === $sensor["id"]) {
                        $tmpMeasvalueSum += $measvalue["measvalue"];
                        $tmpTimestampSum += $measvalue["timestamp"];
                        $takenValuesForNextSum += 1;
                    }
                    if ($takenValuesForNextSum === $countOfValuesForAvarage || $i === count($rawMeasvalsOfThisSensor) - 1) {
                        array_push(
                            $finalMeasvalsOfThisSensor,
                            array(
                                "measvalue" => round(
                                    $tmpMeasvalueSum / $takenValuesForNextSum, 2
                                ),
                                "timestamp" => round(
                                    $tmpTimestampSum / $takenValuesForNextSum, 0
                                ),
                                "sensorid" => $sensor["id"]
                            )
                        );
    
                        $takenValuesForNextSum = 0;
                        $tmpMeasvalueSum = 0;
                        $tmpTimestampSum = 0;
                    }
                }
    
                // insert the new vals of this sensor into the global new vals
                $finalMeasvals = array_merge(
                    $finalMeasvals, $finalMeasvalsOfThisSensor
                );
            }
    
            $measvalues = $finalMeasvals;
        }
    
        // find actual range
        $min = null;
        $max = null;

        foreach ($measvalues as $value) {
            if ($value['timestamp'] < $min || !isset($min)) {
                $min = $value['timestamp'];
            } else if ($value['timestamp'] > $max || !isset($max)) {
                $max = $value['timestamp'];
            }
        }
    
        // add sensors and value types to data object
        $data['measvalues'] = $measvalues;
        $data['sensors'] = $sensors;
        $data['valuetypes'] = $valuetypes;
        $data['range'] = array('from' => $min, 'to' => $max);

        return $data;
    }


}

?>