<?php

namespace Kicken\Chat;


class 
HTTPClient extends StreamSocketClient implements IHTTPRouter {
    private 
$mHeaders '';
    private 
$mRequestBody '';
    private 
$mGotHeaders false;
    private 
$mCloseAfterWrite false;

    public function 
__construct($resource){
        
parent::__construct($resource);
    }

    public function 
nowReadable(){
        
parent::nowReadable();

        
$this->dataAvailable();
    }

    public function 
dataAvailable(){
        if (!
$this->mGotHeaders){
            
$data $this->readAll();
            
$pos strpos($data"\r\n\r\n");
            if (
false !== $pos){
                
$this->mGotHeaders true;
                
$pos += 4;
                
$this->mHeaders .= substr($data0$pos);
                
$this->mRequestBody .= substr($data$pos);

                if (
$this->processHeaders()){
                    
$this->doRouting();
                }
            } else {
                
$this->mHeaders .= $data;
            }
        } else {
            
$this->mRequestBody .= $this->readAll();
            
$this->doRouting();
        }
    }

    private function 
doRouting(){
        try {
            
$this->route($this->mHeaders['REQUEST_URI']);
        } catch (\
Exception $e){
            
$this->sendResponse(500$e->getMessage());
        }
    }

    protected function 
processHeaders(){
        
$curHeader = [];
        
$allHeaders = [];
        
$headers rtrim($this->mHeaders);

        foreach (
explode("\r\n"$headers) as $l){
            if (
preg_match('#^([a-z]+)\s+(.*)\s+HTTP/1.[01]$#i'$l$matches)){
                if (
strpos($matches[2], '?') !== false){
                    list(
$uri$qs) = explode('?'$matches[2]);
                } else {
                    
$uri $matches[2];
                    
$qs '';
                }

                
$allHeaders[] = [
                    
'REQUEST_METHOD',
                    
$matches[1]
                ];
                
$allHeaders[] = [
                    
'REQUEST_URI',
                    
$uri
                
];
                
$allHeaders[] = [
                    
'QUERY_STRING',
                    
$qs
                
];
            } else if (
$l[0] == ' ' || $l[0] == "\t"){
                
$curHeader[1] .= ltrim($l);
            } else {
                if (
$curHeader){
                    
$allHeaders[] = $curHeader;
                }

                
$curHeader explode(':'$l2);
                
$curHeader[0] = rtrim($curHeader[0]);
                
$curHeader[1] = ltrim($curHeader[1]);
            }
        }
        if (
$curHeader){
            
$allHeaders[] = $curHeader;
        }

        
$this->mHeaders = [];
        foreach (
$allHeaders as $h){
            
$h[0] = strtoupper($h[0]);
            
$h[0] = str_replace('-''_'$h[0]);
            
$this->mHeaders[$h[0]] = $h[1];
        }

        return isset(
$this->mHeaders['REQUEST_METHOD']) && isset($this->mHeaders['REQUEST_URI']);
    }

    protected function 
getCodeText($code){
        switch (
$code){
            case 
'200':
                return 
'Ok';
            case 
'400':
                return 
'Bad Request';
            case 
'404':
                return 
'Not Found';
            case 
'403':
                return 
'Forbidden';
            case 
'405':
                return 
'Method not allowed';
            case 
'415':
                return 
'Unsupported media type';
            case 
'500':
                return 
'Internal Server Error';
        }

        return 
'Unknown';
    }

    protected function 
sendResponse($code$body null$headers = []){
        
$response sprintf("HTTP/1.0 %d %s\r\n"$code$this->getCodeText($code));
        
$defaultHeaders = [
            
'EXPIRES' => gmdate(DATE_RFC1123),
            
'PRAGMA' => 'no-cache',
            
'CACHE_CONTROL' => 'no-cache',
            
'LAST_MODIFIED' => gmdate(DATE_RFC1123),
            
'DATE' => gmdate(DATE_RFC1123),
            
'SERVER' => 'phpchatd',
            
'CONTENT_LENGTH' => strlen($body),
            
'CONTENT_TYPE' => 'text/html'
        
];

        
$headers array_merge($defaultHeaders$headers);

        foreach (
$headers as $h => $vlist){
            if (
$vlist !== null){
                
$h ucfirst(strtolower($h));
                
$h str_replace('_''-'$h);

                foreach ((array)
$vlist as $v){
                    
$response .= "{$h}{$v}\r\n";
                }
            }
        }

        
$response .= "\r\n";
        
$response .= $body;

        
$this->mCloseAfterWrite true;
        
$this->writeSocket($response);
    }

    public function 
writeSocket($data){
        
parent::writeSocket($data);
        if (
$this->mCloseAfterWrite && !$this->hasPendingWrites()){
            
$this->close();
        }
    }

    public function 
route($uri){
        
$this->sendResponse(404);
    }

    protected function 
getRequestHeaders(){
        return 
$this->mHeaders;
    }

    protected function 
getRequestBody(){
        return 
$this->mRequestBody;
    }

    protected function 
getQueryVars(){
        
parse_str($this->mHeaders['QUERY_STRING'], $GET);

        return 
$GET;
    }
}