<?

include("../lib/utils.inc.php");

header("Content-Type: text/html; charset=utf-8");

class Configuration
{
    var $server;
    var $channel;
    var $query;
    var $nick;
    var $limit;
    var $reverse;
    var $format;

    function __construct()
    {
        $this->server  = $this->paramValue("s", "freenode");
        $this->channel = $this->paramValue("c", "dingetje");
        $this->query   = $this->paramValue("q", ".*");
        $this->limit   = $this->paramValue("n", 10);
        $this->skip    = $this->paramValue("x", 0);
        $this->reverse = $this->paramValue("r", false);
        $this->case    = $this->paramValue("i", false);
        $this->format  = $this->paramValue("f", Formatter::HTML);

        if (strpos($this->channel, "#") === 0) {
            $this->channel = substr($this->channel, 1);
        }
    }

    private function paramValue($name, $default = null)
    {
        $val = _request($name);
        return isset($val) && strlen($val) > 0 ? urldecode($val) : $default;
    }

    function isValid()
    {
        return isset($this->server) && strlen($this->server) > 0
            && isset($this->channel) && strlen($this->channel) > 0
            && isset($this->query) && strlen($this->query) > 0
            && strpbrk($this->server, "./~") === false;
    }

    function getLogLocation($file = null)
    {
        $logPath = sprintf("/home/dracula/.irssi/logs/%s/", $this->server);
        if (!isset($file))
        {
            return $logPath;
        } else
        {
            return $logPath . $file;
        }
    }
}


class QueryResult
{
    public $num;
    public $matches;
    public $time;

    function __construct($matches, $time)
    {
        $this->matches = $matches;
        $this->num = count($matches);
        $this->time = $time;
    }
}


class QueryHandler
{
    private $matches;
    private $config;
    private static $oldEncoding;
    private static $currentFile;

    function __construct($config)
    {
        $this->config = $config;
    }

    private function retrieve_logfiles()
    {
        // Get ALL files from the log directory
        $files = scandir($this->config->getLogLocation(), $this->config->reverse ? SCANDIR_SORT_DESCENDING : SCANDIR_SORT_ASCENDING);
       
        // Return only the relevant files
        $pattern = sprintf("/^#%s\.\d*\.log$/i", $this->config->channel);
        return preg_grep($pattern, $files);
    }


    private function scan_logfile($file)
    {
        // Callback for array_map
        $prefix_name = function($item)
        {
            if (self::$oldEncoding)
            {
                $item = iconv("ISO-8859-15", "UTF-8", $item);
            }
            return self::$currentFile . ": " . $item;
        };

        // Hack: logfiles before 2012-07-29 are not UTF-8 encoded
        $date = explode(".", $file);
        self::$oldEncoding = ($date[1] < '20120729');
        self::$currentFile = $file;

        // Read all lines from file into array
        $lines = file($this->config->getLogLocation($file));

        if (!isset($lines)) {
            print "Failed to read: ". $file;
            return;
        }
    
        // Turn our query into a PCRE
        $pattern = sprintf("/%s/%s", str_replace("/", "\/", $this->config->query), $this->config->case ? "" : "i");
    
        // Filter lines
        $matches = preg_grep($pattern, $lines);

        // Use decreasing timestamps if we're doing reversed search
        if ($this->config->reverse)
        {
            $matches = array_reverse($matches);
        }

        // Prefix every line with the current filename before returning
        return array_map($prefix_name, $matches);
    }


    function process()
    {
        $startTime = getmicrotime();

        $files = $this->retrieve_logfiles();

        $this->matches = array();
    
        foreach($files as $file)
        {
            $matches = $this->scan_logfile($file);

            if ($this->append_matches($matches))
            {
                break;
            }
        }
        
        $this->matches = array_slice($this->matches, $this->config->skip, $this->config->limit);

        $processTime = getmicrotime() - $startTime;

        $result = new QueryResult($this->matches, $processTime);
        return $result;
    }

    private function append_matches($matches)
    {
        if (!empty ($matches))
        {
            $this->matches = array_merge($this->matches, $matches);
        }
        return count($this->matches) >= $this->config->limit + $this->config->skip;
    }
}


class Formatter
{
    const HTML = 0;
    const TEXT = 1;
    const PHP = 2;
    const JSON = 3;

    private $queryResult;

    function __construct($queryResult) 
    {
        $this->queryResult = $queryResult;
    }

    function Write($format)
    {
        switch($format)
        {
            case self::JSON:
                $this->WriteJSON();
                break;

            case self::TEXT:
                $this->WriteTEXT();
                break;

            case self::HTML:
                $this->WriteHTML();
                break;

            case self::PHP:
                $this->WritePHP();
                break;
        }

    }

    private function WriteJSON()
    {
        print json_encode($this->queryResult);
    }

    private function WriteTEXT()
    {
        foreach($this->queryResult->matches as $match)
        {
            print $match;
        }
    }

    private function WriteHTML()
    {
        print "<pre>";
        foreach($this->queryResult->matches as $key => $match)
        {
            print(sprintf("%3d %s", $key+1, htmlspecialchars($match)));
        }
        print "</pre>";
        print sprintf("<div style='color:#aaa'>Processing time: %ss</div>", round($this->queryResult->time, 3));
    }

    private function WritePHP()
    {
        print_r($this->queryResult);
    }
}



$config = new Configuration();
if (!$config->isValid())
{
    http_response_code(400);
    die("Missing or invalid parameters");
}


$handler = new QueryHandler($config);
$result = $handler->process();


$formatter = new Formatter($result);
$formatter->Write($config->format);

?>