<?php
/**
 * HybridAuth
 * http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth
 * (c) 2009-2014, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html
 */

// A service client for the OAuth 2 flow.
// v0.1
class OAuth2Client
{

    public $api_base_url = '';

    public $authorize_url = '';

    public $token_url = '';

    public $token_info_url = '';

    public $client_id = '';

    public $client_secret = '';

    public $redirect_uri = '';

    public $access_token = '';

    public $refresh_token = '';

    public $access_token_expires_in = '';

    public $access_token_expires_at = '';

    // --
    public $sign_token_name = 'access_token';

    public $decode_json = true;

    public $curl_time_out = 30;

    public $curl_connect_time_out = 30;

    public $curl_ssl_verifypeer = false;

    public $curl_ssl_verifyhost = false;

    public $curl_header = [];

    public $curl_useragent = 'OAuth/2 Simple PHP Client v0.1; HybridAuth http://hybridauth.sourceforge.net/';

    public $curl_authenticate_method = 'POST';

        public $curl_proxy = null;

    // --
    public $http_code = '';

    public $http_info = '';

    // --
    public function __construct($client_id=false, $client_secret=false, $redirect_uri='')
    {
        $this->client_id     = $client_id;
        $this->client_secret = $client_secret;
        $this->redirect_uri  = $redirect_uri;

    }//end __construct()


    public function authorizeUrl($extras=[])
    {
         $params = [
             'client_id'     => $this->client_id,
             'redirect_uri'  => $this->redirect_uri,
             'response_type' => 'code',
         ];

         if (count($extras)) {
             foreach ($extras as $k => $v) {
                        $params[$k] = $v;
             }
         }

         return $this->authorize_url.'?'.http_build_query($params, '', '&');

    }//end authorizeUrl()


    public function authenticate($code)
    {
         $params = [
             'client_id'     => $this->client_id,
             'client_secret' => $this->client_secret,
             'grant_type'    => 'authorization_code',
             'redirect_uri'  => $this->redirect_uri,
             'code'          => $code,
         ];

         $response = $this->request($this->token_url, $params, $this->curl_authenticate_method);

         $response = $this->parseRequestResult($response);

         if (! $response || ! isset($response->access_token)) {
             throw new Exception('The Authorization Service has return: '.$response->error);
         }

         if (isset($response->access_token)) {
             $this->access_token = $response->access_token;
         }

         if (isset($response->refresh_token)) {
             $this->refresh_token = $response->refresh_token;
         }

         if (isset($response->expires_in)) {
             $this->access_token_expires_in = $response->expires_in;
         }

         // calculate when the access token expire
         if (isset($response->expires_in)) {
             $this->access_token_expires_at = (time() + $response->expires_in);
         }

         return $response;

    }//end authenticate()


    public function authenticated()
    {
        if ($this->access_token) {
            if ($this->token_info_url && $this->refresh_token) {
                // check if this access token has expired,
                $tokeninfo = $this->tokenInfo($this->access_token);

                // if yes, access_token has expired, then ask for a new one
                if ($tokeninfo && isset($tokeninfo->error)) {
                    $response = $this->refreshToken($this->refresh_token);

                    // if wrong response
                    if (! isset($response->access_token) || ! $response->access_token) {
                        throw new Exception('The Authorization Service has return an invalid response while requesting a new access token. given up!');
                    }

                    // set new access_token
                    $this->access_token = $response->access_token;
                }
            }

            return true;
        }//end if

        return false;

    }//end authenticated()


    /**
     * Format and sign an oauth for provider api
     */
    public function api($url, $method='GET', $parameters=[])
    {
        if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) {
            $url = $this->api_base_url.$url;
        }

        $parameters[$this->sign_token_name] = $this->access_token;
        $response = null;

        switch ($method) {
            case 'GET':
                $response = $this->request($url, $parameters, 'GET');
                break;

            case 'POST':
                $response = $this->request($url, $parameters, 'POST');
                break;
        }

        if ($response && $this->decode_json) {
            $response = json_decode($response);
        }

        return $response;

    }//end api()


    /**
     * GET wrapper for provider apis request
     */
    function get($url, $parameters=[])
    {
        return $this->api($url, 'GET', $parameters);

    }//end get()


    /**
     * POST wrapper for provider apis request
     */
    function post($url, $parameters=[])
    {
        return $this->api($url, 'POST', $parameters);

    }//end post()


    // -- tokens
    public function tokenInfo($accesstoken)
    {
        $params['access_token'] = $this->access_token;
        $response               = $this->request($this->token_info_url, $params);
        return $this->parseRequestResult($response);

    }//end tokenInfo()


    public function refreshToken($parameters=[])
    {
         $params = [
             'client_id'     => $this->client_id,
             'client_secret' => $this->client_secret,
             'grant_type'    => 'refresh_token',
         ];

         foreach ($parameters as $k => $v) {
             $params[$k] = $v;
         }

         $response = $this->request($this->token_url, $params, 'POST');
         return $this->parseRequestResult($response);

    }//end refreshToken()


    // -- utilities
    private function request($url, $params=false, $type='GET')
    {
        Hybrid_Logger::info("Enter OAuth2Client::request( $url )");
        Hybrid_Logger::debug('OAuth2Client::request(). dump request params: ', serialize($params));

        if ($type == 'GET') {
            $url = $url.( strpos($url, '?') ? '&' : '?' ).http_build_query($params, '', '&');
        }

        $this->http_info = [];
        $ch              = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_time_out);
        curl_setopt($ch, CURLOPT_USERAGENT, $this->curl_useragent);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->curl_connect_time_out);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->curl_ssl_verifypeer);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $this->curl_ssl_verifyhost);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this->curl_header);

        if ($this->curl_proxy) {
            curl_setopt($ch, CURLOPT_PROXY, $this->curl_proxy);
        }

        if ($type == 'POST') {
            curl_setopt($ch, CURLOPT_POST, 1);
            if ($params) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
            }
        }

        $response = curl_exec($ch);
        if ($response === false) {
                Hybrid_Logger::error('OAuth2Client::request(). curl_exec error: ', curl_error($ch));
        }

        Hybrid_Logger::debug('OAuth2Client::request(). dump request info: ', serialize(curl_getinfo($ch)));
        Hybrid_Logger::debug('OAuth2Client::request(). dump request result: ', serialize($response));

        $this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $this->http_info = array_merge($this->http_info, curl_getinfo($ch));

        curl_close($ch);

        return $response;

    }//end request()


    private function parseRequestResult($result)
    {
        if (json_decode($result)) {
            return json_decode($result);
        }

        parse_str($result, $output);

        $result = new StdClass();

        foreach ($output as $k => $v) {
            $result->$k = $v;
        }

        return $result;

    }//end parseRequestResult()


}//end class
