package mark2web

import (
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
	"time"

	"gitbase.de/apairon/mark2web/pkg/helper"
	"gitbase.de/apairon/mark2web/pkg/logger"
	"gitbase.de/apairon/mark2web/pkg/progress"
	"github.com/davecgh/go-spew/spew"
	"github.com/extemporalgenome/slug"
	"github.com/flosch/pongo2"
	"gopkg.in/yaml.v2"
)

// CurrentContext is current pongo2 template context
var CurrentContext *pongo2.Context

// CurrentTreeNode is current node we are on while processing template
var CurrentTreeNode *TreeNode

// NewContext returns prefilled context with some functions and variables
func NewContext() pongo2.Context {
	ctx := pongo2.Context{
		"fnRequest": RequestFn,
		"fnRender":  RenderFn,

		"AssetsPath": Config.Assets.ToPath,
		"Timestamp":  time.Now().Unix,
	}
	CurrentContext = &ctx

	return ctx
}

func (node *TreeNode) fillConfig(inBase, outBase, subDir string, conf *PathConfig) {
	inPath := inBase
	if subDir != "" {
		inPath += "/" + subDir
	}

	logger.I("reading input directory: %s", inPath)

	node.InputPath = inPath

	// read config
	newConfig := new(PathConfig)
	logger.D("looking for config.yml ...")
	configFile := inPath + "/config.yml"
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		logger.D("no config.yml found in this directory, using upper configs")
		helper.Merge(newConfig, conf)
		// remove this
		newConfig.This = ThisPathConfig{}
	} else {
		logger.D("reading config...")
		data, err := ioutil.ReadFile(configFile)
		logger.Eexit(err, "could not read file '%s'", configFile)

		err = yaml.Unmarshal(data, newConfig)
		logger.Eexit(err, "could not parse YAML file '%s'", configFile)

		logger.D("merging config with upper config")
		oldThis := newConfig.This
		helper.Merge(newConfig, conf)
		newConfig.This = oldThis

		logger.D(spew.Sdump(newConfig))
	}

	node.Config = newConfig

	// calc outDir
	stripedDir := subDir
	var regexStr *string
	if newConfig.Path != nil {
		regexStr = newConfig.Path.Strip
	}
	if regexStr != nil && *regexStr != "" {
		if regex, err := regexp.Compile(*regexStr); err != nil {
			logger.Eexit(err, "error compiling path.strip regex '%s' from '%s'", *regexStr, inBase+"/"+subDir)
		} else {
			stripedDir = regex.ReplaceAllString(stripedDir, "$1")
		}
	}

	if node.Config.This.Navname == nil {
		navname := strings.Replace(stripedDir, "_", " ", -1)
		node.Config.This.Navname = &navname
	}

	stripedDir = slug.Slug(stripedDir)
	outPath := outBase + "/" + stripedDir
	outPath = path.Clean(outPath)

	logger.I("calculated output directory: %s", outPath)
	node.OutputPath = outPath

	// handle collections
	node.handleCollections()
}

func (node *TreeNode) addSubNode(tplFilename, subDir string, navname string, ctx interface{}, dataMapKey string, body string, hideInNav bool) {
	progress.IncrTotal("content dir")
	progress.DescribeCurrent("content dir", "subdir "+node.InputPath+"/"+subDir)

	newNode := new(TreeNode)
	newNode.root = node.root

	newPathConfig := new(PathConfig)
	if dataMapKey != "" {
		if newPathConfig.Data == nil {
			newPathConfig.Data = make(helper.MapString)
		}
		// as submap in Data
		newPathConfig.Data[dataMapKey] = ctx
	} else if m, ok := ctx.(map[string]interface{}); ok {
		// direct set data
		newPathConfig.Data = m
	}

	mergedConfig := new(PathConfig)
	err := helper.Merge(mergedConfig, node.Config)
	logger.Eexit(err, "merge of path config failed")

	// dont merge Data[DataKey]
	if dataMapKey != "" {
		mergedConfig.Data[dataMapKey] = nil
	} else {
		mergedConfig.Data = make(helper.MapString)
	}
	err = helper.Merge(mergedConfig, newPathConfig)
	logger.Eexit(err, "merge of path config failed")

	newNode.fillConfig(
		node.InputPath,
		node.OutputPath,
		subDir,
		mergedConfig,
	)
	if navname != "" {
		// set after fillConfig, because it is ignored in fillConfig
		newNode.Config.This.Navname = &navname
	}

	// fake via normal file behavior
	newNode.Config.Template = &tplFilename
	newNode.InputFiles = []string{""} // empty file is special for use InputString
	indexInFile := ""
	indexOutFile := "index.html"
	if idx := newNode.Config.Index; idx != nil {
		if idx.OutputFile != nil && *idx.OutputFile != "" {
			indexOutFile = *idx.OutputFile
		}
	}
	newNode.Config.Index = &IndexConfig{
		InputFile:   &indexInFile,
		OutputFile:  &indexOutFile,
		InputString: &body,
	}
	newNode.Hidden = hideInNav

	node.Sub = append(node.Sub, newNode)
}
