require 'builder'
require 'uri'

module SitemapGenerator
  module Builder
    # A Hash-like class for holding information about a sitemap URL and
    # generating an XML <url> element suitable for sitemaps.
    class SitemapUrl < Hash

      # Return a new instance with options configured on it.
      #
      # == Arguments
      # * sitemap - a Sitemap instance, or
      # * path, options - a path string and options hash
      #
      # == Options
      # Requires a host to be set.  If passing a sitemap, the sitemap must have a +default_host+
      # configured.  If calling with a path and options, you must include the <tt>:host</tt> option.
      #
      # * +priority+
      # * +changefreq+
      # * +lastmod+
      # * +images+
      # * +video+/+videos+
      # * +geo+
      # * +news+
      def initialize(path, options={})
        if sitemap = path.is_a?(SitemapGenerator::Builder::SitemapFile) && path
          options.reverse_merge!(:host => sitemap.location.host, :lastmod => sitemap.lastmod)
          path = sitemap.location.path_in_public
        end

        SitemapGenerator::Utilities.assert_valid_keys(options, :priority, :changefreq, :lastmod, :host, :images, :video, :geo, :news, :videos)
        options.reverse_merge!(:priority => 0.5, :changefreq => 'weekly', :lastmod => Time.now, :images => [], :news => {}, :videos => [])
        if options[:host].blank?
          raise "Cannot generate a url without a host"
        end
        if video = options.delete(:video)
          options[:videos] = video.is_a?(Array) ? options[:videos].concat(video) : options[:videos] << video
        end

        path = path.to_s.sub(/^\//, '')
        loc  = path.empty? ? options[:host] : (options[:host].to_s.sub(/\/$/, '') + '/' + path)
        self.merge!(
          :priority   => options[:priority],
          :changefreq => options[:changefreq],
          :lastmod    => options[:lastmod],
          :host       => options[:host],
          :loc        => loc,
          :images     => prepare_images(options[:images], options[:host]),
          :news       => prepare_news(options[:news]),
          :videos     => options[:videos],
          :geo        => options[:geo]
        )
      end

      # Return the URL as XML
      def to_xml(builder=nil)
        builder = ::Builder::XmlMarkup.new if builder.nil?
        builder.url do
          builder.loc        self[:loc]
          builder.lastmod    w3c_date(self[:lastmod])   if self[:lastmod]
          builder.changefreq self[:changefreq]          if self[:changefreq]
          builder.priority   self[:priority]            if self[:priority]

          unless self[:news].blank?
            news_data = self[:news]
            builder.news:news do
              builder.news:publication do
                builder.news :name, news_data[:publication_name] if news_data[:publication_name]
                builder.news :language, news_data[:publication_language] if news_data[:publication_language]
              end

              builder.news :access, news_data[:access] if news_data[:access]
              builder.news :genres, news_data[:genres] if news_data[:genres]
              builder.news :publication_date, news_data[:publication_date] if news_data[:publication_date]
              builder.news :title, news_data[:title] if news_data[:title]
              builder.news :keywords, news_data[:keywords] if news_data[:keywords]
              builder.news :stock_tickers, news_data[:stock_tickers] if news_data[:stock_tickers]
            end
          end

          self[:images].each do |image|
            builder.image:image do
              builder.image :loc, image[:loc]
              builder.image :caption, image[:caption]             if image[:caption]
              builder.image :geo_location, image[:geo_location]   if image[:geo_location]
              builder.image :title, image[:title]                 if image[:title]
              builder.image :license, image[:license]             if image[:license]
            end
          end

          self[:videos].each do |video|
            builder.video :video do
              builder.video :thumbnail_loc, video[:thumbnail_loc]
              builder.video :title, video[:title]
              builder.video :description, video[:description]
              builder.video :content_loc, video[:content_loc]           if video[:content_loc]
              if video[:player_loc]
                builder.video :player_loc, video[:player_loc], :allow_embed => (video[:allow_embed] ? 'yes' : 'no'), :autoplay => video[:autoplay]
              end

              builder.video :rating, video[:rating]                     if video[:rating]
              builder.video :view_count, video[:view_count]             if video[:view_count]
              builder.video :publication_date, video[:publication_date] if video[:publication_date]
              builder.video :expiration_date, video[:expiration_date]   if video[:expiration_date]
              builder.video :family_friendly, (video[:family_friendly] ? 'yes' : 'no')  if video[:family_friendly]
              builder.video :duration, video[:duration]                 if video[:duration]
              video[:tags].each {|tag| builder.video :tag, tag }        if video[:tags]
              builder.video :tag, video[:tag]                           if video[:tag]
              builder.video :category, video[:category]                 if video[:category]
              builder.video :gallery_loc, video[:gallery_loc]           if video[:gallery_loc]

              if video[:uploader]
                builder.video :uploader, video[:uploader], video[:uploader_info] ? { :info => video[:uploader_info] } : {}
              end
            end
          end

          unless self[:geo].blank?
            geo = self[:geo]
            builder.geo :geo do
              builder.geo :format, geo[:format] if geo[:format]
            end
          end
        end
        builder << '' # Force to string
      end

      def news?
        self[:news].present?
      end

      protected

      def prepare_news(news)
        SitemapGenerator::Utilities.assert_valid_keys(news, :publication_name, :publication_language, :publication_date, :genres, :access, :title, :keywords, :stock_tickers) unless news.empty?
        news
      end

      # Return an Array of image option Hashes suitable to be parsed by SitemapGenerator::Builder::SitemapFile
      def prepare_images(images, host)
        images.delete_if { |key,value| key[:loc] == nil }
        images.each do |r|
          SitemapGenerator::Utilities.assert_valid_keys(r, :loc, :caption, :geo_location, :title, :license)
          r[:loc] = URI.join(host, r[:loc]).to_s
        end
        images[0..(SitemapGenerator::MAX_SITEMAP_IMAGES-1)]
      end

      def w3c_date(date)
         date.utc.strftime("%Y-%m-%dT%H:%M:%S+00:00")
      end
    end
  end
end

{'avg_line_length': 43.99363057324841, 'max_line_length': 192, 'alphanum_fraction': 0.5747792094976111, 'licenses': ['MIT'], 'repository_name': 'fiverr/sitemap_generator', 'path': 'lib/sitemap_generator/builder/sitemap_url.rb', 'size': 6907, 'lang': 'Ruby'}