<?php
if (!isset($config)) require_once('crm_config.db.php');
if (!isset($crm_config)) require_once('crm_config.php');

if (!defined('_DB_HOST') && isset($config['db_host'])) define('_DB_HOST', $config['db_host']);
if (!defined('_DB_NAME') && isset($config['db_name'])) define('_DB_NAME', $config['db_name']);
if (!defined('_DB_USER') && isset($config['db_user'])) define('_DB_USER', $config['db_user']);
if (!defined('_DB_PASS') && isset($config['db_pass'])) define('_DB_PASS', $config['db_pass']);

class crm_database
{
    public static $msh;
    public $writelog;

    private $host;
    private $name;
    private $user;
    private $pass;

    private $connection;

    private $queryResult;
    private $queryString;

    public function __construct()
    {
        $this->connection   = false;
        $this->host         = '';
        $this->user         = '';
        $this->pass         = '';
        $this->name         = '';
        $this->writelog     = false;
    }

    public static function getInstance()
    {
        if (!empty(self::$msh)) {
            return self::$msh;
        }

        self::$msh = new crm_database();
        self::$msh->init();

        return self::$msh;
    }

    public static function escapeString($str = "")
    {
        $ret = mysqli_real_escape_string($this->connection, $str);
        return $ret;
    }

    public function init($host = _DB_HOST, $name = _DB_NAME, $user = _DB_USER, $pass = _DB_PASS)
    {
        $this->host = $host;
        $this->name = $name;
        $this->user = $user;
        $this->pass = $pass;
    }

    private function connect()
    {
        if ($this->connection) {
            return;
        }

        $this->connection = mysqli_connect($this->host, $this->user, $this->pass);

        if (false === $this->connection) {
            throw new Exception('Can\'t connect to db');
        }

        mysqli_select_db($this->connection, $this->name);
        mysqli_query($this->connection, 'SET CHARACTER SET UTF8');
        mysqli_query($this->connection, 'SET NAMES \'UTF8\' ');
        mysqli_set_charset($this->connection, 'utf8');

        if (mysqli_error($this->connection)) {
            throw new Exception(mysqli_error($this->connection));
        }
    }

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

