package tempext

import (
	"bytes"
	"dbweb/lib/safe"
	"encoding/base64"
	"encoding/csv"
	"fmt"
	"log"
	"strconv"

	"github.com/linlexing/mapfun"

	htmltemp "html/template"
	"reflect"
	"strings"
	"text/template"
	"time"

	"net/url"

	"github.com/pborman/uuid"
	"github.com/russross/blackfriday"
)

var (
	FuncMap = template.FuncMap{
		"guid64": func() string {
			return base64.RawStdEncoding.EncodeToString(uuid.NewUUID())
		},
		"cell": NewCell,
		"last": func(x int, a interface{}) bool {
			return x == reflect.ValueOf(a).Len()-1
		},
		"replace": func(s, s1, s2 string) string {
			return strings.Replace(s, s1, s2, -1)
		},
		"has": func(list []string, s string) bool {
			for _, v := range list {
				if v == s {
					return true
				}
			}
			return false
		},
		"contains": func(list []string, val string) bool {
			r := false
			for _, v := range list {
				if v == val {
					r = true
					break
				}
			}
			return r
		},
		"in": func(val string, s ...string) bool {
			for _, one := range s {
				if one == val {
					return true
				}
			}
			return false
		},
		"inc": func(val int) int {
			return val + 1
		},
		"mkmap": func(vals ...interface{}) map[string]interface{} {
			r := map[string]interface{}{}
			for i := 0; i < len(vals); i += 2 {
				r[vals[i].(string)] = vals[i+1]
			}
			return r
		},
		"mkslice": func(vals ...interface{}) []interface{} {
			return vals
		},
		"decsv": func(val string) []string {
			if len(val) == 0 {
				return []string{}
			}
			rev, err := csv.NewReader(bytes.NewBufferString(val)).Read()
			if err != nil {
				log.Panic(err)
			}
			return rev
		},
		"csv": func(vals []string) string {
			bys := bytes.NewBuffer(nil)
			wrt := csv.NewWriter(bys)
			if err := wrt.Write(vals); err != nil {
				log.Panic(err)
			}
			wrt.Flush()
			return bys.String()
		},
		"pmd": func(args ...interface{}) htmltemp.HTML {
			htmlFlags := 0
			htmlFlags |= blackfriday.HTML_USE_XHTML
			htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
			htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
			htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
			htmlFlags |= blackfriday.HTML_HREF_TARGET_BLANK
			renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")

			// set up the parser
			extensions := 0
			extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
			extensions |= blackfriday.EXTENSION_TABLES
			extensions |= blackfriday.EXTENSION_FENCED_CODE
			extensions |= blackfriday.EXTENSION_AUTOLINK
			extensions |= blackfriday.EXTENSION_STRIKETHROUGH
			extensions |= blackfriday.EXTENSION_SPACE_HEADERS
			extensions |= blackfriday.EXTENSION_HEADER_IDS

			s := blackfriday.Markdown([]byte(fmt.Sprintf("%s", args...)), renderer, extensions)
			return htmltemp.HTML(s)
		},
		"ifn": func(val, nval interface{}) interface{} {
			switch tv := val.(type) {
			case nil:
				return nval
			case string:
				if len(tv) == 0 {
					return nval
				}
			default:
				rval := reflect.ValueOf(val)
				switch rval.Kind() {
				case reflect.Slice, reflect.Array, reflect.Chan, reflect.Map:
					if rval.Len() == 0 {
						return nval
					}
				}
			}
			return val
		},
		"mod": func(x, y int) int {
			return x % y
		},
		"findWhere": mapfun.FindWhere,
		"fsize": func(i int64) string {
			if i < 1024 {
				return fmt.Sprintf("%dB", i)
			} else if i < 1024*1024 {
				return fmt.Sprintf("%.2fK", float64(i)/1024)
			} else if i < 1024*1024*1024 {
				return fmt.Sprintf("%.2fM", float64(i)/1024/1024)
			} else if i < 1024*1024*1024*1024 {
				return fmt.Sprintf("%.2fG", float64(i)/1024/1024/1024)
			} else {
				return fmt.Sprintf("%.2fT", float64(i)/1024/1024/1024/1024)
			}
		},
		"keepHtml": func(str string) htmltemp.HTML {
			return htmltemp.HTML(str)
		},
		"dateStr": func(t time.Time) string {
			return t.Format("2006-01-02")
		},
		"isString": func(v interface{}) bool {
			if _, ok := v.(string); ok {
				return true
			}
			return false

		},
		"tostring": safe.String,
		"signstr":  safe.SignString,
		"include": func(name string, v interface{}) htmltemp.HTML {
			log.Panic("占位用的，实际函数会被动态替换")
			return ""
		},
		"isMap": func(v interface{}) bool {
			return reflect.ValueOf(v).Kind() == reflect.Map
		},
		"toHtml": func(v interface{}) htmltemp.HTML {
			str := htmltemp.HTMLEscapeString(safe.String(v))
			str = strings.Replace(str, "\r\n", "\n", -1)
			str = strings.Replace(str, "\n", "<br/>", -1)
			str = strings.Replace(str, " ", "&nbsp;", -1)
			str = strings.Replace(str, "\t", "&emsp;", -1)

			return htmltemp.HTML(str)
		},
		"substr": func(s string, i ...int64) string {
			if len(i) == 1 {
				return s[int(i[0]):]
			}
			return s[int(i[0]):int(i[0]+i[1])]

		},
		"hasPrefix": func(s interface{}, prev interface{}) bool {
			return strings.HasPrefix(safe.String(s), safe.String(prev))
		},
		"QueryEscape": func(s string) string {
			return url.QueryEscape(s)
		},
		"P": func(val interface{}) string {
			switch tv := val.(type) {
			case string:
				return safe.SignStringUseBySqlxNamed(tv)
			case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
				return fmt.Sprintf("%d", tv)
			case float32:
				return strconv.FormatFloat(float64(tv), 'f', -1, 32)
			case float64:
				return strconv.FormatFloat(tv, 'f', -1, 64)
			case []string: //字符串数组一定是用在in语句中
				list := []string{}
				for _, v := range tv {
					list = append(list, safe.SignStringUseBySqlxNamed(v))
				}
				return strings.Join(list, ",")
			default:
				log.Panic(fmt.Errorf("not impl P,data type :%T", val))
			}
			//不会运行到这句
			return ""
		},
		// add returns the sum of a and b.
		"add": add,
		// subtract returns the difference of b from a.
		"subtract": subtract,

		// multiply returns the product of a and b.
		"multiply": multiply,

		// divide returns the division of b from a.
		"divide": divide,
		"now":    time.Now,
		"date": func() string {
			return time.Now().Format("2006-01-02")
		},
		"datetime": func() string {
			return time.Now().Format("2006-01-02 15:04:05")
		},
	}
)

//Cell是用来模拟可变变量的，模板系统不允许修改变量的值，因此用struct来模拟
type Cell struct{ v interface{} }

func NewCell(v ...interface{}) (*Cell, error) {
	switch len(v) {
	case 0:
		return new(Cell), nil
	case 1:
		return &Cell{v[0]}, nil
	default:
		return nil, fmt.Errorf("wrong number of args: want 0 or 1, got %v", len(v))
	}
}

func (c *Cell) Set(v interface{}) *Cell { c.v = v; return c }
func (c *Cell) Get() interface{}        { return c.v }

func GetFuncMap() template.FuncMap {
	return FuncMap
}
func MustExec(tmpText string, data interface{}) string {
	out := bytes.NewBuffer(nil)
	if err := template.Must(template.New("001").Funcs(GetFuncMap()).Parse(tmpText)).Execute(out, data); err != nil {
		log.Panic(err)
	}
	return out.String()
}
