<?php
include_once "db.php";
include_once "QueueSystem.class.php";
include_once "Templates.class.php";
include_once "UrlGenerator.class.php";
include_once "utils.php";

header("Content-Type: text/plain");

function tmp_file($id) {
    return "/tmp/mq/tmp/$id";
}

function data_file($id) {
    return "/tmp/mq/data/$id";
}

function log_file($id) {
    return "/tmp/mq/data/" . $id . ".log";
}

$method = $_SERVER["REQUEST_METHOD"];
$content_type = isset($_SERVER["CONTENT_TYPE"]) ? $_SERVER["CONTENT_TYPE"] : NULL;
$system = new QueueSystem();
$db = $system->db;
$form = array();

if(isset($content_type) && $content_type == "application/x-www-form-urlencoded") {
    $body = file_get_contents("php://input");
    $x = urldecode($body);
    $parts = split("&", $x);
    foreach($parts as $part) {
        list($key, $value) = split("=", $part);
        $form[$key] = $value;
    }
//    var_dump($form);
}

$port = $_SERVER["SERVER_PORT"];
$port = $port == "80" ? "" : ":" . $port;
$baseurl = "http://" . $_SERVER["SERVER_NAME"] . $port . $_SERVER["SCRIPT_NAME"];
$url_generator = new UrlGenerator($baseurl);
if(!isset($_SERVER["PATH_INFO"])) {
    allow_methods("HEAD", "GET");
    status($url_generator);
}
else {
    $path_info = $_SERVER["PATH_INFO"];
    $path_info = preg_replace('/[\/]$/', '', $path_info);
    $path_info = preg_replace('/^[\/]/', '', $path_info);
    $segments = explode("/", $path_info);
//    var_dump($segments);
    switch($segments[0]) {
        case "enqueue":
            allow_methods("POST");
            if(count($segments) != 2) {
                not_found();
            }
            enqueue($url_generator, $segments[1]);
            break;
        case "queue":
            switch(count($segments)) {
                case 1:
                    queues($url_generator);
                    break;
                case 2:
                    queue($url_generator, $segments[1]);
                    break;
                case 3:
                    if($segments[2] == "items") {
                        items($url_generator, $segments[1]);
                    } else {
                        not_found();
                    }
                    break;
                case 4:
                    if($segments[2] == "items") {
                        item($url_generator, $segments[1], $segments[3]);
                    } else {
                        not_found();
                    }
                    break;
                case 5:
                    allow_methods("HEAD", "GET");
                    if($segments[2] == "items") {
                        switch($segments[4]) {
                            case "blob":
                                item_blob($url_generator, $segments[1], $segments[3], FALSE);
                                break;
                            case "blob-as-text":
                                item_blob($url_generator, $segments[1], $segments[3], TRUE);
                                break;
                            default:
                                not_found();
                        }
                    } else {
                        not_found();
                    }
                    break;
                default:
                    not_found();
            }
            break;
        case "cron":
            cron();
            break;
        case "html":
            html($url_generator, array_slice($segments, 1));
            break;
        default:
            not_found();
            break;
    }
}

function html($url_generator, $segments) {
    global $system;

    switch($segments[0]) {
        case "create":
            allow_methods("POST");
            $name = $_POST["name"];
            $endpoint_url = $_POST["endpoint_url"];
            $id = $system->create_queue($name, $endpoint);
            header("HTTP/1.1 301 Created as " . $id);
            header("Location: " . $url_generator->queue($id));
            break;
        default:
            not_found();
            break;
    }
}

$db->commit();
$db = null;

function allow_methods($methods) {
    global $method;
    $methods = func_get_args();
    if(array_search($method, $methods) === FALSE) {
        header("HTTP/1.1 405 Invalid method");
        header("Allow: " . implode(", ", $methods));
        exit(0);
    }
}

function unsupported_media_type() {
    header("HTTP/1.1 415 Unsupported Media Type");
    exit(0);
}

function conflict($short) {
    header("HTTP/1.1 409 " . $short);
    exit(0);
}

function not_found($short = NULL, $long = NULL) {
    if(!isset($short)) {
        $short = "Not found";
    }
    header("HTTP/1.1 404 " . $short);
    if(isset($long)) {
        echo($long);
    } else {
        echo("404 Not found :(\n");
    }
    exit(0);
}

function bad_request($short = NULL, $long = NULL) {
    if(!isset($short)) {
        $short = "Bad Request";
    }
    header("HTTP/1.1 400 " . $short);
    if(isset($long)) {
        echo($long);
    }
    exit(0);
}

