// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mmark converts Markdown to HTML using MMark v1.
package mmark

import (
	"github.com/gohugoio/hugo/markup/blackfriday/blackfriday_config"
	"github.com/gohugoio/hugo/markup/converter"
	"github.com/miekg/mmark"
)

// Provider is the package entry point.
var Provider converter.ProviderProvider = provider{}

type provider struct {
}

func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error) {
	defaultBlackFriday := cfg.MarkupConfig.BlackFriday
	defaultExtensions := getMmarkExtensions(defaultBlackFriday)

	return converter.NewProvider("mmark", func(ctx converter.DocumentContext) (converter.Converter, error) {
		b := defaultBlackFriday
		extensions := defaultExtensions

		if ctx.ConfigOverrides != nil {
			var err error
			b, err = blackfriday_config.UpdateConfig(b, ctx.ConfigOverrides)
			if err != nil {
				return nil, err
			}
			extensions = getMmarkExtensions(b)
		}

		return &mmarkConverter{
			ctx:        ctx,
			b:          b,
			extensions: extensions,
			cfg:        cfg,
		}, nil
	}), nil

}

type mmarkConverter struct {
	ctx        converter.DocumentContext
	extensions int
	b          blackfriday_config.Config
	cfg        converter.ProviderConfig
}

func (c *mmarkConverter) Convert(ctx converter.RenderContext) (converter.Result, error) {
	r := getHTMLRenderer(c.ctx, c.b, c.cfg)
	return mmark.Parse(ctx.Src, r, c.extensions), nil
}

func getHTMLRenderer(
	ctx converter.DocumentContext,
	cfg blackfriday_config.Config,
	pcfg converter.ProviderConfig) mmark.Renderer {

	var (
		flags      int
		documentID string
	)

	documentID = ctx.DocumentID

	renderParameters := mmark.HtmlRendererParameters{
		FootnoteAnchorPrefix:       cfg.FootnoteAnchorPrefix,
		FootnoteReturnLinkContents: cfg.FootnoteReturnLinkContents,
	}

	if documentID != "" && !cfg.PlainIDAnchors {
		renderParameters.FootnoteAnchorPrefix = documentID + ":" + renderParameters.FootnoteAnchorPrefix
	}

	htmlFlags := flags
	htmlFlags |= mmark.HTML_FOOTNOTE_RETURN_LINKS

	return &mmarkRenderer{
		BlackfridayConfig: cfg,
		Config:            pcfg,
		Renderer:          mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters),
	}

}

func getMmarkExtensions(cfg blackfriday_config.Config) int {
	flags := 0
	flags |= mmark.EXTENSION_TABLES
	flags |= mmark.EXTENSION_FENCED_CODE
	flags |= mmark.EXTENSION_AUTOLINK
	flags |= mmark.EXTENSION_SPACE_HEADERS
	flags |= mmark.EXTENSION_CITATION
	flags |= mmark.EXTENSION_TITLEBLOCK_TOML
	flags |= mmark.EXTENSION_HEADER_IDS
	flags |= mmark.EXTENSION_AUTO_HEADER_IDS
	flags |= mmark.EXTENSION_UNIQUE_HEADER_IDS
	flags |= mmark.EXTENSION_FOOTNOTES
	flags |= mmark.EXTENSION_SHORT_REF
	flags |= mmark.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
	flags |= mmark.EXTENSION_INCLUDE

	for _, extension := range cfg.Extensions {
		if flag, ok := mmarkExtensionMap[extension]; ok {
			flags |= flag
		}
	}
	return flags
}

var mmarkExtensionMap = map[string]int{
	"tables":                 mmark.EXTENSION_TABLES,
	"fencedCode":             mmark.EXTENSION_FENCED_CODE,
	"autolink":               mmark.EXTENSION_AUTOLINK,
	"laxHtmlBlocks":          mmark.EXTENSION_LAX_HTML_BLOCKS,
	"spaceHeaders":           mmark.EXTENSION_SPACE_HEADERS,
	"hardLineBreak":          mmark.EXTENSION_HARD_LINE_BREAK,
	"footnotes":              mmark.EXTENSION_FOOTNOTES,
	"noEmptyLineBeforeBlock": mmark.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK,
	"headerIds":              mmark.EXTENSION_HEADER_IDS,
	"autoHeaderIds":          mmark.EXTENSION_AUTO_HEADER_IDS,
}
