<?php

/**
 *    Copyright (C) 2016 Deciso B.V.
 *
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 *    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 *    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 *    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *    POSSIBILITY OF SUCH DAMAGE.
 *
 */


namespace OPNsense\Diagnostics\Api;

use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Diagnostics\Netflow;
use \OPNsense\Core\Config;
use \OPNsense\Core\Backend;
use \Phalcon\Filter;

/**
 * Class NetworkinsightController
 * @package OPNsense\Netflow
 */
class NetworkinsightController extends ApiControllerBase
{
    /**
     * request timeserie data to use for reporting
     * @param string $provider provider class name
     * @param string $measure measure [octets, packets, octets_ps, packets_ps]
     * @param string $from_date from timestamp
     * @param string $to_date to timestamp
     * @param string $resolution resolution in seconds
     * @param string $field field name to aggregate
     * @return array timeseries
     */
    public function timeserieAction(
        $provider = null,
        $measure = null,
        $from_date = null,
        $to_date = null,
        $resolution = null,
        $field = null,
        $emulation = null
    ) {
        // cleanse input
        $filter = new Filter();
        $provider = $filter->sanitize($provider, "alphanum");
        $measure = $filter->sanitize($measure, "string");
        $from_date = $filter->sanitize($from_date, "int");
        $to_date = $filter->sanitize($to_date, "int");
        $resolution = $filter->sanitize($resolution, "int");
        $field = $filter->sanitize($field, "string");

        $result = array();
        if ($this->request->isGet()) {
            $backend = new Backend();
            // request current data
            $response = $backend->configdRun(
                "netflow aggregate fetch {$provider} {$from_date} {$to_date} {$resolution} {$field}"
            );
            // for test, request random data
            //$response = $backend->configdRun(
            //    "netflow aggregate fetch {$provider} {$from_date} {$to_date} {$resolution} {$field} " .
            //    "em0,in~em0,out~em1,in~em1,out~em2,in~em2,out~em3,in~em3,out"
            //);
            $graph_data = json_decode($response, true);
            if ($graph_data != null) {
                ksort($graph_data);
                $timeseries = array();
                foreach ($graph_data as $timeserie => $timeserie_data) {
                    foreach ($timeserie_data as $timeserie_key => $payload) {
                        if (!isset($timeseries[$timeserie_key])) {
                            $timeseries[$timeserie_key] = array();
                        }
                        // measure value
                        $measure_val = 0;
                        if ($measure == "octets") {
                            $measure_val = $payload['octets'];
                        } elseif ($measure == "packets") {
                            $measure_val = $payload['packets'];
                        } elseif ($measure == "octets_ps") {
                            $measure_val = $payload['octets'] / $payload['resolution'];
                        } elseif ($measure == "bps") {
                            $measure_val = ($payload['octets'] / $payload['resolution']) * 8;
                        } elseif ($measure == "packets_ps") {
                            $measure_val = $payload['packets'] / $payload['resolution'];
                        }
                        // add to timeseries
                        $timeseries[$timeserie_key][] = array((int)$timeserie*1000, $measure_val);
                    }
                }
                foreach ($timeseries as $timeserie_key => $data) {
                    $result[] = array("key" => $timeserie_key, "values" => $data);
                }
            }
        }
        return $result;
    }