function conneg() {
    $accept = isset($_SERVER["HTTP_ACCEPT"]) ? $_SERVER["HTTP_ACCEPT"] : NULL;
    // First media type
    $accept = preg_replace('/^([^,]*),.*/', '$1', $accept);
//    var_dump($accept);
    // Remove parameters
    $accept = preg_replace('/^([^;]*);.*/', '$1', $accept);
//    var_dump($accept);
    return $accept;
}

function cron() {
    global $db;

    allow_methods("POST");

    $queues = $db->select_queues();
    foreach($queues as $queue) {
        if(!isset($queue->endpoint_url)) {
            echo("No endpoint configured for queue " . $queue->name . "\n");
            continue;
        }
        $items = $db->select_items($queue->name, 1, 0);
        $count = count($items);

        if($count > 0) {
            $item = $items[0];
            process_item($queue, $items[0]);
            break;
        }

        echo("Queue " . $queue->name . " is empty.\n");
    }
}

function process_item($queue, $item) {
    echo("Processing item " . $item->id . " from queue " . $queue->name . "\n");
    $input_path = data_file($item->id);
    $input_fd = fopen($input_path, "r");

    $log_path = log_file($item->id);
    $log_fd = fopen($log_path, "w");

    if(!$input_fd || !$log_fd) {
        echo("Error opening " . $path . "\n");
        fclose($input_fd);
        fclose($log_fd);
        return;
    }
    $handle = curl_init();

    // http://no2.php.net/manual/en/function.curl-setopt.php

    // Set referer on redirecet
    curl_setopt($handle, CURLOPT_AUTOREFERER, TRUE);
    // Follow redirects
    curl_setopt($handle, CURLOPT_FOLLOWLOCATION, TRUE);
    // We're doing POSTs only.
    curl_setopt($handle, CURLOPT_POST, TRUE);
    curl_setopt($handle, CURLOPT_UPLOAD, TRUE);
    curl_setopt($handle, CURLOPT_MAXREDIRS, 10);

    curl_setopt($handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
    // This should be 2 in production
    curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, 1);

    // Return the transfer as a string of the return value of curl_exec()
//    curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);

    // In seconds
    curl_setopt($handle, CURLOPT_TIMEOUT, 30);

    curl_setopt($handle, CURLOPT_URL, $queue->endpoint_url);
    curl_setopt($handle, CURLOPT_HTTPHEADER, $item->headers);
    curl_setopt($handle, CURLOPT_INFILE, $input_fd);

    curl_setopt($handle, CURLOPT_WRITEHEADER, $log_fd);
    curl_setopt($handle, CURLOPT_FILE, $log_fd);
    curl_setopt($handle, CURLOPT_STDERR, $log_fd);

    curl_setopt($handle, CURLOPT_CUSTOMREQUEST, "POST");

    echo("Executing request...\n");
    fwrite($log_fd, "POST " . $queue->endpoint_url . "\n");
    $ret = curl_exec($handle);

    if($ret === false) {
        echo("curl failed\n");
        echo("error: " . curl_error($handle) . "\n");
    }
    else {
        echo("curl success\n");
    }
    fclose($input_fd);
    fclose($log_fd);

    curl_close($handle);
}

function enqueue($url_generator, $queue) {
    global $db;

    $id = uniqid();
    $tmp_file = tmp_file($id);
    $data_file = data_file($id);

    try {
        echo("tmp file: $tmp_file\n");
        $stdin = fopen('php://input', 'r');
        $size = file_put_contents($tmp_file, $stdin);
        fclose($stdin);
//        $size = file_put_contents($tmp_file, file_get_contents("php://stdin"));
//        if($size === FALSE) {
//            die("Unable to write tmp file.");
//        }
//        $s = stat($tmp_file) or die("Unable to stat tmp file.");
//        $size = $s["size"];
        link($tmp_file, $data_file) or die("Unable to copy tmp file to data file.");
        unlink($tmp_file) or die("Unable to delete temporary file.");

        $headers = array(); 
        foreach($_SERVER as $key => $value) {
            if(strpos($key, "HTTP_") === 0) {
                $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
                $headers[$key] = $value;
            }
        }
        $headers["Content-Type"] = $_SERVER["CONTENT_TYPE"];
        $headers["Content-Length"] = $size;
        var_dump($headers);

        $db->insert_item($id, $headers, array($queue));

        header("HTTP/1.1 201 Created as " . $id);
        header("Location: " . $url_generator->item($queue, $id));
        header("Item-Size: " . $size);
    } catch (Exception $e) {
        header("HTTP/1.1 500 Error");
        echo("Caught exception: " .  $e->getMessage() . "\n");
    }
}

function status(UrlGenerator $url_generator) {
    header("HTTP/1.1 200 OK");
    header("Content-Type: text/html");
    template_status($url_generator);
}

