<?php
namespace CorpusParole\Libraries\Sparql;

use EasyRdf\Sparql\Client;
use EasyRdf\Exception;
use EasyRdf\Format;
use EasyRdf\Http;

class TimeoutSparqlClient extends Client {

    public function query($query, $timeout=0)
    {
        return $this->request('query', $query, $timeout);
    }

    protected function request($type, $query, $timeout=0)
    {
        $processed_query = $this->preprocessQuery($query);
        $response = $this->executeQuery($processed_query, $type, $timeout);
        if (!$response->isSuccessful()) {
            throw new Http\Exception("HTTP request for SPARQL query failed", 0, null, $response->getBody());
        }
        if ($response->getStatus() == 204) {
            // No content
            return $response;
        }
        return $this->parseResponseToQuery($response);
    }

    /**
     * Build http-client object, execute request and return a response
     *
     * @param string $processed_query
     * @param string $type            Should be either "query" or "update"
     *
     * @return Http\Response|\Zend\Http\Response
     * @throws Exception
     */
    protected function executeQuery($processed_query, $type, $timeout=0)
    {
        $client = Http::getDefaultHttpClient();
        $client->resetParameters();
        // Tell the server which response formats we can parse
        $sparql_results_types = array(
            'application/sparql-results+json' => 1.0,
            'application/sparql-results+xml' => 0.8
        );
        if ($type == 'update') {
            // accept anything, as "response body of a […] update request is implementation defined"
            // @see http://www.w3.org/TR/sparql11-protocol/#update-success
            $accept = Format::getHttpAcceptHeader($sparql_results_types);
            $client->setHeaders('Accept', $accept);
            $client->setMethod('POST');
            $client->setUri($this->updateUri);
            $client->setRawData($processed_query);
            $client->setHeaders('Content-Type', 'application/sparql-update');
        } elseif ($type == 'query') {
            $re = '(?:(?:\s*BASE\s*<.*?>\s*)|(?:\s*PREFIX\s+.+:\s*<.*?>\s*))*'.
                '(CONSTRUCT|SELECT|ASK|DESCRIBE)[\W]';
            $result = null;
            $matched = mb_eregi($re, $processed_query, $result);
            if (false === $matched or count($result) !== 2) {
                // non-standard query. is this something non-standard?
                $query_verb = null;
            } else {
                $query_verb = strtoupper($result[1]);
            }
            if ($query_verb === 'SELECT' or $query_verb === 'ASK') {
                // only "results"
                $accept = Format::formatAcceptHeader($sparql_results_types);
            } elseif ($query_verb === 'CONSTRUCT' or $query_verb === 'DESCRIBE') {
                // only "graph"
                $accept = Format::getHttpAcceptHeader();
            } else {
                // both
                $accept = Format::getHttpAcceptHeader($sparql_results_types);
            }
            $client->setHeaders('Accept', $accept);
            $encodedQuery = 'query=' . urlencode($processed_query).(($timeout>0)?"&timeout=$timeout":"");
            // Use GET if the query is less than 2kB
            // 2046 = 2kB minus 1 for '?' and 1 for NULL-terminated string on server
            if (strlen($encodedQuery) + strlen($this->queryUri) <= 2046) {
                $delimiter = $this->queryUri_has_params ? '&' : '?';
                $client->setMethod('GET');
                $client->setUri($this->queryUri . $delimiter . $encodedQuery);
            } else {
                // Fall back to POST instead (which is un-cacheable)
                $client->setMethod('POST');
                $client->setUri($this->queryUri);
                $client->setRawData($encodedQuery);
                $client->setHeaders('Content-Type', 'application/x-www-form-urlencoded');
            }
        } else {
            throw new Exception('unexpected request-type: '.$type);
        }
        return $client->request();
    }


}
