<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

error_reporting(E_ALL);
ini_set('display_errors', true);

class PodcastController extends Controller
{
    public static $BASE_SQL = <<<QUERY
SELECT `podcast`.`id`, `podcast`.`soundfilename`, `podcast`.`duration`,
       `podcast_meta`.`creationdt` AS `created`, `podcast_meta`.`title`, `podcast_meta`.`content`, `podcast_meta`.`program`,
       `podcast_meta`.`imagefile`, `podcast_meta`.`imagecaption`,
       `programs`.`longname` AS `program_name`, `programs`.`description` AS `program_description`
    FROM `podcast` 
      INNER JOIN `podcast_meta` ON `podcast`.`id` = `podcast_meta`.`podcast`
      LEFT JOIN `programs` ON `podcast_meta`.`program` = `programs`.`id`
    WHERE `podcast_meta`.`active` = 1 AND `podcast_meta`.`public` = 1
QUERY;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
    * Lijst van alle podcasts
    */
    public function overview(Request $request) {
        return $this->getPodcastList($request, null, []);
    }

    /**
    * RSS-feed van alle podcasts
    */
    public function rss(Request $request) {
        $podcasts = $this->retrievePodcasts($page = 1, $count = 20, $filter = null, $params = []);
        $view = view('rss.podcasts')->with('podcasts', $podcasts);
        return response($view)->header('Content-Type', 'application/xml');
    }

    /**
    * Lijst van alle podcasts voor een specifiek programma
    */
    public function program(Request $request, $program) {
        if((int)$program <= 0) { 
          return abort(404);
        }
  
        $filter = '`podcast_meta`.`program` = :program';
        $params = ['program' => (int)$program];
        if(($date = $request->get('date')) != null) 
        {
          $filter .= ' AND DATE(`podcast_meta`.`creationdt`) = :date';
          $params['date'] = $date;
        }

        return $this->getPodcastList($request, $filter, $params);
    }

    /**
    * Details over een specifieke podcast
    */
    public function details(Request $request, $id) {
        $podcasts = app('db')->select(self::$BASE_SQL . ' AND `podcast`.`id` = :podcast', ['podcast' => (int)$id]);
        if(count($podcasts) != 1) {
          return abort(404);
        }

        return response()->json(new \Model\Podcast($podcasts[0]));
    }
  
    public function findpodcast(Request $request, $query) {
	$page = (int)$request->get('pagina', 1);
        if($page <= 0) {
          return abort(400);
        }
  
        $query = strip_tags(urldecode($query));
        $searchRange = [
            (new \DateTimeImmutable())->setDate(date('Y') + 1 - $page, 1, 1),
            (new \DateTimeImmutable())->setDate(date('Y') + 2 - $page, 1, 1)];
 
        $podcasts = app('db')->select(self::$BASE_SQL
            . ' AND `podcast_meta`.`creationdt` >= :startRange AND `podcast_meta`.`creationdt` <= :endRange'
            . ' AND MATCH(`podcast_meta`.`title`, `podcast_meta`.`content`) AGAINST(:query IN BOOLEAN MODE)'
            . ' ORDER BY `creationdt` DESC'
            . ' LIMIT 0, 20', [
                'startRange' => $searchRange[0]->format('Y-m-d'),
                'endRange' => $searchRange[1]->format('Y-m-d'),
                'query' => $query
            ]);

        $result = array();
        foreach($podcasts as $podcast) {
          $model = new \Model\Podcast($podcast);
          $model->title = \Helpers::HighlightQuery($query, $model->title);
          $model->content = \Helpers::HighlightQuery($query, $model->content);
          $result[] = $model;
        }

      return response()->json(['page' => $page, 'podcasts' => $result]);
    }

    private function getPodcastList(Request $request, $filter, $params)
    {
        $count = (int)$request->get('aantal', 15);
        $page = (int)$request->get('pagina', 1);
        if($count <= 0 || $page <= 0) {
          return abort(400);
        } 

        $result = $this->retrievePodcasts($page, $count, $filter, $params);
        return response()->json(['page' => $page, 'count' => $count, 'podcasts' => $result]);
     }

     private function retrievePodcasts($page, $count, $filter, $params) 
     {
        $start = ($page - 1) * $count;
        $podcasts = app('db')->select(self::$BASE_SQL 
            . ($filter ? ' AND (' . $filter . ')' : '')
            . ' ORDER BY `podcast_meta`.`creationdt` DESC, `podcast_meta`.`id` DESC'
            . ' LIMIT ' . (int)$start . ', ' . (int)$count,
            $params);

      $result = array();
      foreach($podcasts as $podcast) {
        $result[] = new \Model\Podcast($podcast);
      }
 
      return $result;
    }

    /**
    * Download specifieke podcast 
    */
    public function download(Request $request, $id) {
      $queryResult = app('db')->select(self::$BASE_SQL
          . ' AND `podcast`.`id` = :id '
          . ' LIMIT 0, 1',
          ['id' => (int)$id]);

      if(count($queryResult) == 0) { return abort(404); }

      $podcast = new \Model\Podcast($queryResult[0]);
      if(!$podcast->isValidAuth($request->get('auth'))) {
        return abort(404);
      }

      return response()->download($podcast->getSoundfile(),
          'NH Gooi Gemist - ' . str_replace('/', '-', $podcast->title) . '.mp3');
    }

