<?php

static $g_dbClient;

class PhonalisaDBConnector
{

    private $cache;
    private $socket;
    private $lasterr;
    private $domain;

    public function connect()
    {
        $databaseServer = Yii::app()->params['databaseServer'];
        $multidomain = Yii::app()->params['multidomain'];

        $service_port = 6758; //fixme
        $address = gethostbyname($databaseServer);

        $phonalisaDomain = getPhonalisaDomain();
        if ($phonalisaDomain == false) {
            return false;
        }
        $this->domain = getDBDomainForPhonalisaDomain($phonalisaDomain);

        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($this->socket === false) {
            $this->lasterr = "socket_create() failed, Reason: " . socket_strerror(socket_last_error()) . "\n";
            return false;
        }
        $result = @socket_connect($this->socket, $address, $service_port);
        if ($result === false) {
            $this->lasterr = "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($this->socket)) . "\n";
            return false;
        }
        return true;
    }

    private static function prepareJSON($input)
    {
        //This will convert ASCII/ISO-8859-1 to UTF-8.
        //Be careful with the third parameter (encoding detect list), because
        //if set wrong, some input encodings will get garbled (including UTF-8!)
        $input = mb_convert_encoding($input, 'UTF-8', 'ASCII,UTF-8,ISO-8859-1');

        //Remove UTF-8 BOM if present, json_decode() does not like it.
        if (substr($input, 0, 3) == pack("CCC", 0xEF, 0xBB, 0xBF))
            $input = substr($input, 3);

        return $input;
    }

    private static function staticExec($action, $data, &$bin = NULL, &$binLength = 0)
    {
        $client = PhonalisaDBConnector::instance();
        $result = $client->execute($action, $data, $bin, $binLength);
        $jObject = json_decode($result);

        return $jObject;
    }

    public function execute($action, $data, &$bin = NULL, &$binLength = 0)
    {
        $module = "dbserver";
        $data = array("domain" => $this->domain, "module" => $module, "action" => $action, "data" => $data);
        $data_string = json_encode($data);

        $msg = new TcpMsg();
        $msg->component = "dbserver";
        $msg->name = $action;
        $msg->processType = MsgProcessType::Command;
        $msg->dataType = MsgDataType::Text;
        $msg->msgItems["data"] = $data_string;
        $msg->binaryData = $bin;
        $msg->binaryDataSize = $binLength;
        $msg->send($this->socket);

        //get the answer
        $answer = new TcpMsg();
        $answer->recieve($this->socket);
        $bin = $answer->binaryData;
        $binLength = $answer->binaryDataSize;

        return $answer->msgItems["data"];
    }

    public function disconnect()
    {
        socket_close($socket);
    }

    private static function instance()
    {
        global $g_dbClient;
        if ($g_dbClient == NULL) {

            $g_dbClient = new PhonalisaDBConnector();
            if (!$g_dbClient->connect()) {
                throw new CDbException("Could not connect to Database Server!");
            }
            Log::d("Connected to Database Server.");
        }
        return $g_dbClient;
    }

    public static function save($name, $attributes, $id = "")
    {
        PhonalisaDBConnector::unsetFromCache($name, $id);

        $data = array();
        $data["attributes"] = $attributes;
        $data["id"] = "";
        if ($id != "") {
            $data['id'] = $id;
        }

        $module_data = array($name => $data);

        if ($module_data !== false) {
            Yii::app()->cache->delete($id); // remove from cache
            return PhonalisaDBConnector::staticExec("save", $module_data);
        }
    }

