<?php
namespace Spot\Adapter;

/**
 * Abstract Adapter
 *
 * @package Spot
 * @link http://spot.os.ly
 */
abstract class AdapterAbstract
{
	// Format for date columns, formatted for PHP's date() function
	protected $_format_date;
	protected $_format_time;
	protected $_format_datetime;
	
	// Connection details
	protected $_connection;
	protected $_dsn;
	protected $_dsnParts;
	protected $_options;
	
	
	/**
    * @param mixed $dsn DSN string or pre-existing Mongo object
    * @param array $options
    */
    public function __construct($dsn, array $options = array())
    {
		$this->_dsn = $dsn;
		if(is_string($dsn)) {
			// Parse DSN string
			$this->_dsnParts = self::parseDSN($dsn);
			
		} elseif(is_array($dsn)) {
			// Array of keys => values
			$this->_dsnParts = $dsn;
			
		} elseif(null === $dsn) {
			// No DSN string given
			$this->_dsnParts = array();
		}
        $this->_options = $options;
    }
	
	
	/**
	 * Get database format
	 *
	 * @return string Date format for PHP's date() function
	 */
	public function dateFormat()
	{
		return $this->_format_date;
	}
	
	
	/**
	 * Get database time format
	 *
	 * @return string Time format for PHP's date() function
	 */
	public function timeFormat()
	{
		return $this->_format_time;
	}
	
	
	/**
	 * Get database format
	 *
	 * @return string DateTime format for PHP's date() function
	 */
	public function dateTimeFormat()
	{
		return $this->_format_datetime;
	}
	
	
	/**
	 * Get date
	 *
	 * @return object DateTime
	 */
	public function date($format = null)
	{
		if(null === $format) {
			$format = $this->dateFormat();
		}
		return $this->dateTimeObject($format . ' ' . $this->timeFormat());
	}
	
	
	/**
	 * Get database time format
	 *
	 * @return object DateTime
	 */
	public function time($format = null)
	{
		if(null === $format) {
			$format = $this->timeFormat();
		}
		return $this->dateTimeObject($this->dateFormat() . ' ' . $format);
	}
	
	
	/**
	 * Get datetime
	 *
	 * @return object DateTIme
	 */
	public function dateTime($format = null)
	{
		if(null === $format) {
			$format = $this->dateTimeFormat();
		}
		return $this->dateTimeObject($format);
	}
	
	
	/**
	 * Turn formstted date into timestamp
	 * Also handles input timestamps
	 *
	 * @return DateTime object
	 */
	protected function dateTimeObject($format)
	{
		// Already a timestamp?
		if(is_int($format) || is_float($format)) { // @link http://www.php.net/manual/en/function.is-int.php#97006
			$dt = new \DateTime();
            $dt->setTimestamp($format); // Timestamps must be prefixed with '@' symbol
		} else {
            $dt = new \DateTime();
            $dt->format($format);
        }
		return $dt;
	}
	
	
	/**
	 * Taken from PHPUnit 3.4:
	 * @link http://github.com/sebastianbergmann/phpunit/blob/3.4/PHPUnit/Util/PDO.php
	 *
     * Returns the Data Source Name as a structure containing the various parts of the DSN.
     *
     * Additional keys can be added by appending a URI query string to the
     * end of the DSN.
     *
     * The format of the supplied DSN is in its fullest form:
     * <code>
     *  adapter(dbsyntax)://username:password@protocol+host/database?option=8&another=true
     * </code>
     *
     * Most variations are allowed:
     * <code>
     *  adapter://username:password@protocol+host:110//usr/db_file.db?mode=0644
     *  adapter://username:password@host/database_name
     *  adapter://username:password@host
     *  adapter://username@host
     *  adapter://host/database
     *  adapter://host
     *  adapter(dbsyntax)
     *  adapter
     * </code>
     *
     * This function is 'borrowed' from PEAR /DB.php .
     *
     * @param string $dsn Data Source Name to be parsed
     *
     * @return array an associative array with the following keys:
     *  + adapter:  Database backend used in PHP (mysql, odbc etc.)
     *  + dbsyntax: Database used with regards to SQL syntax etc.
     *  + protocol: Communication protocol to use (tcp, unix etc.)
     *  + host: Host specification (hostname[:port])
     *  + database: Database to use on the DBMS server
     *  + username: User name for login
     *  + password: Password for login
     */
    public static function parseDSN( $dsn )
    {
        if ( $dsn == 'sqlite::memory:' )
        {
            $dsn = 'sqlite://:memory:';
        }

        $parsed = array(
            'adapter'  => FALSE,
            'dbsyntax' => FALSE,
            'username' => FALSE,
            'password' => FALSE,
            'protocol' => FALSE,
            'host'     => FALSE,
            'port'     => FALSE,
            'socket'   => FALSE,
            'database' => FALSE,
        );

        if ( is_array( $dsn ) )
        {
            $dsn = array_merge( $parsed, $dsn );
            if ( !$dsn['dbsyntax'] )
            {
                $dsn['dbsyntax'] = $dsn['adapter'];
            }
            return $dsn;
        }

        // Find phptype and dbsyntax
        if ( ( $pos = strpos( $dsn, '://' ) ) !== FALSE )
        {
            $str = substr( $dsn, 0, $pos );
            $dsn = substr( $dsn, $pos + 3 );
        }
        else
        {
            $str = $dsn;
            $dsn = NULL;
        }

        // Get phptype and dbsyntax
        // $str => phptype(dbsyntax)
        if ( preg_match( '|^(.+?)\((.*?)\)$|', $str, $arr ) )
        {
            $parsed['adapter']  = $arr[1];
            $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
        }
        else
        {
            $parsed['adapter']  = $str;
            $parsed['dbsyntax'] = $str;
        }

        if ( !count( $dsn ) )
        {
            return $parsed;
        }

        // Get (if found): username and password
        // $dsn => username:password@protocol+host/database
        if ( ( $at = strrpos( (string) $dsn, '@' ) ) !== FALSE )
        {
            $str = substr( $dsn, 0, $at );
            $dsn = substr( $dsn, $at + 1 );
            if ( ( $pos = strpos( $str, ':' ) ) !== FALSE )
            {
                $parsed['username'] = rawurldecode( substr( $str, 0, $pos ) );
                $parsed['password'] = rawurldecode( substr( $str, $pos + 1 ) );
            }
            else
            {
                $parsed['username'] = rawurldecode( $str );
            }
        }

        // Find protocol and host

        if ( preg_match( '|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match ) )
        {
            // $dsn => proto(proto_opts)/database
            $proto       = $match[1];
            $proto_opts  = $match[2] ? $match[2] : FALSE;
            $dsn         = $match[3];
        }
        else
        {
            // $dsn => protocol+host/database (old format)
            if ( strpos( $dsn, '+' ) !== FALSE )
            {
                list( $proto, $dsn ) = explode( '+', $dsn, 2 );
            }
            if ( strpos( $dsn, '/' ) !== FALSE )
            {
                list( $proto_opts, $dsn ) = explode( '/', $dsn, 2 );
            }
            else
            {
                $proto_opts = $dsn;
                $dsn = NULL;
            }
        }

        // process the different protocol options
        $parsed['protocol'] = ( !empty( $proto ) ) ? $proto : 'tcp';
        $proto_opts = rawurldecode( $proto_opts );
        if ( $parsed['protocol'] == 'tcp' )
        {
            if ( strpos( $proto_opts, ':' ) !== FALSE )
            {
                list( $parsed['host'], $parsed['port'] ) = explode( ':', $proto_opts );
            }
            else
            {
                $parsed['host'] = $proto_opts;
            }
        }
        elseif ( $parsed['protocol'] == 'unix' )
        {
            $parsed['socket'] = $proto_opts;
        }

        // Get dabase if any
        // $dsn => database
        if ( $dsn )
        {
            if ( ( $pos = strpos( $dsn, '?' ) ) === FALSE )
            {
                // /database
                $parsed['database'] = rawurldecode( $dsn );
            }
            else
            {
                // /database?param1=value1&param2=value2
                $parsed['database'] = rawurldecode( substr( $dsn, 0, $pos ) );
                $dsn = substr( $dsn, $pos + 1 );
                if ( strpos( $dsn, '&') !== FALSE )
                {
                    $opts = explode( '&', $dsn );
                }
                else
                { // database?param1=value1
                    $opts = array( $dsn );
                }
                foreach ( $opts as $opt )
                {
                    list( $key, $value ) = explode( '=', $opt );
                    if ( !isset( $parsed[$key] ) )
                    {
                        // don't allow params overwrite
                        $parsed[$key] = rawurldecode( $value );
                    }
                }
            }
        }
        return $parsed;
    }
}
