package markdown

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"fmt"

	"github.com/beyondblog/k8s-web-terminal/Godeps/_workspace/src/github.com/kataras/iris/config"
	"github.com/beyondblog/k8s-web-terminal/Godeps/_workspace/src/github.com/microcosm-cc/bluemonday"
	"github.com/beyondblog/k8s-web-terminal/Godeps/_workspace/src/github.com/russross/blackfriday"
)

// Supports RAW markdown only, no context binding or layout, to use dynamic markdown with other template engine use the context.Markdown/MarkdownString

type (
	// Engine the jade engine
	Engine struct {
		Config        *config.Template
		templateCache map[string][]byte
		mu            sync.Mutex
	}
)

// New creates and returns a Pongo template engine
func New(c config.Template) *Engine {
	return &Engine{Config: &c, templateCache: make(map[string][]byte)}
}

// BuildTemplates builds the templates
func (e *Engine) BuildTemplates() error {
	if e.Config.Asset == nil || e.Config.AssetNames == nil {
		return e.buildFromDir()
	}
	return e.buildFromAsset()

}

func (e *Engine) buildFromDir() (templateErr error) {
	if e.Config.Directory == "" {
		return nil //we don't return fill error here(yet)
	}
	dir := e.Config.Directory

	// Walk the supplied directory and compile any files that match our extension list.
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

		if info == nil || info.IsDir() {
			return nil
		}

		rel, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}

		ext := ""
		if strings.Index(rel, ".") != -1 {
			ext = filepath.Ext(rel)
		}

		for _, extension := range e.Config.Extensions {
			if ext == extension {
				buf, err := ioutil.ReadFile(path)
				if err != nil {
					templateErr = err
					break
				}

				buf = blackfriday.MarkdownCommon(buf)
				if e.Config.Markdown.Sanitize {
					buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
				}

				if err != nil {
					templateErr = err
					break
				}
				name := filepath.ToSlash(rel)
				e.templateCache[name] = buf
				break
			}
		}
		return nil
	})

	return nil
}

func (e *Engine) buildFromAsset() error {
	var templateErr error
	dir := e.Config.Directory
	for _, path := range e.Config.AssetNames() {
		if !strings.HasPrefix(path, dir) {
			continue
		}

		rel, err := filepath.Rel(dir, path)
		if err != nil {
			panic(err)
		}

		ext := ""
		if strings.Index(rel, ".") != -1 {
			ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
		}

		for _, extension := range e.Config.Extensions {
			if ext == extension {

				buf, err := e.Config.Asset(path)
				if err != nil {
					templateErr = err
					break
				}
				b := blackfriday.MarkdownCommon(buf)
				if e.Config.Markdown.Sanitize {
					b = bluemonday.UGCPolicy().SanitizeBytes(b)
				}
				name := filepath.ToSlash(rel)
				e.templateCache[name] = b
				break
			}
		}
	}
	return templateErr
}

func (e *Engine) fromCache(relativeName string) []byte {
	e.mu.Lock()

	tmpl, ok := e.templateCache[relativeName]

	if ok {
		e.mu.Unlock() // defer is slow
		return tmpl
	}
	e.mu.Unlock() // defer is slow
	return nil
}

// ExecuteWriter executes a templates and write its results to the out writer
// layout here is useless
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
	if tmpl := e.fromCache(name); tmpl != nil {
		_, err := out.Write(tmpl)
		return err
	}

	return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
}