    public function query($query, $getID = false)
    {
        $this->connect();
        $this->AddSemicolonToQueryString($query);
        $this->queryString = $query;

        if ($this->is_write_type($query) === true) {
            $boolWrite  = true;
            $arrSkip    = array('h_aktivalas_infok', 'h_history', 'u_history', 'u_userstatus_history', 'cl_hlusers.userstats2013', 'cl_hlusers.switch', 'cl_hlusers.writelog');

            foreach ($arrSkip as $tabla) {
                if (strpos($query, $tabla)) {
                    $boolWrite = false;
                    break;
                }
            }

            if ($boolWrite) {
                mysqli_query($this->connection, "INSERT INTO `cl_hlusers`.`writelog` (`user`,`datum`,`ip`,`url`,`func`,`sql`) VALUES (
                    '" . (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'www.archlinexp.com') . "',
                    '" . date("Y-m-d H:i:s", time()) . "',
                    '" . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1') . "',
                    '" . (isset($_SERVER['SERVER_NAME']) ? addslashes($_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']) : '') . "',
                    '" . addslashes("function: " . __FUNCTION__ . " method: " . __METHOD__) . "','" . addslashes($query) . "');");
            }
        }

        $this->queryResult = mysqli_query($this->connection, $query);
        if (mysqli_error($this->connection)) {
            throw new Exception(mysqli_error($this->connection) . ' Query: ' . $query);
        }

        if ($getID) return $this->getInsertedId();
    }

    public function AddSemicolonToQueryString(&$query)
    {
        $query = rtrim($query);

        if (substr($query, -1) != ';') {
            $query = $query . ";";
        }
    }

    public function fetchNext()
    {
        $back = array();
        if ($row = mysqli_fetch_assoc($this->queryResult)) {
            foreach ($row as $key => $value) {
                $row[$key] = stripslashes($value);
            }
            $back = $row;
            return $back;
        }

        return false;
    }

    public function fetchAssoc()
    {
        $back = array();
        while ($row = mysqli_fetch_assoc($this->queryResult)) {
            foreach ($row as $key => $value) {
                $row[$key] = stripslashes($value);
            }
            $back[] = $row;
        }

        return $back;
    }

    public function getInsertedId()
    {
        return mysqli_insert_id($this->connection);
    }

    public function select($table, $where = false, $offset = false, $limit = false, $order = false)
    {
        $whereStr = '';
        $limitStr = '';
        $orderStr = '';

        if (is_array($where)) {
            $whereStr = 'WHERE ' . $this->whereMakerPriv($where);
        } elseif ($where) {
            $whereStr = 'WHERE ' . $where;
        }

        if (($offset !== false and $offset !== null) && ($limit !== false and $limit !== null)) {
            $limitStr = 'LIMIT ' . $offset . ', ' . $limit;
        }

        if (is_array($order)) {
            $orderStr = 'ORDER BY ';
            foreach ($order as $key => $value) {
                $orderStr .= $key . ' ' . $value . ' ';
            }
        } elseif ($order) {
            $orderStr = 'ORDER BY ' . $order;
        }

        return $this->query('SELECT * FROM ' . $table . ' ' . $whereStr . ' ' . $orderStr . ' ' . $limitStr . ';');
    }

    public function getProp($table, $func, $field, $where = false)
    {
        $whereStr = '';
        if (is_array($where)) {
            $whereStr = 'WHERE ' . $this->whereMakerPriv($where);
        } elseif ($where) {
            $whereStr = 'WHERE ' . $where;
        }

        $this->query('SELECT ' . $func . '(' . $field . ') as p FROM ' . $table . ' ' . $whereStr . ';');

        $arr = $this->fetchAssoc();

        return $arr[0]['p'];
    }

    public function getCount($table, $field = 'id', $where = false)
    {
        return $this->getProp($table, 'COUNT', $field, $where);
    }

    public function getSum($table, $field = 'id', $where = false)
    {
        return $this->getProp($table, 'SUM', $field, $where);
    }

    public function getAvg($table, $field = 'id', $where = false)
    {
        return $this->getProp($table, 'AVG', $field, $where);
    }

    public function delete($table, $where = false)
    {
        $whereStr = '';
        if (is_array($where)) {
            $whereStr = 'WHERE ' . $this->whereMakerPriv($where);
        } elseif ($where) {
            $whereStr = 'WHERE ' . $where;
        }

        return $this->query('DELETE FROM ' . $table . ' ' . $whereStr . ';');
    }

    public function update($table, $infoArr, $where = false)
    {
        if (!is_array($infoArr)) {
            return false;
        }

        $whereStr = '';
        $dataArr = array();
        $data = '';
        if (is_array($where)) {
            $whereStr = 'WHERE ' . $this->whereMakerPriv($where);
        } elseif ($where) {
            $whereStr = 'WHERE ' . $where;
        }

        foreach ($infoArr as $key => $value) {
            $dataArr[] = '`' . $key . '`="' . mysqli_real_escape_string($this->connection, $value) . '" ';
        }
        $data = implode(',', $dataArr);

        return $this->query('UPDATE ' . $table . ' SET ' . $data . ' ' . $whereStr . ';');
    }

    public function insert($table, $infoArr)
    {
        $this->connect();
        $columArr = array();
        $valueArr = array();
        $colums = '';
        $values = '';

        foreach ($infoArr as $key => $value) {
            $columArr[] = '`' . $key . '`';
            $valueArr[] = '"' . mysqli_real_escape_string($this->connection, $value) . '"';
        }

        $colums = implode(',', $columArr);
        $values = implode(',', $valueArr);

        return $this->query('INSERT INTO ' . $table . ' (' . $colums . ') VALUES (' . $values . ');', true);
    }

    public function affectedRows()
    {
        return mysqli_affected_rows($this->connection);
    }

    public function whereMakerPriv($where)
    {
        if (!is_array($where)) {
            return 1;
        }
        $this->connect();
        $back = '1 ';
        foreach ($where as $key => $value) {
            if (is_array($value)) {
                $back .= 'AND `' . $key . '`' . $value[0] . '"' . $value[1] . '" ';
            } else {
                $back .= 'AND `' . $key . '`="' . mysqli_real_escape_string($this->connection, $value) . '" ';
            }
        }
        return $back;
    }

    public static function whereMaker($where)
    {
        if (!is_array($where)) {
            return 1;
        }
        $back = '1 ';
        foreach ($where as $key => $value) {
            if (is_array($value)) {
                $back .= 'AND `' . $key . '`' . $value[0] . '"' . $value[1] . '" ';
            } else {
                $back .= 'AND `' . $key . '`="' . $value . '" ';
            }
        }
        return $back;
    }

    public function is_write_type($sql)
    {
        if (!preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql)) {
            return false;
        }

        return true;
    }

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

    public function setConnect()
    {
        $this->connect();
    }
}