function item($url_generator, $queue, $id) {
    global $db, $method;

    $item = $db->select_item($queue, $id);
    if($item == NULL) {
        not_found("No such item: " . $id);
    }

    switch($method) {
        case "HEAD":
        case "GET":
            header("Link: <" . $url_generator->item_blob($queue, $item->id) . ">;rel=enclosure");
            if($method == "HEAD") {
                break;
            }
            echo("Showing: " . $item->id . "\n");
            echo($item->toPlainText($url_generator));
            break;
        case "DELETE":
            $db->delete_item($queue, $id);
            @unlink(data_file($id));
            @unlink(log_file($id));
            header("HTTP/1.1 204 Deleted item " . $id);
            break;
        default:
            allow_methods("HEAD", "GET", "DELETE");
            break;
    }
}

function item_blob($url_generator, $queue, $id, $as_text) {
    global $db, $method;

    $item = $db->select_item($queue, $id);
    if($item == NULL) {
        not_found("No such item: " . $id);
    }

    if($as_text) {
        header("Content-Type: text/plain");
    } else {
        $content_type = $item->headers["Content-Type"];
        header("Content-Type: $content_type");
    }
    // TODO: set Content-Length
    readfile(data_file($id));
}

function items($url_generator, $queue) {
    global $db;

    allow_methods("GET");

    header("HTTP/1.1 200 Items in $queue");
    $items = $db->select_items($queue);
    switch(conneg()) {
        default:
        case "text/plain":
            header("Content-Type: text/plain");
            template_item_list_text($url_generator, $queue, $items);
            break;
        case "text/html":
            header("Content-Type: text/html");
            template_item_list_html($url_generator, $queue, $items);
            break;
    }
}

function queues($url_generator) {
    global $method, $content_type, $db;

    switch($method) {
        case "GET":
        case "HEAD":
            $queues = $db->select_queues();
            $count = count($queues);

            switch(conneg()) {
                default:
                case "text/plain":
                    header("Content-Type: text/plain");
                    template_queue_list_text($url_generator, $queues);
                    break;
                case "text/html":
                    header("Content-Type: text/html");
                    template_queue_list_html($url_generator, $queues);
                    break;
            }
            break;
        case "POST":
            $name = NULL;
            $endpoint_url = NULL;
            switch($content_type) {
                case "application/x-www-form-urlencoded":
                    $endpoint_url = $_POST["endpoint_url"];
                    $system->create_queue(NULL, $endpoint_url);
                    header("HTTP/1.1 201 Queue created as " . $name);
                    header("Location: " . $url_generator->queue($name));
                    break;
                default:
                    unsupported_media_type();
            }
            break;
        default:
            allow_methods("GET", "HEAD", "POST");
    }
}

function queue($url_generator, $queue) {
    global $method, $content_type, $form, $db;

    if(!Queue::valid_name($queue)) {
        not_found("Invalid queue name");
    }

    switch($method) {
        case "GET":
        case "HEAD":
            $q = $db->select_queue($queue);
            if(!isset($q)) {
                not_found("No such queue: " . $queue);
            }

            switch(conneg()) {
                case "text/html";
                    header("Content-Type: text/html");
                    template_queue_html($url_generator, $q);
                    break;
                default:
                case "text/plain";
                    header("Content-Type: text/plain");
                    template_queue_text($url_generator, $q);
                    break;
            }
            break;
        case "PUT":
            $q = $db->select_queue($queue);
            if($q != NULL) {
                conflict("Queue already exist: " . $queue);
            }
            switch($content_type) {
                case "application/x-www-form-urlencoded":
                    $name = $queue;
                    $endpoint_url = $_POST["endpoint_url"];
                    $system->create_queue($name, $endpoint_url);
                    header("HTTP/1.1 201 Queue created as " . $name);
                    header("Location: " . $url_generator->queue($name));
                    break;
                default:
                    unsupported_media_type();
            }
            break;
        case "PATCH":
            $q = $db->select_queue($queue);
            if($q == NULL) {
                not_found("No such queue: " . $queue);
            }
            switch($content_type) {
                case "application/x-www-form-urlencoded":
                    var_dump($form);
                    $endpoint_url = $form["endpoint_url"];
                    if(!isset($endpoint_url)) {
                        header("HTTP/1.1 400 Bad request; missing endpoint_url");
                        exit(0);
                    }
                    else {
                        $db->update_queue($queue, $endpoint_url);
                        header("HTTP/1.1 204 Queue updated " . $queue);
                        header("Content-Location: " . $url_generator->queue($queue));
                    }
                    break;
                default:
                    unsupported_media_type();
            }
            break;
        case "DELETE":
            $db->delete_queue_and_items($queue);
            break;
        default:
            allow_methods("GET", "HEAD", "PUT", "PATCH", "DELETE");
    }
}
?>
