<?php

if(!defined('ALC_API')) die('-1');
define('WP_USE_THEMES', false);
// will be included from /vX/ (one more directory nested)
require '../../../../../wp-blog-header.php';
if(!defined('ABSPATH')) die('-1');

if(class_exists('AwesomeLiveChat_HTTP')) die('-1');

require_once dirname(__FILE__) . '/http.php';

if(class_exists('AwesomeLiveChat_API')) die('-1');

if(!class_exists('AwesomeLiveChat')) die('-1');

if(!class_exists('AwesomeLiveChat_Library')) die('-1');

class AwesomeLiveChat_API extends AwesomeLiveChat
{
    private $Boots;
    private $Options;
    protected $_;
    protected $Code;
    protected $timeout;

    protected $username;
    protected $password;

    protected $auth_token;
    protected $auth_id;

    protected $token;
    protected $id;

    private $type;
    private $method;

    // GET
    protected $Get = array();

    // POST
    protected $Post = array();

    // BODY
    protected $Body = array();

    // ?r=/method/p1/p2...
    // method
    private $action = '';
    // p1/p2
    private $Args = array();

    private $auth = true;

    public function __construct($type = 'json', $auth = true)
    {
        $this->Boots    = $this->boots();
        $this->Options  = $this->options();
        $this->_        = new AwesomeLiveChat_Library();
        $this->HTTP     = new AwesomeLiveChat_HTTP();
        $this->type     = $type;
        $this->auth     = $auth;
        $this->timeout = $this->_->settings('general', 'timeout');
        $this->timeout = $this->timeout
        ? ($this->timeout > 100 ? 100 : $this->timeout) : 25;

        $this->method = $_SERVER['REQUEST_METHOD'];

        $this->Get = array_map('sanitize_text_field', $_GET);

        $this->Post = array_map('sanitize_text_field', $_POST);

        $Body = @json_decode(file_get_contents('php://input'));
        $this->Body = $Body
            ? array_map('sanitize_text_field', (array) $Body)
            : array();

        $this->username = isset($_SERVER['PHP_AUTH_USER'])
            ? $_SERVER['PHP_AUTH_USER']
            : $this->post('username', $this->body('username', null));

        $this->password = isset($_SERVER['PHP_AUTH_PW'])
            ? $_SERVER['PHP_AUTH_PW']
            : $this->post('password', $this->body('password', null));

        $request = $this->get('r', '');
        if($request)
        {
            preg_match('/\.(.+)$/', $request, $Type);
            if(count($Type))
            {
                if(isset($Type[1]))
                {
                    $type = strtolower($Type[1]);
                    if(in_array($type, array('json', 'xml')))
                    {
                        $this->type = $type;
                    }
                }
                $request = str_replace($Type[0], '', $request);
            }
            $this->Args = explode('/', $request);
            $this->action = strtolower(array_shift($this->Args));
        }

        $this->auth_token = $this->get('token');

        $this->token = $this->post('token')
            ? $this->post('token')
            : $this->body('token');

        $this->init();
    }

    // http://darklaunch.com/2009/05/23/php-xml-encode-using-domdocument-convert-array-to-xml-json-encode
    private function xmlEncode($mixed, $domElement=null, $DOMDocument=null)
    {
        if (is_null($DOMDocument)) {
            $DOMDocument = new DOMDocument('1.0', 'iso-8859-1');
            $DOMDocument->formatOutput = true;
            $this->xmlEncode($mixed, $DOMDocument, $DOMDocument);
            return $DOMDocument->saveXML();
        }
        else {
            if (is_array($mixed)) {
                foreach ($mixed as $index => $mixedElement) {
                    if (is_int($index)) {
                        if ($index === 0) {
                            $node = $domElement;
                        }
                        else {
                            $node = $DOMDocument->createElement($domElement->tagName);
                            $domElement->parentNode->appendChild($node);
                        }
                    }
                    else {
                        $plural = $DOMDocument->createElement($index);
                        $domElement->appendChild($plural);
                        $node = $plural;
                        if (!(rtrim($index, 's') === $index)) {
                            $singular = $DOMDocument->createElement(rtrim($index, 's'));
                            $plural->appendChild($singular);
                            $node = $singular;
                        }
                    }

                    $this->xmlEncode($mixedElement, $node, $DOMDocument);
                }
            }
            else {
                $mixed = is_bool($mixed) ? ($mixed ? 'true' : 'false') : $mixed;
                $domElement->appendChild($DOMDocument->createTextNode($mixed));
            }
        }
    }

    protected function response($Data, $status = 404)
    {
        $status_text = $this->HTTP->status($status);
        $status_protocol = $_SERVER['SERVER_PROTOCOL'];
        header($status_protocol . ' ' . $status . ' ' . $status_text);
        if($status < 200 || $status >= 300 && $status != 304)
        {
            if(!is_array($Data))
                $Data = array('reason' => $Data);
            $Data['code']  = $status;
            $Data['error'] = $status_text;
            ksort($Data);
        }
        $header = 'Content-Type: ';
        $Response = null;
        switch($this->type)
        {
            case 'xml':
                $header .= 'application/xml';
                $Response = $this->xmlEncode(array('response'=>$Data));
            break;
            default:
                $header .= 'application/json';
                $Response = json_encode($Data);
            break;
        }
        header($header . '; charset='
            . $this->Boots->Database
            ->term('blog_charset')->get()
        );
        die($Response);
    }

    protected function get($term, $default = null)
    {
        return isset($this->Get[$term])
            ? $this->Get[$term]
            : $default;
    }

    protected function post($term, $default = null)
    {
        return isset($this->Post[$term])
            ? $this->Post[$term]
            : $default;
    }

    protected function body($term, $default = null)
    {
        return isset($this->Body[$term])
            ? $this->Body[$term]
            : $default;
    }

    private function init()
    {
        $method = strtolower($this->method . '_' . $this->action);
        if($method === 'get_pulse')
            $this->response(
                array('version' => ALC_API),
                $this->HTTP->HTTP_OK
            );
        if($this->auth) $this->authenticate();
        if(method_exists($this, $method))
        {
            $Reflection = new ReflectionMethod($this, $method);
            if(!$Reflection->isPublic())
                $this->response('Does not exist');
            if(count($this->Args) < $Reflection->getNumberOfRequiredParameters())
                $this->response(
                    'Not enough arguments',
                    $this->HTTP->HTTP_NOT_IMPLEMENTED
                );
            if(count($this->Args) > $Reflection->getNumberOfParameters())
                $this->response(
                    'Too many arguments',
                    $this->HTTP->HTTP_NOT_IMPLEMENTED
                );
            call_user_func_array(array($this, $method), $this->Args);
        }
        else $this->response('Does not exist');
    }
}