    /***
    * Stream een specifieke podcast (niet voor uitzending)
    */
    public function stream(Request $request, $id) {
      $queryResult = app('db')->select(self::$BASE_SQL
          . ' AND `podcast`.`id` = :id '
          . ' LIMIT 0, 1',
          ['id' => $id]);

      if(count($queryResult) == 0) { return abort(404); }

      $podcast = new \Model\Podcast($queryResult[0]);
      if(!$podcast->isValidAuth($request->get('auth'))) {
        return abort(404);
      }

      $filename = $podcast->getSoundfile();
      $file = fopen($filename, "rb");
      $size = filesize($filename);
      $content = fread($file, $size);
      fclose($file);

      self::streamFile($request, 'audio/mpeg', $content, $size);
    }

    /***
    * Regionieuws
    */
    public function latestNews(Request $request) { 
      $filename = '/var/audio/regionieuws.mp3';
      return response()->download($filename,
          'NH Gooi Regionieuws ' . date('d-m-Y') . '.mp3');

      $size = filesize($filename);
      $content = fread($file, $size);
      fclose($file); 

      self::streamFile($request, 'audio/mpeg', $content, $size);
    }

    /***
    * Stream een specifieke uitzending
    */
    public function complete(Request $r, $year, $month, $day, $hour, $duration) {
        if($duration <= 0) { 
          return abort(404);
        }

        $currentHour = new \DateTime();
        $currentHour->setDate($year, $month, $day)->setTime($hour, 0, 0);
        $files = [];
        $size = 0;
        while($duration--) {
          $file = '/var/audio/uitzending/' . $currentHour->format('Ymd_Hi') . '.MP3';
          if(!file_exists($file)) {
            return abort(404);
          }

          $size += ($filesize = filesize($file));
          $files[] = [$file, $filesize];
          $currentHour->add(\DateInterval::createFromDateString('1 hour'));
        }

        $headers = [
            'Pragma' => 'public',
            'Expires' => '-1',
            'Cache-Control' => 'public,must-revalidate,post-check=0,pre-check=0',
            'Content-Type' => 'audio/mpeg',
            'Content-Length' => $size,
            'Accept-Ranges' => 'bytes'
        ];

        $range = [];
        if(isset($_SERVER['HTTP_RANGE'])) {
          list($unit, $rangestr) = explode('=', $_SERVER['HTTP_RANGE'], 2);
          if($unit == "bytes") {
            $range = explode('-', $rangestr);
            $range[1] = (empty($range[1])) ? ($size - 1) : min(abs(intval($range[1])), ($size - 1));
            $range[0] = (empty($range[0]) || $range[1] < abs(intval($range[0]))) ? 0 : max(abs(intval($range[0])), 0);
            $range[2] = ($range[1] - $range[0] + 1);

            $headers['Content-Range'] = 'bytes ' . $range[0] . '-' . $range[1] . '/' . $size;
            $headers['Content-Length'] = $range[2];
          }
        }

        if($range === null) {
          abort(416); // Requested Range Not Satisfiable
          return;
        }

        return new StreamedResponse(function() use($files, $range) {
            $output = fopen('php://output', 'w');
            foreach($files as $file) {
              if(count($range) && $range[0] >= $file[1]) {
                // We have a range and can skip the whole file
                continue;
              }

              $fp = fopen($file[0], 'rb');
              if(count($range) && $range[0]) {
                fseek($fp, $range[0]);
                $range[0] = 0;
              }

              $maxToRead = count($range) ? min(1024, $range[2]) : 1024;
              while ($buf = fread($fp, $maxToRead)) {
                fwrite($output, $buf);
                if(count($range)) {
                  $maxToRead = min(1024, $range[2]);
                  $bufSize = mb_strlen($buf, '8bit');
                  if(($range[2] -= $bufSize) == 0) {
                   break;
                  }
                }
              }

              fclose($fp);

              if(count($range) && $range[2] == 0) {
                break;
              }
            }

            fclose($output);
        }, count($range) ? 206 : 200, $headers);
    }

    // Provide a streaming file with support for scrubbing
    // Source: https://gist.github.com/widnyana/cd2bdda07dc02e9fce71
    private static function streamFile(Request $r, $contentType, $stream, $fullsize ) {
        $size = $fullsize;
        $response_code = 200;
        $headers = array("Content-type" => $contentType);

        // Check for request for part of the stream
        $range = $r->header('Range');
        if($range != null) {
            $eqPos = strpos($range, "=");
            $toPos = strpos($range, "-");
            $unit = substr($range, 0, $eqPos);
            $start = intval(substr($range, $eqPos+1, $toPos));
            $success = fseek($stream, $start);
            if($success == 0) {
                $size = $fullsize - $start;
                $response_code = 206;
                $headers["Accept-Ranges"] = $unit;
                $headers["Content-Range"] = $unit . " " . $start . "-" . ($fullsize-1) . "/" . $fullsize;
            }
        }

        $headers["Content-Length"] = $size;
        $response = new StreamedResponse(
          function () use ($stream) {
            $out = fopen('php://output', 'w');
            fputs($out, $stream);
            fclose($out);
          }, 200, $headers);
        $response->send();
    }

}
