require 'open-uri'
require 'uri'
require 'cgi'
require 'digest'

module Jekyll

  class Snippet < Liquid::Tag

    def initialize(tag_name, markup, tokens)
      parse_params(markup)

      unless @url.start_with?('http')
        @url = absolute_url(@url)
      end

      if @url =~ URI::regexp
        @content = fetchContent(@url)
      else
        STDERR.puts "Invalid URL passed to Snippet: " + @url
      end

      super
    end

    def render(context)
      if @content
        snippet = @content
        if @id
          snippet = snippet[/#{Regexp.escape("START SNIPPET: " + @id)}[^\n]*\n(.*?)\n[^\n]*#{Regexp.escape("END SNIPPET: " + @id)}/m, 1]
          unless snippet
            snippet = "START SNIPPET: " + @id + " not found in " + @url
            STDERR.puts snippet
          end
        end
        if @javadoc
          unless @javadoc.to_s.strip.casecmp("FALSE") == 0
            snippet = snippet.gsub /^\s*\*/, ''
            snippet = CGI.unescapeHTML(snippet)
          end
        end
        if @lang
          snippet = "\r\n" + "```" + @lang + "\r\n" + snippet + "\r\n" + "```" + "\r\n"
        else
          snippet = "<p>" + escapeJekyll(snippet) + "</p>"
        end

        return snippet
      else
        raise 'Something went wrong in Snippet'
      end
    end

    def fetchContent(url)
      cacheKey = Digest::SHA1.hexdigest(url.strip)
      cacheFile = "target/snippet_" + cacheKey + ".cache"
      unless File.exist?(cacheFile)
        File.open(cacheFile, 'w') { |file| file.write(Net::HTTP.get(URI.parse(URI.encode(url.strip)))) }
      end
      file = File.open(cacheFile, "rb")
      content = file.read
      file.close
      return content
    end

    def parse_params(markup)
      markup_split = markup.split("|").reject { |s| s.empty? }.each { |param| parse_param(param) }
    end

    def parse_param(param)
      param_split = param.split("=", 2).reject { |s| s.empty? }
      if param_split[0].casecmp("id")==0
        @id = param_split[1].strip
      else
        if param_split[0].casecmp("lang")==0
          @lang = param_split[1].strip
        else
          if param_split[0].casecmp("javadoc")==0
            @javadoc = param_split[1].strip
          else
            if param_split[0].casecmp("url")==0
              @url = param_split[1].strip
            else
              @url = param.strip
            end
          end
        end
      end
    end

    def absolute_url(url)
      p = 'struts'
      f = ''
      tail = ''
      slashIndex = url.index('/')
      unless slashIndex
        url = url.strip.gsub!('.','/')
        tail = '.java'
        if url.start_with?('org/apache/struts2/dojo/')
          p = 'struts-archive'
          f = 'plugins/struts2-dojo-plugin/src/main/java/'
        else
          f = 'core/src/main/java/'
        end
      else
        baseUrl = url[0..slashIndex]
        url = url[slashIndex+1..-1]
        if baseUrl.casecmp("struts2-tags/") == 0
          f = 'core/src/site/resources/tags/'
        else
          if url.start_with?('plugins/dojo/')
            p = 'struts-archive'
            f = 'plugins/struts2-dojo-plugin/'
            url = url[13..-1]
          end
        end
      end
      url = 'https://gitbox.apache.org/repos/asf?p=' + p + '.git;a=blob_plain;f=' + f + url + tail + ';hb=HEAD'
    end

    def escape_brackets(content)
      content.gsub(/{/,'&#x7b;').gsub(/}/, '&#x7d;')
    end

    def unescape_brackets(content)
      content.gsub!(/&(amp;)?#x7b;/, '{')
      content.gsub!(/&(amp;)?#x7d;/, '}')
      content
    end

    def escapeJekyll(content)

      # Escape markdown style code blocks

        # Escape four tab or space indented code blocks
        content = content.gsub /^((\t| {4})[^\n].+?)\n($|\S)/m do
          "#{escape_brackets $1}\n#{$3}"
        end

        # Escape in-line code backticks
        content = content.gsub /(`[^`\n]+?`)/ do
          "#{escape_brackets $1}"
        end

        # Escape in-line code double backticks
        content = content.gsub /(``[^\n]+?``)/ do
          escape_brackets $1
        end

      # Escape highlight and codeblock tag contents
      content = content.gsub /^({%\s*(codeblock|highlight).+?%})(.+?){%\s*end(codeblock|highlight)\s*%}/m do
        "#{$1}{% raw %}#{unescape_brackets $3}{% endraw %}{% end#{$4} %}"
      end

      # Escape codefenced codeblocks
      content = content.gsub /^(`{3}.+?`{3})/m do

        # Replace any raw/endraw tags inside of codefence block
        # as some of the regex above may have escaped contents
        # of the codefence block
        #
        code = unescape_brackets($1).gsub(/{% (end)?raw %}/, '')

        # Wrap codefence content in raw tags
        "{% raw %}\n#{code}\n{% endraw %}"
      end

      content
    end

  end
end

Liquid::Template.register_tag('snippet', Jekyll::Snippet)