package util

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"sync"
)

var lock sync.Mutex

//单条tag
type Tag struct {
	Key       string //原始的key
	Value     string //
	Weight    int    //权重
	TagReg    *regexp.Regexp
	IsInvalid bool //是否打上标签
}
type Tags []*Tag //
//tag文件
type TagFile struct {
	Name  string //
	Type  string // reg
	Items Tags   //数据
}

//实现排序接口
func (s Tags) Len() int {
	return len(s)
}
func (s Tags) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s Tags) Less(i, j int) bool {
	return strings.Compare(s[i].Value, s[j].Value) < 1
}

//
//var TagdbTable = make(map[string]*TagFile)
var TagdbTable = sync.Map{}
var SiteTagdbTable = sync.Map{}
var blocktagdb = make(map[string]*TagFile)

//加载
func (t *TagFile) Load(path string) {
	fi, _ := os.Open(path)
	info, _ := fi.Stat()
	name := info.Name()
	bs, _ := ioutil.ReadAll(fi)
	fi.Close()
	lines := strings.Split(strings.Replace(string(bs), "\r", "", -1), "\n")
	name = name[:strings.Index(name, ".")]
	//排序
	t.Items = make([]*Tag, len(lines))
	for k, v := range lines {
		t.Items[k] = &Tag{"", v, 0 - k, nil, false}
	}
	sort.Sort(t.Items)
	t.Name = name
}

//快速匹配
func (t *TagFile) Match(src string) (bool, *Tag) {
	pos := binarysearch(t.Items, 0, len(t.Items)-1, src, t.Type)
	if pos < 0 {
		return false, nil
	} else {
		return true, t.Items[pos]
	}

}

//2分查找，返回位置
func binarysearch(arr Tags, begin, end int, key, types string) int {
	if begin > end {
		return -1
	}
	mid := (begin + end) / 2
	if mid < 0 {
		return -1
	}
	if types == "reg" {
		if arr[mid].TagReg.MatchString(key) {
			return mid
		} else if arr[mid].Value > key {
			return binarysearch(arr, begin, mid-1, key, types)
		} else {
			return binarysearch(arr, mid+1, end, key, types)
		}
	} else {
		if arr[mid].TagReg != nil && arr[mid].TagReg.MatchString(key) {
			return mid
		} else if arr[mid].Value == key {
			return mid
		} else if arr[mid].Value > key {
			return binarysearch(arr, begin, mid-1, key, types)
		} else {
			return binarysearch(arr, mid+1, end, key, types)
		}
	}
}

//加载指定目录下的所有文件
func LoadTagDb(respath string) {
	filepath.Walk(respath, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		if strings.HasSuffix(info.Name(), ".txt") {
			t := &TagFile{}
			t.Load(path)
			//tagdb数据源从库里查
			//			if strings.HasSuffix(respath, "/tagdb") {
			//				TagdbTable[t.Name] = t
			//			}
			if strings.HasSuffix(respath, "/blocktagdb") {
				blocktagdb[t.Name] = t
			}
		}
		return nil
	})
}

//取得匹配
func GetTags(src string, isSite bool, codeSite string) Tags {
	return GetAppointTags(src, nil, isSite, codeSite)
}

//根据指定的标签库取得匹配
func GetAppointTags(src string, array []string, isSite bool, codeSite string) Tags {
	src = TrimLRAll(src, "")
	ret := make(Tags, 0)
	m := map[string]bool{}
	if array != nil && len(array) > 0 {
		for _, v := range array {
			m[v] = true
		}
	}
	//lock.Lock()
	//for k, v := range TagdbTable {
	//	if len(m) > 0 && !m[k] {
	//		continue
	//	}
	//	if ok, tag := v.Match(src); ok {
	//		ret = append(ret, &Tag{src, v.Name, tag.Weight, tag.TagReg, false})
	//	}
	//}
	//lock.Unlock()
	if isSite {
		value, ok := SiteTagdbTable.Load(codeSite)
		if !ok{
			return ret
		}
		if vvv,ok2 := value.(sync.Map);ok2{
			vvv.Range(func(key, value interface{}) bool {
				if len(m) > 0 && !m[fmt.Sprint(key)] {
					return true
				}
				if v,ok := value.(*TagFile);ok {
					if ok, tag := v.Match(src); ok {
						ret = append(ret, &Tag{src, v.Name, tag.Weight, tag.TagReg, false})
					}
				}
				return true
			})
		}
	}else {
		TagdbTable.Range(func(key, value interface{}) bool {
			if len(m) > 0 && !m[fmt.Sprint(key)] {
				return true
			}
			if v,ok := value.(*TagFile);ok {
				if ok, tag := v.Match(src); ok {
					ret = append(ret, &Tag{src, v.Name, tag.Weight, tag.TagReg, false})
				}
			}
			return true
		})
	}
	//sort.Sort(ret)
	return ret
}

//取得匹配
func GetBlockTags(src string) Tags {
	src = TrimLRAll(src, "")
	ret := make(Tags, 0)
	for _, v := range blocktagdb {
		if ok, tag := v.Match(src); ok {
			ret = append(ret, &Tag{src, v.Name, tag.Weight, nil, false})
		}
	}
	//sort.Sort(ret)
	return ret
}
