require 'html5/filters/base'

module HTML5
  module Filters
    class InjectMetaCharset < Base
      def initialize(source, encoding)
        super(source)
        @encoding = encoding
      end

      def each
        state = :pre_head
        meta_found = @encoding.nil?
        pending = []

        __getobj__.each do |token|
          case token[:type]
          when :StartTag
            state = :in_head if token[:name].downcase == "head"

          when :EmptyTag
            if token[:name].downcase == "meta"
              # replace charset with actual encoding
              token[:data].each_with_index do |(name, value), index|
                if name == 'charset'
                  token[:data][index][1] = @encoding
                  meta_found = true
                end
              end

              # replace charset with actual encoding
              has_http_equiv_content_type = false
              content_index = -1
              token[:data].each_with_index do |(name, value), i|
                if name.downcase == 'charset'
                  token[:data][i] = ['charset', @encoding]
                  meta_found = true
                  break
                elsif name == 'http-equiv' and value.downcase == 'content-type'
                  has_http_equiv_content_type = true
                elsif name == 'content'
                  content_index = i
                end
              end

              if !meta_found
                if has_http_equiv_content_type && content_index >= 0
                  token[:data][content_index][1] = 'text/html; charset=%s' % @encoding
                  meta_found = true
                end
              end

            elsif token[:name].downcase == "head" && !meta_found
              # insert meta into empty head
              yield :type => :StartTag, :name => "head", :data => token[:data]
              yield :type => :EmptyTag, :name => "meta", :data => [["charset", @encoding]]
              yield :type => :EndTag,   :name => "head"
              meta_found = true
              next
            end

          when :EndTag
            if token[:name].downcase == "head" && pending.any?
              # insert meta into head (if necessary) and flush pending queue
              yield pending.shift
              yield :type => :EmptyTag, :name => "meta", :data => [["charset", @encoding]] if !meta_found
              yield pending.shift while pending.any?
              meta_found = true
              state = :post_head
            end
          end

          if state == :in_head
            pending << token
          else
            yield token
          end
        end
      end
    end
  end
end
