<?php
    /**
     * Class: RSSFeed
     * Generates an RSS feed piece by piece.
     *
     * See Also:
     *     http://www.rssboard.org/rss-2-0-11
     */
    class RSSFeed implements FeedGenerator {
        # Boolean: $open
        # Has the feed been opened?
        protected $open = false;

        # Variable: $count
        # The number of entries generated.
        protected $count = 0;

        # Array: $xml
        # Holds the feed as an array.
        protected $xml = array();

        /**
         * Function: type
         * Returns the content type of the feed.
         */
        public static function type(): string {
            return "application/rss+xml";
        }

        /**
         * Function: open
         * Adds the opening channel element and top-level elements.
         *
         * Parameters:
         *     $title - Title for this channel.
         *     $subtitle - Subtitle (optional).
         *     $id - Feed ID (optional).
         *     $updated - Time of update (optional).
         */
        public function open(
            $title,
            $subtitle = "",
            $id = "",
            $updated = null
        ): bool {
            if ($this->open)
                return false;

            $language = lang_base(Config::current()->locale);
            $link = url("/", MainController::current());

            $feed = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
            $feed.= '<rss version="2.0">'."\n";
            $feed.= '<channel>'."\n";
            $feed.= '<language>'.fix($language).'</language>'."\n";
            $feed.= '<title>'.strip_tags($title).'</title>'."\n";

            if (!empty($subtitle))
                $feed.= '<description>'.
                        strip_tags($subtitle).
                        '</description>'.
                        "\n";

            $feed.= '<lastBuildDate>'.
                    when(DATE_RSS, oneof($updated, time())).
                    '</lastBuildDate>'.
                    "\n";

            $feed.= '<link>'.$link.'</link>'."\n";
            $feed.= '<generator>'.CHYRP_IDENTITY.'</generator>'."\n";

            $this->xml = array(
                "feed" => $feed,
                "items" => array()
            );

            return $this->open = true;
        }

        /**
         * Function: entry
         * Adds an individual feed item.
         *
         * Parameters:
         *     $title - Title for this item.
         *     $id - The unique ID.
         *     $content - Content for this item.
         *     $link - The URL to the resource.
         *     $published - Time of creation.
         *     $updated - Time of update (optional).
         *     $name - Name of the author (optional).
         *     $uri - URI of the author (optional).
         *     $email - Email address of the author (optional).
         *
         * Notes:
         *     The item remains open to allow triggered insertions.
         */
        public function entry(
            $title,
            $id,
            $content,
            $link,
            $published,
            $updated = null,
            $name = "",
            $uri = "",
            $email = ""
        ): bool {
            if (!$this->open)
                return false;

            $this->count++;

            $entry = '<title>'.strip_tags($title).'</title>'."\n";
            $entry.= '<guid>'.fix($id).'</guid>'."\n";

            $entry.= '<pubDate>'.
                         when(DATE_RSS, $published).
                         '</pubDate>'.
                         "\n";

            $entry.= '<link>'.fix($link).'</link>'."\n";

            $entry.= '<description>'.
                         fix($content, false, true).
                         '</description>'.
                         "\n";

            if (!empty($email) and is_email($email))
                $entry.= '<author>'.fix($email).'</author>'."\n";

            $item = $this->count - 1;
            $this->xml["items"][$item] = $entry;
            return true;
        }

        /**
         * Function: category
         * Adds a category element for an item.
         *
         * Parameters:
         *     $term - String that identifies the category.
         *     $scheme - URI for the categorization scheme (optional).
         *     $label - Human-readable label for the category (optional).
         */
        public function category($term, $scheme = "", $label = ""): bool {
            if (!$this->open)
                return false;

            if (!$this->count)
                return false;

            $category = '<category';

            if (!empty($scheme))
                $category.= ' domain="'.fix($scheme, true).'"';

            $category.= '>'.fix($term, true).'</category>'."\n";

            $item = $this->count - 1;
            $this->xml["items"][$item].= $category;
            return true;
        }

        /**
         * Function: rights
         * Not implemented in RSS 2.0.11.
         */
        public function rights($text): bool {
            return false;
        }

        /**
         * Function: enclosure
         * Adds an enclosure element for a resource that is potentially large in size.
         *
         * Parameters:
         *     $link - The URL to the resource.
         *     $length - Size in bytes of the resource (optional).
         *     $type - The media type of the resource (optional).
         *     $title - Title for the resource (optional).
         */
        public function enclosure(
            $link,
            $length = 0,
            $type = "",
            $title = ""
        ): bool {
            if (!$this->open)
                return false;

            $enclosure = '<enclosure url="'.fix($link, true).'"'.
                         ' length="'.fix($length, true).'"'.
                         ' type="'.
                         fix(oneof($type, "application/octet-stream"), true).
                         '" />'.
                         "\n";

            if (!$this->count) {
                $this->xml["feed"].= $enclosure;
            } else {
                $item = $this->count - 1;
                $this->xml["items"][$item].= $enclosure;
            }

            return true;
        }

        /**
         * Function: related
         * Not implemented in RSS 2.0.11.
         */
        public function related($link): bool {
            return false;
        }

        /**
         * Function: feed
         * Returns the generated feed.
         */
        public function feed(): string {
            $feed = $this->xml["feed"];
            $items = $this->xml["items"];

            foreach ($items as $item) {
                $feed.= '<item>'."\n".
                        $item.
                        '</item>'."\n";
            }

            $feed.= '</channel>'."\n";
            $feed.= '</rss>'."\n";
            return $feed;
        }

        /**
         * Function: output
         * Displays the generated feed.
         */
        public function display(): bool {
            if (headers_sent())
                return false;

            header("Content-Type: ".self::type()."; charset=UTF-8");
            echo $this->feed();
            return true;
        }
    }