class Database
{
    private $host     = _DB_HOST;
    private $username = _DB_USER;
    private $password = _DB_PASS;

    private $queryResult;
    private $queryString;

    private static $msh;
    protected $connection;

    private static $arrSkip = array(
        "cl_hlusers.h_aktivalas_infok",
        "cl_hlusers.h_history",
        "cl_hlusers.u_history",
        "cl_hlusers.u_userstatus_history",
        "cl_hlusers.userstats2013",
        "cl_hlusers.switch",
        "cl_hlusers.writelog",
        "cl_hlusers.telemetria",
        "cl_hlusers.computers",
        "cl_hlusers.debug_table"
    );

    public function __construct()
    {
        if (!isset($this->connection)) {
            $this->connection = new mysqli($this->host, $this->username, $this->password);

            if (!$this->connection) {
                echo 'Cannot connect to database!';
                exit;
            }
        }

        unset($this->queryResult);

        $this->connection->set_charset("utf8");
        return $this->connection;
    }

    public static function getInstance()
    {
        if (!empty(self::$msh)) return self::$msh;
        self::$msh = new Database();
        return self::$msh;
    }

    public function query($query, $params = null, $isClosed = false, $getID = false)
    {
        // $params = array("ss","string_1","string_2");
        $this->addSemicolonToQueryString($query);

        $imp = '';
        if ($params != null) $imp = implode(", ", $params);
        $this->queryString = "{$query} ($imp)";

        if ($this->isWriteType($query)) {
            $boolWrite  = true;

            foreach (self::$arrSkip as $tabla) {
                if (strpos($query, $tabla)) {
                    $boolWrite = false;
                    break;
                }
            }

            // A nagios test ne irja tele a cl_hlusers.writelog tablat
            if (strpos($_SERVER['REQUEST_URI'], 'nagios_monitor.php') !== false || strpos($_SERVER['REQUEST_URI'], 'maintenance/test_API.php') !== false || $_SERVER['SERVER_NAME'] == 'secondary.cadline.hu')
                $boolWrite = false;

            // A tesztek futtatasakor ne logolja a modositasokat  (majd a webformos)
            if (strpos($_SERVER['REQUEST_URI'], 'maintenance/tips.php') /*&& $_SERVER['REMOTE_ADDR'] == '185.43.206.63'*/) {
                $paramIndex = 5;
                if ((substr($params[$paramIndex], 0, 7) == '3710018') || (substr($params[$paramIndex], 0, 7) == '3710028')
                    || (substr($params[$paramIndex], 0, 7) == '3710038') || (substr($params[$paramIndex], 0, 7) == '3700078')
                    || (substr($params[$paramIndex], 0, 7) == '3700018')
                ) {
                    $boolWrite = false;
                }
            }

            $func = addslashes("function: " . __FUNCTION__ . " method: " . __METHOD__)/*.' --> '.$params[$paramIndex]*/;

            if ($boolWrite) {
                mysqli_query($this->connection, "INSERT INTO `cl_hlusers`.`writelog` (`user`,`datum`,`ip`,`url`,`func`,`sql`) VALUES (
                    '" . (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'www.archlinexp.com') . "',
                    '" . date("Y-m-d H:i:s", time()) . "',
                    '" . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1') . "',
                    '" . (isset($_SERVER['SERVER_NAME']) ? addslashes($_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']) : '') . "',
                    '" . $func . "','" . addslashes($query) . "');");
            }
        }

        $this->queryResult = $this->connection->prepare($query);

        if ($params != null) {
            $tmp = array();
            foreach ($params as $key => $value) $tmp[$key] = &$params[$key];
            call_user_func_array(array($this->queryResult, 'bind_param'), $tmp);
        }

        $this->queryResult->execute();
        if (mysqli_error($this->connection)) throw new Exception(mysqli_error($this->connection) . ' Query: ' . $query);

        if ($getID) return $this->getInsertedId();
        if ($isClosed) $this->queryResult->close();
    }

    public function insert($table, $infoArr, $type, $needEscape = true)
    {
        $columArr = array();
        $bindArr  = array();
        $endArr   = array();
        $colums   = '';
        $values   = '';

        array_push($endArr, $type);

        foreach ($infoArr as $key => $value) {
            $columArr[] = "`{$key}`";
            $bindArr[]  = "?";

            $string = !$needEscape && $key == 'versionInfo' ? $value : mysqli_real_escape_string($this->connection, $value);
            array_push($endArr, $string);
        }

        $colums = implode(',', $columArr);
        $values = implode(',', $bindArr);

        $sql = "INSERT INTO {$table} ({$colums}) VALUES ({$values});";
        return $this->query($sql, $endArr, false, true);
    }

    public function update($table, $infoArr, $where = false, $type, $needEscape = true)
    {
        if (!is_array($infoArr)) return false;

        $whereStr   = '';
        $paramValue = array();
        $bindArr    = array();
        $endArr     = array();
        $data       = '';

        array_push($endArr, $type);

        if (is_array($where)) $whereStr = 'WHERE ' . $this->whereMakerPriv($where, $paramValue);
        elseif ($where) $whereStr = "WHERE {$where}";

        foreach ($infoArr as $key => $value) {
            $bindArr[] = "`{$key}` = ?";

            $string = !$needEscape && $key == 'versionInfo' ? $value : mysqli_real_escape_string($this->connection, $value);
            array_push($endArr, $string);
        }

        foreach ($paramValue as $param) array_push($endArr, mysqli_real_escape_string($this->connection, $param));

        $data = implode(',', $bindArr);

        $query = "UPDATE {$table} SET {$data} {$whereStr};";
        return $this->query($query, $endArr, true);
    }

    public function delete($table, $where = false, $type)
    {
        $whereStr   = '';
        $paramValue = array();
        $endArr     = array();

        array_push($endArr, $type);

        if (is_array($where)) $whereStr = 'WHERE ' . $this->whereMakerPriv($where, $paramValue);
        elseif ($where) $whereStr = "WHERE {$where}";

        foreach ($paramValue as $param) array_push($endArr, mysqli_real_escape_string($this->connection, $param));

        $query = "DELETE FROM {$table} {$whereStr};";
        return $this->query($query, $endArr, true);
    }

    public function fetchNext()
    {
        $result = $this->get_row();
        $this->queryResult->free_result();
        $this->queryResult->close();

        return $result;
    }

    public function fetchAssoc()
    {
        $result = $this->get_result();
        $this->queryResult->free_result();
        $this->queryResult->close();

        return $result;
    }

    protected function get_result()
    {
        $result = array();
        $this->queryResult->store_result();
        for ($i = 0; $i < $this->queryResult->num_rows; $i++) {
            $metadata   = $this->queryResult->result_metadata();
            $params     = array();
            while ($field = $metadata->fetch_field()) $params[] = &$result[$i][$field->name];
            call_user_func_array(array($this->queryResult, 'bind_result'), $params);
            $this->queryResult->fetch();
        }

        if (!empty($result)) return $result;
        else return null;
    }

    protected function get_row()
    {
        $result = array();
        $this->queryResult->store_result();

        if ($this->queryResult->num_rows > 0) {
            for ($i = 0; $i < 1; $i++) {
                $metadata   = $this->queryResult->result_metadata();
                $params     = array();
                while ($field = $metadata->fetch_field()) $params[] = &$result[$i][$field->name];
                call_user_func_array(array($this->queryResult, 'bind_result'), $params);
                $this->queryResult->fetch();
            }
        }

        if (!empty($result)) return $result[0];
        else return null;
    }

    public function getInsertedId()
    {
        $id = $this->queryResult->insert_id;
        $this->queryResult->close();

        return $id;
    }

    private function addSemicolonToQueryString(&$query)
    {
        $query = rtrim($query);

        if (substr($query, -1) != ';') $query = "{$query};";
    }

    private function whereMakerPriv($where, &$paramValue = array())
    {
        if (!is_array($where)) return 1;
        $back = '1 ';
        foreach ($where as $key => $value) {
            if (is_array($value)) {
                $back .= 'AND `' . $key . '`' . $value[0] . '"' . $value[1] . '" ';
            } else {
                $back .= "AND `{$key}` = ? ";
                array_push($paramValue, $value);
            }
        }
        return $back;
    }

    private function isWriteType($sql)
    {
        if (!preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql)) {
            return false;
        }
        return true;
    }

    public function getQueryString()
    {
        return $this->queryString;
    }
    public function getQueryResult()
    {
        return $this->queryResult;
    }
}
