func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error {
	if s.reload {
		s.rmu.Lock()
		defer s.rmu.Unlock()
		if err := s.Load(); err != nil {
			return err
		}
	}
	if tmpl := s.fromCache(filename); tmpl != nil {
		return tmpl.ExecuteWriter(getPongoContext(bindingData), w)
	}
	return fmt.Errorf("template with name %s doesn't exists in the dir", filename)
}
ExecuteWriter executes a templates and write its results to the w writer
layout here is useless.
package view
import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	stdPath "path"
	"path/filepath"
	"strings"
	"sync"
	"github.com/flosch/pongo2"
	"github.com/kataras/iris/context"
)
type (
	Value pongo2.Value
	Error pongo2.Error
	FilterFunction func(in *Value, param *Value) (out *Value, err *Error)
	Parser pongo2.Parser
	Token pongo2.Token
	INodeTag pongo2.INodeTag
	TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error)
)
func AsValue(i interface{}) *Value {
	return (*Value)(pongo2.AsValue(i))
}
func AsSafeValue(i interface{}) *Value {
	return (*Value)(pongo2.AsSafeValue(i))
}
func (value *Value) GetValue() *pongo2.Value {
	return (*pongo2.Value)(value)
}
func (error *Error) GetError() *pongo2.Error {
	return (*pongo2.Error)(error)
}
func (parser *Parser) GetParser() *pongo2.Parser {
	return (*pongo2.Parser)(parser)
}
func (token *Token) GetToken() *pongo2.Token {
	return (*pongo2.Token)(token)
}
type tDjangoAssetLoader struct {
	baseDir  string
	assetGet func(name string) ([]byte, error)
}
func (dal *tDjangoAssetLoader) Abs(base, name string) string {
	if stdPath.IsAbs(name) {
		return name
	}
	return stdPath.Join(dal.baseDir, name)
}
func (dal *tDjangoAssetLoader) Get(path string) (io.Reader, error) {
	if stdPath.IsAbs(path) {
		path = path[1:]
	}
	res, err := dal.assetGet(path)
	if err != nil {
		return nil, err
	}
	return bytes.NewBuffer(res), nil
}
type DjangoEngine struct {
	directory string
	extension string
	assetFn   func(name string) ([]byte, error)  
	namesFn   func() []string                    
	reload    bool
	rmu sync.RWMutex  
	filters map[string]FilterFunction
	globals       map[string]interface{}
	mu            sync.Mutex  
	templateCache map[string]*pongo2.Template
}
var _ Engine = &DjangoEngine{}
func Django(directory, extension string) *DjangoEngine {
	s := &DjangoEngine{
		directory:     directory,
		extension:     extension,
		globals:       make(map[string]interface{}, 0),
		filters:       make(map[string]FilterFunction, 0),
		templateCache: make(map[string]*pongo2.Template, 0),
	}
	return s
}
func (s *DjangoEngine) Ext() string {
	return s.extension
}
func (s *DjangoEngine) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) *DjangoEngine {
	s.assetFn, s.namesFn = assetFn, namesFn
	return s
}
func (s *DjangoEngine) Reload(developmentMode bool) *DjangoEngine {
	s.reload = developmentMode
	return s
}
func (s *DjangoEngine) AddFunc(funcName string, funcBody interface{}) {
	s.rmu.Lock()
	s.globals[funcName] = funcBody
	s.rmu.Unlock()
}
func (s *DjangoEngine) AddFilter(filterName string, filterBody FilterFunction) *DjangoEngine {
	return s.registerFilter(filterName, filterBody)
}
func (s *DjangoEngine) RegisterFilter(filterName string, filterBody FilterFunction) *DjangoEngine {
	return s.registerFilter(filterName, filterBody)
}
func (s *DjangoEngine) registerFilter(filterName string, filterBody FilterFunction) *DjangoEngine {
	fn := pongo2.FilterFunction(func(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
		theOut, theErr := filterBody((*Value)(in), (*Value)(param))
		return (*pongo2.Value)(theOut), (*pongo2.Error)(theErr)
	})
	pongo2.RegisterFilter(filterName, fn)
	return s
}
func (s *DjangoEngine) RegisterTag(tagName string, parserFn TagParser) error {
	fn := func(doc *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser) (pongo2.INodeTag, *pongo2.Error) {
		t, err := parserFn((*Parser)(doc), (*Token)(start), (*Parser)(arguments))
		return t, (*pongo2.Error)(err)
	}
	return pongo2.RegisterTag(tagName, fn)
}
func (s *DjangoEngine) Load() error {
	if s.assetFn != nil && s.namesFn != nil {
		return s.loadAssets()
	}
	dir, err := filepath.Abs(s.directory)
	if err != nil {
		return err
	}
	s.directory = dir
	return s.loadDirectory()
}
func (s *DjangoEngine) loadDirectory() (templateErr error) {
	dir, extension := s.directory, s.extension
	fsLoader, err := pongo2.NewLocalFileSystemLoader(dir)  
	if err != nil {
		return err
	}
	set := pongo2.NewSet("", fsLoader)
	set.Globals = getPongoContext(s.globals)
	s.mu.Lock()
	defer s.mu.Unlock()
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if info == nil || info.IsDir() {
		} else {
			rel, err := filepath.Rel(dir, path)
			if err != nil {
				templateErr = err
				return err
			}
			ext := filepath.Ext(rel)
			if ext == extension {
				buf, err := ioutil.ReadFile(path)
				if err != nil {
					templateErr = err
					return err
				}
				name := filepath.ToSlash(rel)
				s.templateCache[name], templateErr = set.FromString(string(buf))
				if templateErr != nil {
					return templateErr
				}
			}
		}
		return nil
	})
	return
}
func (s *DjangoEngine) loadAssets() error {
	virtualDirectory, virtualExtension := s.directory, s.extension
	assetFn, namesFn := s.assetFn, s.namesFn
	set := pongo2.NewSet("", &tDjangoAssetLoader{baseDir: s.directory, assetGet: s.assetFn})
	set.Globals = getPongoContext(s.globals)
	if len(virtualDirectory) > 0 {
		if virtualDirectory[0] == '.' {  
			virtualDirectory = virtualDirectory[1:]
		}
		if virtualDirectory[0] == '/' || virtualDirectory[0] == os.PathSeparator {  
			virtualDirectory = virtualDirectory[1:]
		}
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	var templateErr error
	names := namesFn()
	for _, path := range names {
		if !strings.HasPrefix(path, virtualDirectory) {
			continue
		}
		rel, err := filepath.Rel(virtualDirectory, path)
		if err != nil {
			templateErr = err
			return err
		}
		ext := filepath.Ext(rel)
		if ext == virtualExtension {
			buf, err := assetFn(path)
			if err != nil {
				templateErr = err
				return err
			}
			name := filepath.ToSlash(rel)
			s.templateCache[name], err = set.FromString(string(buf))
			if err != nil {
				templateErr = err
				return err
			}
		}
	}
	return templateErr
}
func getPongoContext(templateData interface{}) pongo2.Context {
	if templateData == nil {
		return nil
	}
	if contextData, isPongoContext := templateData.(pongo2.Context); isPongoContext {
		return contextData
	}
	if contextData, isContextViewData := templateData.(context.Map); isContextViewData {
		return pongo2.Context(contextData)
	}
	return templateData.(map[string]interface{})
}
func (s *DjangoEngine) fromCache(relativeName string) *pongo2.Template {
	s.mu.Lock()
	tmpl, ok := s.templateCache[relativeName]
	if ok {
		s.mu.Unlock()
		return tmpl
	}
	s.mu.Unlock()
	return nil
}