#!/usr/bin/env perl
# -*- cperl; cperl-indent-level: 4 -*-
# Copyright (C) 2011-2021, Roland van Ipenburg
## no critic qw(RequireExplicitInclusion ProhibitCallsToUnexportedSubs ProhibitCallsToUndeclaredSubs RequireTidyCode ProhibitLongLines RestrictLongStrings ProhibitImplicitNewlines RequireASCII)
use strict;
use warnings;

use utf8;
use 5.014000;

our $VERSION = 'v1.0.5';

use Dancer2;
use HTTP::Status qw(:constants status_message);
use English qw( -no_match_vars);

use Readonly;
Readonly::Scalar my $EMPTY           => q{};
Readonly::Scalar my $SPACE           => q{ };
Readonly::Scalar my $FALLBACK_OUTPUT => q{PHP};
Readonly::Scalar my $CONNECTOR_PORT  => 18_081;
Readonly::Scalar my $MAX_REQUESTS    => 100;
Readonly::Scalar my $API_KEY         => q{TEST};
Readonly::Scalar my $API_VERSION     => q{v1};
Readonly::Scalar my $CHARSET         => q{; charset=utf-8};
Readonly::Scalar my $ROOT            => qq{/$API_VERSION};
Readonly::Scalar my $STRIP_QUERY     => qr{^/[?]}sxm;
Readonly::Scalar my $ISE             => HTTP::Status::HTTP_INTERNAL_SERVER_ERROR;
Readonly::Scalar my $BAD             => HTTP::Status::HTTP_BAD_REQUEST;
Readonly::Scalar my $AUTH            => HTTP::Status::HTTP_UNAUTHORIZED;
Readonly::Scalar my $FORB            => HTTP::Status::HTTP_FORBIDDEN;

