<?php

namespace IMSGlobal\LTI\ToolProvider\DataConnector;

use IMSGlobal\LTI\ToolProvider\ConsumerNonce;
use IMSGlobal\LTI\ToolProvider\Context;
use IMSGlobal\LTI\ToolProvider\ResourceLink;
use IMSGlobal\LTI\ToolProvider\ResourceLinkShareKey;
use IMSGlobal\LTI\ToolProvider\ToolConsumer;
use IMSGlobal\LTI\ToolProvider\ToolProxy;
use IMSGlobal\LTI\ToolProvider\User;
use PDO;

/**
 * Class to provide a connection to a persistent store for LTI objects
 *
 * This class assumes no data persistence - it should be extended for specific database connections.
 *
 * @author  Stephen P Vickers <svickers@imsglobal.org>
 * @copyright  IMS Global Learning Consortium Inc
 * @date  2016
 * @version 3.0.0
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */
class DataConnector
{

/**
 * Default name for database table used to store tool consumers.
 */
    const CONSUMER_TABLE_NAME = 'lti2_consumer';
/**
 * Default name for database table used to store pending tool proxies.
 */
    const TOOL_PROXY_TABLE_NAME = 'lti2_tool_proxy';
/**
 * Default name for database table used to store contexts.
 */
    const CONTEXT_TABLE_NAME = 'lti2_context';
/**
 * Default name for database table used to store resource links.
 */
    const RESOURCE_LINK_TABLE_NAME = 'lti2_resource_link';
/**
 * Default name for database table used to store users.
 */
    const USER_RESULT_TABLE_NAME = 'lti2_user_result';
/**
 * Default name for database table used to store resource link share keys.
 */
    const RESOURCE_LINK_SHARE_KEY_TABLE_NAME = 'lti2_share_key';
/**
 * Default name for database table used to store nonce values.
 */
    const NONCE_TABLE_NAME = 'lti2_nonce';

/**
 * Database object.
 *
 * @var object $db
 */
    protected $db = null;
/**
 * Prefix for database table names.
 *
 * @var string $dbTableNamePrefix
 */
    protected $dbTableNamePrefix = '';
/**
 * SQL date format (default = 'Y-m-d')
 *
 * @var string $dateFormat
 */
    protected $dateFormat = 'Y-m-d';
/**
 * SQL time format (default = 'H:i:s')
 *
 * @var string $timeFormat
 */
    protected $timeFormat = 'H:i:s';

/**
 * Class constructor
 *
 * @param object $db                 Database connection object
 * @param string $dbTableNamePrefix  Prefix for database table names (optional, default is none)
 */
    public function __construct($db, $dbTableNamePrefix = '')
    {

        $this->db = $db;
        $this->dbTableNamePrefix = $dbTableNamePrefix;

    }

###
###  ToolConsumer methods
###

/**
 * Load tool consumer object.
 *
 * @param ToolConsumer $consumer ToolConsumer object
 *
 * @return boolean True if the tool consumer object was successfully loaded
 */
    public function loadToolConsumer($consumer)
    {

        $consumer->secret = 'secret';
        $consumer->enabled = true;
        $now = time();
        $consumer->created = $now;
        $consumer->updated = $now;

        return true;

    }

/**
 * Save tool consumer object.
 *
 * @param ToolConsumer $consumer Consumer object
 *
 * @return boolean True if the tool consumer object was successfully saved
 */
    public function saveToolConsumer($consumer)
    {

        $consumer->updated = time();

        return true;

    }

/**
 * Delete tool consumer object.
 *
 * @param ToolConsumer $consumer Consumer object
 *
 * @return boolean True if the tool consumer object was successfully deleted
 */
    public function deleteToolConsumer($consumer)
    {

        $consumer->initialize();

        return true;

    }

/**
 * Load tool consumer objects.
 *
 * @return array Array of all defined ToolConsumer objects
 */
    public function getToolConsumers()
    {

        return array();

    }


###
###  ToolProxy methods
###

/**
 * Load tool proxy object.
 *
 * @param ToolProxy $toolProxy ToolProxy object
 *
 * @return boolean True if the tool proxy object was successfully loaded
 */
    public function loadToolProxy($toolProxy)
    {

        $now = time();
        $toolProxy->created = $now;
        $toolProxy->updated = $now;

        return true;

    }

/**
 * Save tool proxy object.
 *
 * @param ToolProxy $toolProxy ToolProxy object
 *
 * @return boolean True if the tool proxy object was successfully saved
 */
    public function saveToolProxy($toolProxy)
    {

        $toolProxy->updated = time();

        return true;

    }

/**
 * Delete tool proxy object.
 *
 * @param ToolProxy $toolProxy ToolProxy object
 *
 * @return boolean True if the tool proxy object was successfully deleted
 */
    public function deleteToolProxy($toolProxy)
    {

        $toolProxy->initialize();

        return true;

    }

###
###  Context methods
###

/**
 * Load context object.
 *
 * @param Context $context Context object
 *
 * @return boolean True if the context object was successfully loaded
 */
    public function loadContext($context)
    {

        $now = time();
        $context->created = $now;
        $context->updated = $now;

        return true;

    }

/**
 * Save context object.
 *
 * @param Context $context Context object
 *
 * @return boolean True if the context object was successfully saved
 */
    public function saveContext($context)
    {

        $context->updated = time();

        return true;

    }

/**
 * Delete context object.
 *
 * @param Context $context Context object
 *
 * @return boolean True if the Context object was successfully deleted
 */
    public function deleteContext($context)
    {

        $context->initialize();

        return true;

    }

###
###  ResourceLink methods
###

/**
 * Load resource link object.
 *
 * @param ResourceLink $resourceLink Resource_Link object
 *
 * @return boolean True if the resource link object was successfully loaded
 */
    public function loadResourceLink($resourceLink)
    {

        $now = time();
        $resourceLink->created = $now;
        $resourceLink->updated = $now;

        return true;

    }

/**
 * Save resource link object.
 *
 * @param ResourceLink $resourceLink Resource_Link object
 *
 * @return boolean True if the resource link object was successfully saved
 */
    public function saveResourceLink($resourceLink)
    {

        $resourceLink->updated = time();

        return true;

    }

/**
 * Delete resource link object.
 *
 * @param ResourceLink $resourceLink Resource_Link object
 *
 * @return boolean True if the resource link object was successfully deleted
 */
    public function deleteResourceLink($resourceLink)
    {

        $resourceLink->initialize();

        return true;

    }

/**
 * Get array of user objects.
 *
 * Obtain an array of User objects for users with a result sourcedId.  The array may include users from other
 * resource links which are sharing this resource link.  It may also be optionally indexed by the user ID of a specified scope.
 *
 * @param ResourceLink $resourceLink      Resource link object
 * @param boolean     $localOnly True if only users within the resource link are to be returned (excluding users sharing this resource link)
 * @param int         $idScope     Scope value to use for user IDs
 *
 * @return array Array of User objects
 */
    public function getUserResultSourcedIDsResourceLink($resourceLink, $localOnly, $idScope)
    {

        return array();

    }

/**
 * Get array of shares defined for this resource link.
 *
 * @param ResourceLink $resourceLink Resource_Link object
 *
 * @return array Array of ResourceLinkShare objects
 */
    public function getSharesResourceLink($resourceLink)
    {

        return array();

    }

###
###  ConsumerNonce methods
###

/**
 * Load nonce object.
 *
 * @param ConsumerNonce $nonce Nonce object
 *
 * @return boolean True if the nonce object was successfully loaded
 */
    public function loadConsumerNonce($nonce)
    {
        return false;  // assume the nonce does not already exist

    }

/**
 * Save nonce object.
 *
 * @param ConsumerNonce $nonce Nonce object
 *
 * @return boolean True if the nonce object was successfully saved
 */
    public function saveConsumerNonce($nonce)
    {

        return true;

    }

###
###  ResourceLinkShareKey methods
###

/**
 * Load resource link share key object.
 *
 * @param ResourceLinkShareKey $shareKey Resource_Link share key object
 *
 * @return boolean True if the resource link share key object was successfully loaded
 */
    public function loadResourceLinkShareKey($shareKey)
    {

        return true;

    }

/**
 * Save resource link share key object.
 *
 * @param ResourceLinkShareKey $shareKey Resource link share key object
 *
 * @return boolean True if the resource link share key object was successfully saved
 */
    public function saveResourceLinkShareKey($shareKey)
    {

        return true;

    }

/**
 * Delete resource link share key object.
 *
 * @param ResourceLinkShareKey $shareKey Resource link share key object
 *
 * @return boolean True if the resource link share key object was successfully deleted
 */
    public function deleteResourceLinkShareKey($shareKey)
    {

        return true;

    }

###
###  User methods
###

/**
 * Load user object.
 *
 * @param User $user User object
 *
 * @return boolean True if the user object was successfully loaded
 */
    public function loadUser($user)
    {

        $now = time();
        $user->created = $now;
        $user->updated = $now;

        return true;

    }

/**
 * Save user object.
 *
 * @param User $user User object
 *
 * @return boolean True if the user object was successfully saved
 */
    public function saveUser($user)
    {

        $user->updated = time();

        return true;

    }

/**
 * Delete user object.
 *
 * @param User $user User object
 *
 * @return boolean True if the user object was successfully deleted
 */
    public function deleteUser($user)
    {

        $user->initialize();

        return true;

    }

###
###  Other methods
###

/**
 * Return a hash of a consumer key for values longer than 255 characters.
 *
 * @param string $key
 * @return string
 */
    protected static function getConsumerKey($key)
    {

        $len = strlen($key);
        if ($len > 255) {
            $key = 'sha512:' . hash('sha512', $key);
        }

        return $key;

    }

/**
 * Create data connector object.
 *
 * A data connector provides access to persistent storage for the different objects.
 *
 * Names of tables may be given a prefix to allow multiple versions to share the same schema.  A separate sub-class is defined for
 * each different database connection - the class to use is determined by inspecting the database object passed, but this can be overridden
 * (for example, to use a bespoke connector) by specifying a type.  If no database is passed then this class is used which acts as a dummy
 * connector with no persistence.
 *
 * @param string  $dbTableNamePrefix  Prefix for database table names (optional, default is none)
 * @param object  $db                 A database connection object or string (optional, default is no persistence)
 * @param string  $type               The type of data connector (optional, default is based on $db parameter)
 *
 * @return DataConnector Data connector object
 */
    public static function getDataConnector($dbTableNamePrefix = '', $db = null, $type = '')
    {

        if (is_null($dbTableNamePrefix)) {
            $dbTableNamePrefix = '';
        }
        if (!is_null($db) && empty($type)) {
            if (is_object($db)) {
                $type = get_class($db);
            }
        }
        $type = strtolower($type);
        if (($type === 'pdo') && ($db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite')) {
            $type .= '_sqlite';
        }
        if (!empty($type)) {
            $type ="DataConnector_{$type}";
        } else {
            $type ='DataConnector';
        }
        $type = "\\IMSGlobal\\LTI\\ToolProvider\\DataConnector\\{$type}";
        $dataConnector = new $type($db, $dbTableNamePrefix);

        return $dataConnector;

    }

/**
 * Generate a random string.
 *
 * The generated string will only comprise letters (upper- and lower-case) and digits.
 *
 * @param int $length Length of string to be generated (optional, default is 8 characters)
 *
 * @return string Random string
 */
    static function getRandomString($length = 8)
    {

        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

        $value = '';
        $charsLength = strlen($chars) - 1;

        for ($i = 1 ; $i <= $length; $i++) {
            $value .= $chars[rand(0, $charsLength)];
        }

        return $value;

    }

/**
 * Quote a string for use in a database query.
 *
 * Any single quotes in the value passed will be replaced with two single quotes.  If a null value is passed, a string
 * of 'null' is returned (which will never be enclosed in quotes irrespective of the value of the $addQuotes parameter.
 *
 * @param string $value Value to be quoted
 * @param bool $addQuotes If true the returned string will be enclosed in single quotes (optional, default is true)
 * @return string The quoted string.
 */
    static function quoted($value, $addQuotes = true)
    {

        if (is_null($value)) {
            $value = 'null';
        } else {
            $value = str_replace('\'', '\'\'', $value);
            if ($addQuotes) {
                $value = "'{$value}'";
            }
        }

        return $value;

    }

}
