<?php

namespace Kanboard\Core\Http;

use Kanboard\Core\Base;
use Kanboard\Core\Csv;

/**
 * Response class
 *
 * @package  http
 * @author   Frederic Guillot
 */
class Response extends Base
{
    /**
     * Send headers to cache a resource
     *
     * @access public
     * @param  integer $duration
     * @param  string  $etag
     */
    public function cache($duration, $etag = '')
    {
        header('Pragma: cache');
        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $duration) . ' GMT');
        header('Cache-Control: public, max-age=' . $duration);

        if ($etag) {
            header('ETag: "' . $etag . '"');
        }
    }

    /**
     * Send no cache headers
     *
     * @access public
     */
    public function nocache()
    {
        header('Pragma: no-cache');
        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');

        // Use no-store due to a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=28035
        header('Cache-Control: no-store, must-revalidate');
    }

    /**
     * Send a custom Content-Type header
     *
     * @access public
     * @param  string   $mimetype   Mime-type
     */
    public function contentType($mimetype)
    {
        header('Content-Type: '.$mimetype);
    }

    /**
     * Force the browser to download an attachment
     *
     * @access public
     * @param  string   $filename    File name
     */
    public function forceDownload($filename)
    {
        header('Content-Disposition: attachment; filename="'.$filename.'"');
        header('Content-Transfer-Encoding: binary');
        header('Content-Type: application/octet-stream');
    }

    /**
     * Send a custom HTTP status code
     *
     * @access public
     * @param  integer   $status_code   HTTP status code
     */
    public function status($status_code)
    {
        header('Status: '.$status_code);
        header($this->request->getServerVariable('SERVER_PROTOCOL').' '.$status_code);
    }

    /**
     * Redirect to another URL
     *
     * @access public
     * @param  string   $url   Redirection URL
     * @param  boolean  $self  If Ajax request and true: refresh the current page
     */
    public function redirect($url, $self = false)
    {
        if ($this->request->isAjax()) {
            header('X-Ajax-Redirect: '.($self ? 'self' : $url));
        } else {
            header('Location: '.$url);
        }

        exit;
    }

    /**
     * Send a CSV response
     *
     * @access public
     * @param  array    $data          Data to serialize in csv
     * @param  integer  $status_code   HTTP status code
     */
    public function csv(array $data, $status_code = 200)
    {
        $this->status($status_code);
        $this->nocache();

        header('Content-Type: text/csv');
        Csv::output($data);
        exit;
    }

    /**
     * Send a Json response
     *
     * @access public
     * @param  array    $data          Data to serialize in json
     * @param  integer  $status_code   HTTP status code
     */
    public function json(array $data, $status_code = 200)
    {
        $this->status($status_code);
        $this->nocache();
        header('Content-Type: application/json');
        echo json_encode($data);
        exit;
    }

    /**
     * Send a text response
     *
     * @access public
     * @param  string   $data          Raw data
     * @param  integer  $status_code   HTTP status code
     */
    public function text($data, $status_code = 200)
    {
        $this->status($status_code);
        $this->nocache();
        header('Content-Type: text/plain; charset=utf-8');
        echo $data;
        exit;
    }

    /**
     * Send a HTML response
     *
     * @access public
     * @param  string   $data          Raw data
     * @param  integer  $status_code   HTTP status code
     */
    public function html($data, $status_code = 200)
    {
        $this->status($status_code);
        $this->nocache();
        header('Content-Type: text/html; charset=utf-8');
        echo $data;
        exit;
    }

    /**
     * Send a XML response
     *
     * @access public
     * @param  string   $data          Raw data
     * @param  integer  $status_code   HTTP status code
     */
    public function xml($data, $status_code = 200)
    {
        $this->status($status_code);
        $this->nocache();
        header('Content-Type: text/xml; charset=utf-8');
        echo $data;
        exit;
    }

    /**
     * Send a javascript response
     *
     * @access public
     * @param  string   $data          Raw data
     * @param  integer  $status_code   HTTP status code
     */
    public function js($data, $status_code = 200)
    {
        $this->status($status_code);

        header('Content-Type: text/javascript; charset=utf-8');
        echo $data;

        exit;
    }

    /**
     * Send a css response
     *
     * @access public
     * @param  string   $data          Raw data
     * @param  integer  $status_code   HTTP status code
     */
    public function css($data, $status_code = 200)
    {
        $this->status($status_code);

        header('Content-Type: text/css; charset=utf-8');
        echo $data;

        exit;
    }

    /**
     * Send a binary response
     *
     * @access public
     * @param  string   $data          Raw data
     * @param  integer  $status_code   HTTP status code
     */
    public function binary($data, $status_code = 200)
    {
        $this->status($status_code);
        $this->nocache();
        header('Content-Transfer-Encoding: binary');
        header('Content-Type: application/octet-stream');
        echo $data;
        exit;
    }

    /**
     * Send the security header: Content-Security-Policy
     *
     * @access public
     * @param  array    $policies   CSP rules
     */
    public function csp(array $policies = array())
    {
        $values = '';

        foreach ($policies as $policy => $acl) {
            $values .= $policy.' '.trim($acl).'; ';
        }

        header('Content-Security-Policy: '.$values);
    }

    /**
     * Send the security header: X-Content-Type-Options
     *
     * @access public
     */
    public function nosniff()
    {
        header('X-Content-Type-Options: nosniff');
    }

    /**
     * Send the security header: X-XSS-Protection
     *
     * @access public
     */
    public function xss()
    {
        header('X-XSS-Protection: 1; mode=block');
    }

    /**
     * Send the security header: Strict-Transport-Security (only if we use HTTPS)
     *
     * @access public
     */
    public function hsts()
    {
        if ($this->request->isHTTPS()) {
            header('Strict-Transport-Security: max-age=31536000');
        }
    }

    /**
     * Send the security header: X-Frame-Options (deny by default)
     *
     * @access public
     * @param  string   $mode   Frame option mode
     * @param  array    $urls   Allowed urls for the given mode
     */
    public function xframe($mode = 'DENY', array $urls = array())
    {
        header('X-Frame-Options: '.$mode.' '.implode(' ', $urls));
    }
}