Readonly::Hash my %OUTPUT       => (
    'json' => q{application/json},
    'xml'  => q{text/xml},
    'php'  => q{text/plain},
);
Readonly::Hash my %RESPONSE => (
    'version' => {
        'json' => q{{
    "version": [
        {
            "version": "v1",
            "build": "0.0.1"
        }
    ]   
        }},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list type="version" itemcount="1">
    <item>
        <version><![CDATA[v1]]></version>
        <build><![CDATA[0.0.1]]></build>
    </item>
</list>},
        'php' =>
q{a:1:{s:7:"version";a:1:{i:0;a:2:{s:7:"version";s:2:"v1";s:5:"build";s:5:"0.0.1";}}}},
    },
    'latest_article' => {
        'json' => q{{
"latest_article": [
    [
        {
            "id": "156833"
            "type": "article",
            "title": "Zeker honderd doden bij vliegramp in Tripoli"
            "description": "Bij een vliegtuigongeluk op de luchthaven van de Libische hoofdstad Tripoli zouden 103 doden zijn gevallen."
            "published": "2010-05-12 10:26:00"
            "last_update": "2010-05-12 09:11:04"
            "thumbnail_xs": http://content.nos.nl/data/image/xs/2010/07/30/175416.jpg
            "thumbnail_s": http://content.nos.nl/data/image/s/2010/07/30/175416.jpg
            "thumbnail_m": http://content.nos.nl/data/image/m/2010/07/30/175416.jpg
            "link": http://nos.nl/artikel/156833-zeker-honderd-doden-bij-vliegramp-in-tripoli.html
            "keywords": [
                "libië"
                "doden"
                "tripoli"
                "vliegtuig"
                "crash"
                "Seizoen 2009/2010"
            ]
        },
        {
            "id": "156845"
            "type": "article",
            "title": "Groei Nederlandse economie neemt af"
            "description": "De Nederlandse economie is in het eerste kwartaal van het jaar gegroeid, maar de groei is lager dan in de twee voorgaande kwartalen."
            "published": "2010-05-12 10:06:02"
            "last_update": "2010-05-12 10:11:04"
            "thumbnail_xs": http://content.nos.nl/data/image/xs/2010/07/30/175416.jpg
            "thumbnail_s": http://content.nos.nl/data/image/s/2010/07/30/175416.jpg
            "thumbnail_m": http://content.nos.nl/data/image/m/2010/07/30/175416.jpg
            "link": http://nos.nl/artikel/156845-groei-nederlandse-economie-neemt-af.html
            "keywords": [
                "economie"
                "cbs"
                "kwartaalcijfers"
                "groei"
            ]
        }
    ]
]}},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list  type="article" itemcount="10">
    <article>
        <id>175418</id>
        <type>article</type>
        <title><![CDATA[Geen schot in Belgische formatie]]></title>
        <description><![CDATA[In België is er anderhalve maand na de verkiezingen nog geen zicht op de vorming van een kabinet.]]></description>
        <published><![CDATA[2010-07-30 20:29:04]]></published>
        <last_update><![CDATA[2010-07-30 20:35:06]]></last_update>
        <thumbnail_xs><![CDATA[http://content.nos.nl/data/image/xs/2010/05/25/159715.jpg]]></thumbnail_xs>
        <thumbnail_s><![CDATA[http://content.nos.nl/data/image/s/2010/05/25/159715.jpg]]></thumbnail_s>
        <thumbnail_m><![CDATA[http://content.nos.nl/data/image/m/2010/05/25/159715.jpg]]></thumbnail_m>
        <link><![CDATA[http://nos.nl/artikel/175418-geen-schot-in-belgische-formatie.html]]></link>
        <keywords>
            <keyword><![CDATA[belgië]]></keyword>
            <keyword><![CDATA[Vlaanderen]]></keyword>
            <keyword><![CDATA[Elio di Rupo]]></keyword>
            <keyword><![CDATA[kabinetsformatie]]></keyword>
        </keywords>
    </article>
</list>},
    },
    'latest_video' => {
        'json' => q{{
    "latest_video": [
        [
            {
                "id": "175327"
                "type": "video",
                "title": "28 doden door bosbranden Rusland"
                "description": "In Rusland zijn bij bosbranden zeker 28 doden gevallen. Veel huizen zijn in vlammen opgegaan, en duizenden mensen zijn geëvacueerd. Rusland heeft te kampen met de heetste zomer in ruim honderd jaar. Door de extreme droogte is de natuur extra kwetsbaar voor bosbranden."
                "published": "2010-07-30 22:25:06"
                "last_update": "2010-07-30 15:43:07"
                "thumbnail_xs": http://content.nos.nl/data/video/xs/2010/07/30/300710_18_zui-CNO1007300Z_1.jpg
                "thumbnail_s": http://content.nos.nl/data/video/s/2010/07/30/300710_18_zui-CNO1007300Z_1.jpg
                "thumbnail_m": http://content.nos.nl/data/video/m/2010/07/30/300710_18_zui-CNO1007300Z_1.jpg
                "link": http://nos.nl/video/175327-28-doden-door-bosbranden-rusland.html
                "embedcode": "<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>"
                "keywords": [
                    "bosbranden"
                    "droogte"
                    "hitte"
                    "Rusland"
                ]
            },
            {
                "id": "175424"
                "type": "video",
                "title": "Duisburg verwacht tienduizenden bezoekers rouwdienst"
                "description": "Duisburg maakt zich op voor een dag van rouw. Morgen wordt het drama met de Love Parade van vorige week herdacht. Daarbij vielen 21 doden. Om elf uur begint de rouwdienst, waarbij bondskanselier Angela Merkel aanwezig is en naar verwachting tienduizenden rouwenden, misschien zelfs wel 100.000. Sommige inwoners van Duisburg zien dat met angst tegemoet."
                "published": "2010-07-30 20:55:06"
                "last_update": "2010-07-30 21:05:08"
                "thumbnail_xs": http://content.nos.nl/data/video/xs/2010/07/30/300710_18_zui-CNO1007300Z_1.jpg
                "thumbnail_s": http://content.nos.nl/data/video/s/2010/07/30/300710_18_zui-CNO1007300Z_1.jpg
                "thumbnail_m": http://content.nos.nl/data/video/m/2010/07/30/300710_18_zui-CNO1007300Z_1.jpg
                "link": http://nos.nl/video/175424-duisburg-verwacht-tienduizenden-bezoekers-rouwdienst.html
                "embedcode": "<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>"          
                "keywords": [
                    "herdenking"
                    "Duisburg"
                    "Duitsland"
                    "rouwdienst"
                    "Love Parade"
            ]
        }
    ]
   ]
}},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list type="latest_video" itemcount="2">
    <video>
        <id>175327</id>
        <type>video</type>
        <title><![CDATA[28 doden door bosbranden Rusland]]></title>
        <description><![CDATA[In Rusland zijn bij bosbranden zeker 28 doden gevallen. Veel huizen zijn in vlammen opgegaan, en duizenden mensen zijn geëvacueerd. Rusland heeft te kampen met de heetste zomer in ruim honderd jaar. Door de extreme droogte is de natuur extra kwetsbaar voor bosbranden.]]></description>
        <published><![CDATA[2010-07-30 22:25:06]]></published>
        <last_update><![CDATA[2010-07-30 15:43:07]]></last_update>
        <thumbnail_xs><![CDATA[http://content.nos.nl/data/video/xs/2010/07/30/300710_15_ru2-CNO1007300Z_1.jpg]]></thumbnail_xs>
        <thumbnail_s><![CDATA[http://content.nos.nl/data/video/s/2010/07/30/300710_15_ru2-CNO1007300Z_1.jpg]]></thumbnail_s>
        <thumbnail_m><![CDATA[http://content.nos.nl/data/video/m/2010/07/30/300710_15_ru2-CNO1007300Z_1.jpg]]></thumbnail_m>
        <link><![CDATA[http://nos.nl/video/175327-28-doden-door-bosbranden-rusland.html]]></link>
        <embedcode><![CDATA[<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>]]></embedcode>        
        <keywords>
            <keyword><![CDATA[bosbranden]]></keyword>
            <keyword><![CDATA[droogte]]></keyword>
            <keyword><![CDATA[hitte]]></keyword>
            <keyword><![CDATA[Rusland]]></keyword>
        </keywords>
    </video>
    <video>
        <id>175424</id>
        <type>video</type>
        <title><![CDATA[Duisburg verwacht tienduizenden bezoekers rouwdienst]]></title>
        <description><![CDATA[Duisburg maakt zich op voor een dag van rouw. Morgen wordt het drama met de Love Parade van vorige week herdacht. Daarbij vielen 21 doden. Om  elf uur begint de rouwdienst, waarbij bondskanselier Angela Merkel aanwezig is en naar verwachting tienduizenden rouwenden, misschien zelfs wel 100.000. Sommige inwoners van Duisburg zien dat met angst tegemoet.]]></description>
        <published><![CDATA[2010-07-30 20:55:06]]></published>
        <last_update><![CDATA[2010-07-30 21:05:08]]></last_update>
        <thumbnail_xs><![CDATA[http://content.nos.nl/data/video/xs/2010/07/30/300710_15_ru2-CNO1007300Z_1.jpg]]></thumbnail_xs>
        <thumbnail_s><![CDATA[http://content.nos.nl/data/video/s/2010/07/30/300710_15_ru2-CNO1007300Z_1.jpg]]></thumbnail_s>
        <thumbnail_m><![CDATA[http://content.nos.nl/data/video/m/2010/07/30/300710_15_ru2-CNO1007300Z_1.jpg]]></thumbnail_m>
        <link><![CDATA[http://nos.nl/video/175424-duisburg-verwacht-tienduizenden-bezoekers-rouwdienst.html]]></link>
        <embedcode><![CDATA[<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>]]></embedcode>        
        <keywords>
            <keyword><![CDATA[herdenking]]></keyword>
            <keyword><![CDATA[Duisburg]]></keyword>
            <keyword><![CDATA[Duitsland]]></keyword>
            <keyword><![CDATA[rouwdienst]]></keyword>
            <keyword><![CDATA[Love Parade]]></keyword>
        </keywords>
    </video>
</list>},
    },
    'latest_audio' => {
        'json' => q{{
    "latest_audio": [
        [
            {
                "id": "175384"
                "type": "audio",
                "title": "Paul Sneijder over Belgische formatie"
                "description": "Ook in Belgie laat een nieuwe regering op zich wachten.n Toch is preformateur Di Rupo vanmiddag naar de koning geweest. Correspondent Paul Sneijder in Brussel."
                "published": "2010-07-30 18:00:00"
                "last_update": "2010-07-30 18:33:02"
                "thumbnail_xs": ""
                "thumbnail_s": ""
                "thumbnail_m": ""
                "link": http://nos.nl/audio/175384-paul-sneijder-over-belgische-formatie.html
                "embedcode": "<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_audio.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>"          
                "keywords": [
                    "belgië"
                    "brussel"
                    "formatie"
                ]
            },
            {
                "id": "175383"
                "type": "audio",
                "title": "Griekse chauffeurs blijven staken"
                "description": "In Griekenland dreigt een onhoudbare situatie. De vrachtwagenchauffeurs gaan door met staken. Daardoor wordt het brandstoftekort steeds nijpender. Met alle gevolgen van dien. Correspondent Conny Keesen in Athene."
                "published": "2010-07-30 18:00:00"
                "last_update": "2010-07-30 18:33:02"
                "thumbnail_xs": ""
                "thumbnail_s": ""
                "thumbnail_m": ""
                "link": http://nos.nl/audio/175383-griekse-chauffeurs-blijven-staken.html
                "embedcode": "<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_audio.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>"          
                "keywords": [
                    "vrachtwagenchauffeurs"
                    "benzine"
                    "staking"
                    "Griekenland"
                    "Athene"
                    "brandstoftekort"
                ]
            }
        ]
    ]
        }},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list type="latest_audio" itemcount="2">
    <audio>
        <id>175384</id>
        <type><![CDATA[audio]]></type>
        <title><![CDATA[Paul Sneijder over Belgische formatie]]></title>
        <description><![CDATA[Ook in Belgie laat een nieuwe regering op zich wachten.n Toch is preformateur Di Rupo vanmiddag naar de koning geweest. Correspondent Paul Sneijder in Brussel.]]></description>
        <published><![CDATA[2010-07-30 18:00:00]]></published>
        <last_update><![CDATA[2010-07-30 18:33:02]]></last_update>
        <thumbnail_xs><![CDATA[]]></thumbnail_xs>
        <thumbnail_s><![CDATA[]]></thumbnail_s>
        <thumbnail_m><![CDATA[]]></thumbnail_m>
        <link><![CDATA[http://nos.nl/audio/175384-paul-sneijder-over-belgische-formatie.html]]></link>
        <embedcode><![CDATA[<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_audio.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>]]></embedcode>        
        <keywords>
            <keyword><![CDATA[belgië]]></keyword>
            <keyword><![CDATA[brussel]]></keyword>
            <keyword><![CDATA[formatie]]></keyword>
        </keywords>
    </audio>
    <audio>
        <id>175383</id>
        <type><![CDATA[audio]]></type>
        <title><![CDATA[Griekse chauffeurs blijven staken]]></title>
        <description><![CDATA[In Griekenland dreigt een onhoudbare situatie. De vrachtwagenchauffeurs gaan door met staken. Daardoor wordt het brandstoftekort steeds nijpender. Met alle gevolgen van dien. Correspondent Conny Keesen in Athene.]]></description>
        <published><![CDATA[2010-07-30 18:00:00]]></published>
        <last_update><![CDATA[2010-07-30 18:33:02]]></last_update>
        <thumbnail_xs><![CDATA[]]></thumbnail_xs>
        <thumbnail_s><![CDATA[]]></thumbnail_s>
        <thumbnail_m><![CDATA[]]></thumbnail_m>
        <link><![CDATA[http://nos.nl/audio/175383-griekse-chauffeurs-blijven-staken.html]]></link>
        <embedcode><![CDATA[<object width="550" height="309"><param name="movie" value="http://s.nos.nl/swf/embed/nos_partner_audio.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user"></param><param name="wmode" value="transparent"></param><param name="allowScriptAccess" value="always"></param><param name="allowfullscreen" value="true"></param><embed src="http://s.nos.nl/swf/embed/nos_partner_video.swf?tcmid=tcm-5-776295&amp;platform=open&amp;partner=user" type="application/x-shockwave-flash" wmode="transparent" width="550" height="309" allowfullscreen="true" allowScriptAccess="always"></embed></object>]]></embedcode>        
        <keywords>
            <keyword><![CDATA[vrachtwagenchauffeurs]]></keyword>
            <keyword><![CDATA[benzine]]></keyword>
            <keyword><![CDATA[staking]]></keyword>
            <keyword><![CDATA[Griekenland]]></keyword>
            <keyword><![CDATA[Athene]]></keyword>
            <keyword><![CDATA[brandstoftekort]]></keyword>
        </keywords>
    </audio>
</list>},
    },
    'search' => {
        'json' => q{{
    "search":[
        {
            "documents":[
                {
                    "id":"video-36572",
                    "score":100,
                    "type":"video",
                    "title":"Cricket: finale twenty20 op Lord's","description":"Cricket, dan. De finale van het 
                    twenty20-toernooi, in Londen. Het is de tweede editie in deze verkorte uitvoering en Pakistan 
                    is voor de tweede keer finalist. Twee jaar geleden verloor Pakistan van India, en nu is het tijd 
                    voor revanche. De tegenstander is Sri Lanka.",
                    "published":"2009-06-22 23:06:54",
                    "last_update":"2010-12-02 16:12:09",
                    "thumbnail":"http://content.nos.nl/data/video/s/2009/06/22/36572.jpg",
                    "category":"Sport",
                    "subcategory":"Algemeen",
                    "link":"http://nos.nl/video/36572-cricket-finale-twenty20-op-lords.html",
                    "keywords":[
                        "india",
                        "pakistan",
                        "Cricket"
                    ]
                }
            ],
            "keywords":[
                {
                    "tag":"Cricket",
                    "count":1
                },
                {   
                    "tag":"india",
                    "count":1
                },
                {
                    "tag":"pakistan",
                    "count":1
                }
            ]
        }
    ]
        }},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list type="search" itemcount="1">
    <documents>
        <document>
            <id>video-36572</id>
            <score>100</score>
            <type>video</type>
            <title>Cricket: finale twenty20 op Lord's</title>
            <description>
            Cricket, dan. De finale van het twenty20-toernooi, in Londen. Het is de tweede editie in deze verkorte uitvoering en Pakistan is voor de tweede keer finalist. Twee jaar geleden verloor Pakistan van India, en nu is het tijd voor revanche. De tegenstander is Sri Lanka.
            </description>
            <published>2009-06-22 23:06:54</published>
            <last_update>2010-12-02 16:12:09</last_update>
            <thumbnail>
            http://content.nos.nl/data/video/s/2009/06/22/36572.jpg
            </thumbnail>
            <category>Sport</category>
            <subcategory>Algemeen</subcategory>
            <link>
            http://nos.nl/video/36572-cricket-finale-twenty20-op-lords.html
            </link>
            <keywords>
                <keyword>india</keyword>
                <keyword>pakistan</keyword>
                <keyword>Cricket</keyword>
            </keywords>
        </document>
    </documents>
    <related>
        <related>Cricket</related>
        <related>india</related>
        <related>pakistan</related>
    </related>
</list>},
    },
    'guide_radio' => {
        'json' => q{{
    guide: [
        [
            {
                "date": "2010-10-06",
                "guide": [
                    {
                        "id": "171668744"
                        "type": "radio"
                        "channel_icon": http://open.nos.nl/img/icons/radio/ra1.png
                        "channel_code": "RA1"
                        "channel_name": "Radio 1"
                        "starttime": "2010-10-06 06:00:00"
                        "endtime": "2010-10-06 06:02:00"
                        "genre": "Nieuws/actualiteiten"
                        "title": "Nieuws"
                        "description": ""
                    },
                    {
                        "id": "171929021"
                        "type": "radio"
                        "channel_icon": http://open.nos.nl/img/icons/radio/ra2.png
                        "channel_code": "RA2"
                        "channel_name": "Radio 2"
                        "starttime": "2010-10-06 06:00:00"
                        "endtime": "2010-10-06 06:05:00"
                        "genre": "Overige"
                        "title": "Nieuws"
                        "description": ""
                    },
                ]
            },
            {
                "date": "2010-10-07"
                "guide": [
                    {
                        "id": "171668782"
                        "type": "radio"
                        "channel_icon": http://open.nos.nl/img/icons/radio/ra1.png
                        "channel_code": "RA1"
                        "channel_name": "Radio 1"
                        "starttime": "2010-10-07 22:04:00"
                        "endtime": "2010-10-07 23:05:00"
                        "genre": "Sport"
                        "title": "NOS-Langs de lijn"
                        "description": ""
                    },
                ]
            }
        ]
    ]
        }},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list type="guide" daycount="2" itemcount="60">
    <dayguide type="radio" date="2010-10-06">
        <item>
            <id>171668744</id>
            <type>radio</type>
            <channel_icon>http://open.nos.nl/img/icons/radio/ra1.png</channel_icon>
            <channel_code>RA1</channel_code>
            <channel_name>Radio 1</channel_name>
            <starttime>2010-10-06 06:00:00</starttime>
            <endtime>2010-10-06 06:02:00</endtime>
            <genre>Nieuws/actualiteiten</genre>
            <title>Nieuws</title>
            <description></description>
        </item>
        <item>
            <id>171929021</id>
            <type>radio</type>
            <channel_icon>http://open.nos.nl/img/icons/radio/ra2.png</channel_icon>
            <channel_code>RA2</channel_code>
            <channel_name>Radio 2</channel_name>
            <starttime>2010-10-06 06:00:00</starttime>
            <endtime>2010-10-06 06:05:00</endtime>
            <genre>Overige</genre>
            <title>Nieuws</title>
            <description></description>
        </item>
    </dayguide>
    <dayguide type="tv" date="2010-10-07">
        <item>
            <id>171929021</id>
            <type>radio</type>
            <channel_icon>http://open.nos.nl/img/icons/radio/ra2.png</channel_icon>
            <channel_code>RA2</channel_code>
            <channel_name>Radio 2</channel_name>
            <starttime>2010-10-06 06:00:00</starttime>
            <endtime>2010-10-06 06:05:00</endtime>
            <genre>Overige</genre>
            <title>Nieuws</title>
            <description></description>
        </item>
    </dayguide>
</list>
        },
    },
    'guide_tv' => {
        'json' => q{{
    "guide": [
        [
            {
                "date": "2010-10-06",
                "guide": [
                    {
                        "id": "171026258",
                        "type": "tv",
                        "channel_icon": "http://open.nos.nl/img/icons/tv/nl1.png",
                        "channel_code": "NL1",
                        "channel_name": "Nederland 1",
                        "starttime": "2010-10-06 07:00:00",
                        "endtime": "2010-10-06 07:10:00",
                        "genre": "Nieuws/actualiteiten",
                        "title": "NOS Journaal",
                        "description": "Journaal"
                    },
                    {
                        "id": "171026254",
                        "type": "tv",
                        "channel_icon": "http://open.nos.nl/img/icons/tv/nl1.png",
                        "channel_code": "NL1",
                        "channel_name": "Nederland 1",
                        "starttime": "2010-10-06 08:00:00",
                        "endtime": "2010-10-06 08:10:00",
                        "genre": "Nieuws/actualiteiten",
                        "title": "NOS Journaal",
                        "description": "Journaal"
                    },
                ]
            },
            {
                "date": "2010-10-07",
                "guide": [
                    {
                        "id": "171026258",
                        "type": "tv",
                        "channel_icon": "http://open.nos.nl/img/icons/tv/nl1.png",
                        "channel_code": "NL1",
                        "channel_name": "Nederland 1",
                        "starttime": "2010-10-07 07:00:00",
                        "endtime": "2010-10-07 07:10:00",
                        "genre": "Nieuws/actualiteiten",
                        "title": "NOS Journaal",
                        "description": "Journaal"
                    },
                    {
                        "id": "171026254",
                        "type": "tv",
                        "channel_icon": "http://open.nos.nl/img/icons/tv/nl1.png",
                        "channel_code": "NL1",
                        "channel_name": "Nederland 1",
                        "starttime": "2010-10-07 08:00:00",
                        "endtime": "2010-10-07 08:10:00",
                        "genre": "Nieuws/actualiteiten",
                        "title": "NOS Journaal",
                        "description": "Journaal",
                    },
                ]
            }
        ]
    ]
        }},
        'xml' => q{<?xml version="1.0" encoding="UTF-8"?>
<list type="guide" daycount="2" itemcount="60">
<dayguide type="tv" date="2010-10-06">
<item>
<id>171026258</id>
<type><![CDATA[tv]]></type>
<channel_icon><![CDATA[http://open.nos.nl/img/icons/tv/nl1.png]]></channel_icon>
<channel_code><![CDATA[NL1]]></channel_code>
<channel_name><![CDATA[Nederland 1]]></channel_name>
<starttime><![CDATA[2010-10-06 07:00:00]]></starttime>
<endtime><![CDATA[2010-10-06 07:10:00]]></endtime>
<genre><![CDATA[Nieuws/actualiteiten]]></genre>
<title><![CDATA[NOS Journaal]]></title>
<description><![CDATA[Journaal]]></description>
</item>
<item>
<id>171246201</id>
<type><![CDATA[tv]]></type>
<channel_icon><![CDATA[http://open.nos.nl/img/icons/tv/nl2.png]]></channel_icon>
<channel_code><![CDATA[NL2]]></channel_code>
<channel_name><![CDATA[Nederland 2]]></channel_name>
<starttime><![CDATA[2010-10-06 07:00:00]]></starttime>
<endtime><![CDATA[2010-10-06 07:10:00]]></endtime>
<genre><![CDATA[Nieuws/actualiteiten]]></genre>
<title><![CDATA[NOS Journaal met gebarentolk]]></title>
<description><![CDATA[NOS Journaal met gebarentolk]]></description>
</item>
</dayguide>
<dayguide type="tv" date="2010-10-07">
<item>
<id>171026214</id>
<type><![CDATA[tv]]></type>
<channel_icon><![CDATA[http://open.nos.nl/img/icons/tv/nl1.png]]></channel_icon>
<channel_code><![CDATA[NL1]]></channel_code>
<channel_name><![CDATA[Nederland 1]]></channel_name>
<starttime><![CDATA[2010-10-07 00:00:00]]></starttime>
<endtime><![CDATA[2010-10-07 00:20:00]]></endtime>
<genre><![CDATA[Nieuws/actualiteiten]]></genre>
<title><![CDATA[NOS Journaal]]></title>
<description><![CDATA[NOS Journaal Laat]]></description>
</item>
<item>
<id>171026209</id>
<type><![CDATA[tv]]></type>
<channel_icon><![CDATA[http://open.nos.nl/img/icons/tv/nl1.png]]></channel_icon>
<channel_code><![CDATA[NL1]]></channel_code>
<channel_name><![CDATA[Nederland 1]]></channel_name>
<starttime><![CDATA[2010-10-07 02:20:00]]></starttime>
<endtime><![CDATA[2010-10-07 06:25:00]]></endtime>
<genre><![CDATA[Informatief]]></genre>
<title><![CDATA[NOS Tekst tv]]></title>
<description><![CDATA[Informatie uit Teletekst.]]></description>
</item>
</dayguide>
</list>
        },
    },
);
Readonly::Hash my %ERROR => (
    'bad_request_missing' =>
      q{{"badrequest":{"error":{"code":101,"message":"API-key not found"}}}},
    'bad_request_invalid' =>
q{{"wrong param value":{"error":{"code":"111","message":"Param output must be of value (php,xml,json)"}}}},
    'unauthorized' =>
q{{"unauthorized, invalid key":{"error":{"code":201,"message":"Invalid key"}}}},
    'forbidden' =>
q{{"forbidden, rate limit":{"error":{"code":301,"message":"Rate limit, max requests per minute is set at 60"}}}},
);

my $requests = 0;
my $last_hit = 0;

prefix $ROOT;

get qr{.*}msx => sub {
    if (time - $last_hit > 60) {
        $requests = 0;
    }
    my $re_ise = qr{FORCED_internal_server_error}msx;
    my $re_bad = qr{FORCED_bad_request}msx;
    my $re_cat = qr{(?<cat>[^/]+)/(?<par>[^/]+)}msx;
    my $re_key = qr{key/(?<key>[^/]+)}msx;
    my $re_out = qr{output/(?<out>[^/]+)}msx;
    request->path =~ m{$ROOT/$re_cat/$re_key/$re_out}ismx;
    my $cat = $LAST_PAREN_MATCH{'cat'};
    my $par = $LAST_PAREN_MATCH{'par'};
    my $key = $LAST_PAREN_MATCH{'key'};
    my $out = $LAST_PAREN_MATCH{'out'};
    if (request->path =~ m{$re_ise}ismx ) {
        status $ISE;
        return;
    }
    if (request->path =~ m{$re_bad}ismx ) {
        $out = q{FORCED BAD REQUEST FOR TESTING};
    }
    if ( !defined $OUTPUT{$out} ) {
        $out = lc $FALLBACK_OUTPUT;
        status $BAD;
        content_type $OUTPUT{$out};
        response_header 'Status' => $BAD . $SPACE . status_message($BAD);
        return $ERROR{'bad_request_invalid'};
    }
    content_type $OUTPUT{$out};
    if ( !defined $key ) {
        status $BAD;
        response_header 'Status' => $BAD . $SPACE . status_message($BAD);
        return $ERROR{'bad_request_missing'};
    }
    if ( $key ne $API_KEY ) {
        status $AUTH;
        response_header 'Status' => $AUTH . $SPACE . status_message($AUTH);
        return $ERROR{'unauthorized'};
    }
    $requests++;
    $last_hit = time;
    # dummy rate limit tester:
    if ( $requests > $MAX_REQUESTS ) {
        status $FORB;
        response_header 'Status' => $FORB . $SPACE . status_message($FORB);
        $requests = 0;
        return $ERROR{'forbidden'};
    }
    if ( q{index} eq $cat ) {
        return ${$RESPONSE{'version'}}{$out};
    }
    if ( q{latest} eq $cat ) {
        return ${$RESPONSE{ qq{latest_$par} }}{$out};
    }
    if ( q{search} eq $cat ) {
        return $RESPONSE{'search'}->{$out};
    }
    if ( q{guide} eq $cat ) {
        return ${$RESPONSE{ qq{guide_$par} }}{$out};
    }
};

set 'port' => $CONNECTOR_PORT;
set 'charset' => $CHARSET;
start;

__END__

=encoding utf8

=for stopwords Bitbucket DummyServer.pl manpage Readonly Ipenburg
MERCHANTABILITY

=head1 NAME

DummyServer.pl - a dummy NOS Open server to test the API against

=head1 USAGE

B<./DummyServer.pl> [B<--port=PORT>]

=head1 DESCRIPTION

For debugging against a server that isn't the NOS Open live server, this script
provides the same API against limited content.

=head1 REQUIRED ARGUMENTS

None.

=head1 OPTIONS

=over 4

=item B< -?, -h, --help>

Show help

=item B< -m, --man>

Show manpage

=item B< -v, --verbose>

Be more verbose

=item B<--version>

Show version and license

=item B<--port>

Port number to listen on, defaults to port 18081

=back

=head1 DIAGNOSTICS

=head1 EXIT STATUS

=head1 CONFIGURATION

=head1 DEPENDENCIES

Perl 5.14.0, CGI, Getopt::Long, HTTP::Server::Brick, Pod::Usage,
Pod::Usage::CommandLine, Readonly, WWW::NOS::Open

=head1 INCOMPATIBILITIES

Version 2 of the API is not provided.

=head1 BUGS AND LIMITATIONS

Only version 1 of the API is provided.

Please report any bugs or feature requests at
L<Bitbucket|https://bitbucket.org/rolandvanipenburg/www-nos-open/issues>.

=head1 CONFIGURATION AND ENVIRONMENT

Using the defaults it starts the HTTP service on port 18081.

=head1 AUTHOR

Roland van Ipenburg, E<lt>roland@rolandvanipenburg.comE<gt>

=head1 LICENSE AND COPYRIGHT

Copyright 2011-2021 by Roland van Ipenburg

This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.14.0 or,
at your option, any later version of Perl 5 you may have available.

=head1 DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

=cut