    /**
     * request top usage data (for reporting), values can optionally be filtered using filter_field and filter_value
     * @param string $provider provider class name
     * @param string $from_date from timestamp
     * @param string $to_date to timestamp
     * @param string $field field name(s) to aggregate
     * @param string $measure measure [octets, packets]
     * @param string $max_hits maximum number of results
     * @return array timeseries
     */
    public function topAction(
        $provider = null,
        $from_date = null,
        $to_date = null,
        $field = null,
        $measure = null,
        $max_hits = null
    ) {
        // cleanse input
        $filter = new Filter();
        $provider = $filter->sanitize($provider, "alphanum");
        $from_date = $filter->sanitize($from_date, "int");
        $to_date = $filter->sanitize($to_date, "int");
        $field = $filter->sanitize($field, "string");
        $measure = $filter->sanitize($measure, "string");
        $max_hits = $filter->sanitize($max_hits, "int");

        $result = array();
        if ($this->request->isGet()) {
            if ($this->request->get("filter_field") != null && $this->request->get("filter_value") != null) {
                $filter_fields = explode(',', $this->request->get("filter_field"));
                $filter_values = explode(',', $this->request->get("filter_value"));
                $data_filter="";
                foreach ($filter_fields as $field_indx => $filter_field) {
                    if ($data_filter != '') {
                        $data_filter .= ',';
                    }
                    if (isset($filter_values[$field_indx])) {
                        $data_filter .= $filter_field.'='.$filter_values[$field_indx];
                    }
                }
                $data_filter = "'{$data_filter}'";
            } else {
                // no filter, empty parameter
                $data_filter = "''";
            }
            $backend = new Backend();
            $configd_cmd = "netflow aggregate top {$provider} {$from_date} {$to_date} {$field}";
            $configd_cmd .= " {$measure} {$data_filter} {$max_hits}";
            $response = $backend->configdRun($configd_cmd);
            $graph_data = json_decode($response, true);
            if ($graph_data != null) {
                return $graph_data;
            }
        }
        return array();
    }

    /**
     * get metadata from backend aggregation process
     * @return array timeseries
     */
    public function getMetadataAction()
    {
        if ($this->request->isGet()) {
            $backend = new Backend();
            $configd_cmd = "netflow aggregate metadata json";
            $response = $backend->configdRun($configd_cmd);
            $metadata = json_decode($response, true);
            if ($metadata != null) {
                return $metadata;
            }
        }
        return array();
    }

    /**
     * return interface map (device / name)
     * @return array interfaces
     */
    public function getInterfacesAction()
    {
        // map physical interfaces to description / name
        $configObj = Config::getInstance()->object();
        $allInterfaces = array();
        foreach ($configObj->interfaces->children() as $key => $intf) {
            $allInterfaces[(string)$intf->if] = empty($intf->descr) ? $key : (string)$intf->descr;
        }
        return $allInterfaces;
    }

    /**
     * return known protocols
     */
    public function getProtocolsAction()
    {
        $result = array();
        foreach (explode("\n", file_get_contents('/etc/protocols')) as $line) {
            if (strlen($line) > 1 && $line[0] != '#') {
                $parts = preg_split('/\s+/', $line);
                if (count($parts) >= 4) {
                    $result[$parts[1]] = $parts[0];
                }
            }
        }
        return $result;
    }

    /**
     * return known services
     */
    public function getServicesAction()
    {
        $result = array();
        foreach (explode("\n", file_get_contents('/etc/services')) as $line) {
            if (strlen($line) > 1 && $line[0] != '#') {
                // there a few ports which have different names for different protocols, but to not overcomplicate
                // things here, we ignore those exceptions.
                $parts = preg_split('/\s+/', $line);
                if (count($parts) >= 2) {
                    $portnum = explode('/', trim($parts[1]))[0];
                    $result[$portnum] = $parts[0];
                }
            }
        }
        return $result;
    }

    /**
     * request timeserie data to use for reporting
     * @param string $provider provider class name
     * @param string $from_date from timestamp
     * @param string $to_date to timestamp
     * @param string $resolution resolution in seconds
     * @return string csv output
     */
    public function exportAction(
        $provider = null,
        $from_date = null,
        $to_date = null,
        $resolution = null
    ) {
        $this->response->setContentType('application/CSV', 'UTF-8');
        $this->response->setHeader(
            'Content-Disposition:',
            "Attachment; filename=\"" . $provider . ".csv\""
        );
        if ($this->request->isGet()) {
            $backend = new Backend();
            $configd_cmd = "netflow aggregate export {$provider} {$from_date} {$to_date} {$resolution}";
            $response = $backend->configdRun($configd_cmd);
            return $response;
        } else {
            return "";
        }
    }
}