# Copyright (C) 2012-2025  Sutou Kouhei <kou@cozmixng.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

require "kramdown"
require "kramdown-parser-gfm"

require_relative "base"

module Kramdown
  module Parser
    class Kramdown
      alias_method :handle_extension_raw, :handle_extension
      def handle_extension(name, opts, body, type, line_no=nil)
        return true if handle_extension_raw(name, opts, body, type, line_no)
        element = Element.new(name.to_sym,
                              nil,
                              opts,
                              :category => type,
                              :location => line_no)
        if body
          root, warnings = self.class.parse(body, @options)
          fix_location(root, line_no)
          if type == :span
            p_element = root.children.first
            p_element.children.each do |sub_element|
              element.children << sub_element
            end
          else
            root.children.each do |sub_element|
              element.children << sub_element
            end
          end
        end
        @tree.children << element
        true
      end

      alias_method :configure_parser_raw, :configure_parser
      def configure_parser
        @span_parsers.unshift(:strikethrough)
        position = @block_parsers.index(:codeblock_fenced)
        @block_parsers.insert(position, :codeblock_fenced_gfm)

        configure_parser_raw
      end

      STRIKETHROUGH_DELIMITER = "~~"
      STRIKETHROUGH_DELIMITER_PATTERN =
        /#{Regexp.escape(STRIKETHROUGH_DELIMITER)}/
      def parse_strikethrough
        start_line_number = @src.current_line_number

        delimiter = @src.scan(STRIKETHROUGH_DELIMITER_PATTERN)
        saved_pos = @src.save_pos

        text = @src.scan_until(STRIKETHROUGH_DELIMITER_PATTERN)
        if text
          text = text.sub(/#{STRIKETHROUGH_DELIMITER_PATTERN}\Z/, "")
          @tree.children << Element.new(:strikethrough, text, nil,
                                        :location => start_line_number)
        else
          @src.revert_pos(saved_pos)
          add_text(delimiter)
        end
      end
      define_parser(:strikethrough,
                    STRIKETHROUGH_DELIMITER_PATTERN,
                    STRIKETHROUGH_DELIMITER)

      escaped_chars_start_re =
        parser(:escaped_chars).start_re.to_s.sub(/(\]\)\))\z/, "~\\1")
      parser(:escaped_chars).start_re = /#{escaped_chars_start_re}/


      parser(:codeblock_fenced_gfm).method = "parse_codeblock_fenced_gfm"
      def parse_codeblock_fenced_gfm
        original_match = self.class::FENCED_CODEBLOCK_MATCH
        begin
          self.class.send(:remove_const, :FENCED_CODEBLOCK_MATCH)
          self.class.const_set(:FENCED_CODEBLOCK_MATCH,
                               GFM::FENCED_CODEBLOCK_MATCH)
          parse_codeblock_fenced
        ensure
          self.class.send(:remove_const, :FENCED_CODEBLOCK_MATCH)
          self.class.const_set(:FENCED_CODEBLOCK_MATCH, original_match)
        end
      end

      private
      def fix_location(element, base_location)
        return unless element.options.key?(:location)
        element.options[:location] += base_location - 1
        element.children.each do |sub_element|
          fix_location(sub_element, base_location)
        end
      end
    end
  end
end

module Rabbit
  module Parser
    class Markdown < Base
    end
  end
end

require_relative "markdown/converter"

module Rabbit
  module Parser
    class Markdown
      unshift_loader(self)
      class << self
        def format_name
          "Makrdown"
        end

        def match?(source)
          /\A(?:md|markdown)\z/i =~ source.extension.to_s
        end
      end

      def parse
        document = Kramdown::Document.new(@source.read)
        converter = Converter.new(@canvas)
        converter.convert(document.root)
      end
    end
  end
end
