<?php
    class Session {
        protected $session_id;
        public $date_added = '';
        public $last_access = '';
        public $expire = '';
        public $now = '';
        public $data = array();
	
        public function __construct($db) {
            $this->db = $db;
            $this->expire = SESSION_GC_MAXLIFETIME;
            $this->now = date('Y-m-d H:i:s', time());
            $this->start();
            register_shutdown_function(array($this, 'close'));
        }

        public function start() {
            if (isset($_COOKIE[SESSION_NAME])) {
                $session_id = $_COOKIE[SESSION_NAME];
            } else {
                if (function_exists('random_bytes')) {
                    $session_id = substr(bin2hex(random_bytes(26)), 0, 26);
                } else {
                    $session_id = substr(bin2hex(openssl_random_pseudo_bytes(26)), 0, 26);
                }
                setcookie(SESSION_NAME, $session_id, time() + $this->expire, ini_get('session.cookie_path'), ini_get('session.cookie_domain'));
            }
    
            if (preg_match('/^[a-zA-Z0-9,\-]{22,52}$/', $session_id)) {
                $this->session_id = $session_id;
            } else {
                $this->start();
            }
            
            $this->data = $this->read($session_id);            
            return $session_id;
        }

        public function read($session_id) {
            $query = $this->db->query("SELECT `date_added`, `last_access`, `data`, `user_agent` FROM `{$this->db->prefix}session` WHERE session_id=:session_id AND expire>:expire", array(':session_id'=>$session_id, ':expire'=>(int)time()));

            if ($query->num_rows) {
                $this->date_added = $query->row['date_added'];
                $this->last_access = $this->now;
                return json_decode($query->row['data'], true);
            } else {
                $this->date_added = $this->now;
                $this->last_access = $this->now;
                return false;
            }
        }
        
        public function write($session_id, $data) {
            if ($session_id) {
                $data = $data ? json_encode($data) : '';
                $this->db->query("REPLACE INTO `{$this->db->prefix}session` SET session_id = :session_id, date_added=:date_added, last_access=:last_access, `data` = :data, expire = :expire, user_agent=:user_agent", array(':session_id' => $session_id, 'date_added' => $this->date_added, ':last_access' => $this->last_access, 'data' => $data, ':expire' => date('Y-m-d H:i:s', time() + $this->expire), ':user_agent'=>$_SERVER['HTTP_USER_AGENT']));
            }
        }
        
        public function destroy() {
            $this->delete($this->getId());
            $this->regenerate_session_id();
        }

        public function delete($session_id) {
            $this->db->query("DELETE FROM `{$this->db->prefix}session` WHERE session_id = :session_id", array('session_id' => $session_id));
        }
        
        public function gc($expire) {
            $this->db->query("DELETE FROM `{$this->db->prefix}session` WHERE expire < :expire", array('expire'=> ((int)time() + $expire)));
        }

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

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

        public function getUserSessions($user_id) {
            $query = $this->db->query("SELECT * FROM `{$this->db->prefix}session` WHERE data LIKE '%\"user_id\":\"$user_id\"%' ORDER BY last_access DESC");
            if ($query->num_rows) {
                return $query->rows;
            } else {
                return false;
            }
        }

        public function close() {
            $this->write($this->getId(), $this->data);
        }

        public function regenerate_session_id() {
            if (function_exists('random_bytes')) {
                $session_id = substr(bin2hex(random_bytes(26)), 0, 26);
            } else {
                $session_id = substr(bin2hex(openssl_random_pseudo_bytes(26)), 0, 26);
            }

            $this->session_id = $session_id;
            
            setcookie(SESSION_NAME, $session_id, time() + $this->expire, ini_get('session.cookie_path'), '/');
        }

        public function getOnlineVisitorsCount() {
            $stmt = $this->db->query("SELECT COUNT(*) AS 'total' FROM `{$this->db->prefix}session` WHERE last_access > NOW() - INTERVAL 15 MINUTE");
            return $stmt->row['total'];
        }
    }