    public static function saveBinaryField($name, $id, $field, $bin, $length)
    {
        $data = array();
        $data['id'] = $id;
        $data['field'] = $field;

        $module_data = array($name => $data);

        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("saveBinaryField", $module_data, $bin, $length);
        }
    }

    public static function getBinaryField($name, $id, $field, &$bin, &$length)
    {
        $data = array();
        $data['id'] = $id;
        $data['field'] = $field;

        $module_data = array($name => $data);
        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("getBinaryField", $module_data, $bin, $length);
        }
    }

    public static function deleteBinaryField($name, $id, $field, $bin = '', $length = 0)
    {
        $data = array();
        $data['id'] = $id;
        $data['field'] = $field;

        $module_data = array($name => $data);

        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("deleteBinaryField", $module_data, $bin, $length);
        }
    }

    public static function storeLicense($bin, $length)
    {
        $data = array();
        return PhonalisaDBConnector::staticExec("storeLicense", $data, $bin, $length);
    }

    public static function getLicense()
    {
        $data = array();
        return PhonalisaDBConnector::staticExec("getLicense", $data);
    }

    public static function getSystemID()
    {
        $data = array();
        return PhonalisaDBConnector::staticExec("getSystemID", $data);
    }

    public static function getBackups()
    {
        $data = array();
        return PhonalisaDBConnector::staticExec("getbackups", $data);
    }

    public static function getBackupData($filename, &$bin, &$length, $limit, $offset)
    {
        $data = array();
        $data['filename'] = $filename;
        $data['limit'] = $limit;
        $data['offset'] = $offset;

        return PhonalisaDBConnector::staticExec("getbackupdata", $data, $bin, $length);
    }

    public static function storeBackupData($filename, $bin, $length, $append)
    {
        $data = array();
        $data['filename'] = $filename;
        $data['append'] = $append;

        return PhonalisaDBConnector::staticExec("storebackupdata", $data, $bin, $length);
    }

    public static function deleteBackup($fileName)
    {
        $data = array("filename" => $fileName);
        return PhonalisaDBConnector::staticExec("deletebackup", $data);
    }

    public static function restoreBackup($fileName)
    {
        $data = array("filename" => $fileName);
        return PhonalisaDBConnector::staticExec("restorebackup", $data);
    }

    public static function createBackup($comment)
    {
        $data = array("comment" => $comment);
        return PhonalisaDBConnector::staticExec("createbackup", $data);
    }

    // @stefan: NEVER USE THIS METHOD! NEVER!
    public static function resetDatabase()
    {
        $data = array();
        return PhonalisaDBConnector::staticExec("resetdatabase", $data);
    }

    public static function delete($name, $id = "")
    {
        $data = array();
        $data["id"] = "";
        if ($id != "") {
            $data['id'] = $id;
        }
        $module_data = array($name => $data);

        if ($module_data !== false) {
            Yii::app()->cache->delete($id); // remove from cache
            return PhonalisaDBConnector::staticExec("delete", $module_data);
        }
    }

    public static function findAllByAttributes($name, $attributes, $limit = 1, $offset = 0)
    {
        $user = array("limit" => (int) $limit, 'offset' => (int) $offset, "attributes" => $attributes);
        $module_data = array($name => $user);
        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("findallbyattributes", $module_data);
        }
    }

    public static function findAll($name, $attributes, $limit = 1, $offset = 0)
    {
        $user = array("limit" => (int) $limit, "offset" => (int) $offset, "attributes" => $attributes);
        $module_data = array($name => $user);

        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("findall", $module_data);
        }
    }

    public static function find($name, $attributes, $sort, $limit = 1, $offset = 0)
    {
        $newSort = explode(", ", $sort);
        $sort = $newSort[count($newSort) - 1];

        $user = array("limit" => (int) $limit, "offset" => (int) $offset, "attributes" => $attributes, "sort" => $sort);
        $module_data = array($name => $user);

        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("find", $module_data);
        }
    }

    public static function count($name, $attributes)
    {
        $user = array("attributes" => $attributes);
        $module_data = array($name => $user);

        if ($module_data !== false) {
            return PhonalisaDBConnector::staticExec("count", $module_data);
        }
    }

    public static function findByPk($name, $id)
    {
        $cached = PhonalisaDBConnector::getFromCache($name, $id);
        if ($cached !== false) {
            return $cached;
        }

        $user = array("id" => $id);
        $module_data = array($name => $user);
        $data = PhonalisaDBConnector::staticExec("findbypk", $module_data);

        PhonalisaDBConnector::storeInCache($name, $id, $data);

        return $data;
    }

    private static function getFromCache($name, $id)
    {
        $client = PhonalisaDBConnector::instance();
        $data = false;
        if (isset($client->cache[$name][$id])) {
            $data = $client->cache[$name][$id];
        }
        return $data;
    }

    private static function storeInCache($name, $id, $data)
    {
        $client = PhonalisaDBConnector::instance();
        $client->cache[$name][$id] = $data;
    }

    private static function unsetFromCache($name, $id)
    {
        $client = PhonalisaDBConnector::instance();
        if (isset($client->cache[$name][$id])) {
            unset($client->cache[$name][$id]);
        }
    }
}
