// extractudp
package extract

import (
	"encoding/json"
	"fmt"
	db "jy/mongodbutil"
	ju "jy/util"
	mu "mfw/util"
	"net"
	qu "qfw/util"
	"strings"
	"sync"
	"time"

	log "github.com/donnie4w/go-logger/logger"
	"gopkg.in/mgo.v2/bson"
)

var Udpclient mu.UdpClient //udp对象
var nextNodes []map[string]interface{}
var IsExtStop bool
var udplock sync.Mutex

// 新增机器节点
func ExtractUdpUpdateMachine() {
	machine := *qu.ObjToMap(ju.Config["udpmachine"])
	if len(machine) == 0 || machine == nil {
		return
	}
	skey := fmt.Sprintf("%s:%d:%s", machine["addr"], qu.IntAll(machine["port"]), machine["stype"])
	machine["skey"] = skey
	db.Mgo.Update("extract_control_center", map[string]interface{}{"skey": skey}, machine, true, false)
}

// udp通知抽取
func ExtractUdp() {
	nextNodes = ju.IsMarkInterfaceMap(ju.Config["nextNode"])
	Udpclient = mu.UdpClient{Local: ":" + qu.ObjToString(ju.Config["udpport"]), BufSize: 1024}
	Udpclient.Listen(processUdpMsg)
}

func processUdpMsg(act byte, data []byte, ra *net.UDPAddr) {
	switch act {
	case mu.OP_TYPE_DATA:
		var rep map[string]interface{}
		err := json.Unmarshal(data, &rep)
		if err != nil {
			log.Debug(err)
		} else {
			stype, _ := rep["stype"].(string)
			if stype == "distributed" { //分布式抽取分支
				go Udpclient.WriteUdp([]byte("发送分布式抽取分支"+qu.ObjToString(rep["ip"])+"udpok"), mu.OP_NOOP, ra)
				InstanceId := qu.ObjToString(rep["InstanceId"])
				db.Mgo.Update("ecs", `{"InstanceId":"`+InstanceId+`"}`,
					map[string]interface{}{
						"$set": map[string]interface{}{
							"extstatus": "running",
						},
					}, true, false)
				ExtractByUdp("", "", ra, qu.ObjToString(rep["InstanceId"]), qu.ObjToString(rep["ip"]))
				db.Mgo.Update("ecs", `{"InstanceId":"`+InstanceId+`"}`,
					map[string]interface{}{
						"$set": map[string]interface{}{
							"extstatus": "ok",
						},
					}, true, false)
				log.Debug("分布式抽取完成,可以释放esc实例", qu.ObjToString(rep["ip"]))
			} else if stype == "stop_extract" {
				IsExtStop = true
			} else if stype == "heart_extract" {
				skey, _ := rep["skey"].(string)
				Udpclient.WriteUdp([]byte(skey), mu.OP_NOOP, ra)
			} else if stype == "update_rule" {
				ju.IsUpdateRule = true
			} else if stype == "monitor" {
				log.Debug("收到监测......")
				Udpclient.WriteUdp([]byte("monitor ok"), mu.OP_NOOP, ra)
			} else {
				nomal, _ := rep["nomal"].(string)
				file, _ := rep["file"].(string)
				fileArr := strings.Split(file, "-")
				nomalArr := strings.Split(nomal, "-")
				if len(fileArr) != 2 || len(nomalArr) != 2 || nomal == "" || file == "" {
					log.Debug("接收upd异常...")
					go Udpclient.WriteUdp([]byte("接收upd异常..."), mu.OP_NOOP, ra)
				} else {
					go Udpclient.WriteUdp([]byte("ok"), mu.OP_NOOP, ra)
					udplock.Lock()
					ju.TaskUdpList = append(ju.TaskUdpList, map[string]map[string]string{
						"nomal": {"sid": nomalArr[0], "eid": nomalArr[1]},
						"file":  {"sid": fileArr[0], "eid": fileArr[1]},
					})
					log.Debug("udp收到任务...数量：", len(ju.TaskUdpList))
					udplock.Unlock()
				}
			}
		}
	case mu.OP_NOOP: //下个节点回应
		log.Debug(string(data))
	}
}

var ext *ExtractTask

// 根据id区间抽取-udp模式
func ExtractByUdp(sid, eid string, ra *net.UDPAddr, instanceId ...string) {
	defer qu.Catch()
	if ext == nil {
		ext = nil
		ext = &ExtractTask{}
		ext.Id = qu.ObjToString(ju.Config["udptaskid"])
		ext.InitTaskInfo()
		ext.TaskInfo.FDB = db.MgoFactory(3, 5, 600, ext.TaskInfo.FromDbAddr, ext.TaskInfo.FromDB)
		ext.TaskInfo.TDB = db.MgoFactory(3, 5, 600, ext.TaskInfo.ToDbAddr, ext.TaskInfo.ToDB)
		ext.InitSite()
		ext.InitRulePres()
		ext.InitRuleBacks(false)
		ext.InitRuleBacks(true)
		ext.InitRuleCore(false)
		ext.InitRuleCore(true)
		ext.InitBlockRule()
		ext.InitPkgCore()
		ext.InitTag(false)
		ext.InitTag(true)
		ext.InitClearFn(false)
		ext.InitClearFn(true)
		ext.Lock()
		if ext.IsExtractCity { //版本上控制是否开始城市抽取
			ext.InitCityInfo()
			ext.InitAreaCode()
			ext.InitPostCode()
		}
		ext.Unlock()
		//质量审核
		ext.InitAuditFields()
		ext.InitAuditRule()
		ext.InitAuditClass()
		ext.InitAuditRecogField()
		//品牌抽取是否开启
		ju.IsBrandGoods, _ = ju.Config["brandgoods"].(bool)
		ext.ResultSave(true)
		ext.BidSave(true)
		ext.InitFile()
		ext.IsRun = true
		ext.BidTotal = 0
	} else {
		if ju.IsUpdateRule {
			ju.IsUpdateRule = false
			log.Debug("每天更新一次规则......")
			//规则重置
			ext.InitSite()
			ext.InitRulePres()
			ext.InitRuleBacks(false)
			ext.InitRuleBacks(true)
			ext.InitRuleCore(false)
			ext.InitRuleCore(true)
			ext.InitBlockRule()
			ext.InitPkgCore()
			ext.InitTag(false)
			ext.InitTag(true)
			ext.InitClearFn(false)
			ext.InitClearFn(true)
			//地域重置
			ext.Lock()
			if ext.IsExtractCity { //版本上控制是否开始城市抽取
				ext.InitCityInfo()
				ext.InitAreaCode()
				ext.InitPostCode()
			}
			ext.Unlock()
		}
		ext.BidTotal = 0
	}
	index := 0
	if len(instanceId) > 0 { //分布式抽取进度
		go Udpclient.WriteUdp([]byte(fmt.Sprintf("IP=%s,数据接收成功", instanceId[1])), mu.OP_NOOP, ra)
		for {
			tsk, b := db.Mgo.FindOne("esctask", `{"state":{"$lt":1}}`)
			if tsk != nil && !b {
				break
			}
			db.Mgo.UpdateById("esctask", (*tsk)["_id"], map[string]interface{}{
				"$set": map[string]interface{}{
					"InstanceId": instanceId[0],
					"state":      1,
					"runtime":    time.Now().Format(qu.Date_Full_Layout),
				},
			})
			query := bson.M{"_id": bson.M{"$gte": bson.ObjectIdHex(qu.ObjToString((*tsk)["sid"])), "$lte": bson.ObjectIdHex(qu.ObjToString((*tsk)["eid"]))}}
			count1 := ext.TaskInfo.FDB.Count(ext.TaskInfo.FromColl, query)
			count2 := ext.TaskInfo.FDB.Count(ext.TaskInfo.FromColl+"_back", query)
			log.Debug("timestr", (*tsk)["timestr"], "count", count1+count2)
			list, _ := ext.TaskInfo.FDB.Find(ext.TaskInfo.FromColl, query, nil, Fields, false, -1, -1)
			for _, v := range *list {
				if spidercode[qu.ObjToString(v["spidercode"])] { //临时开标记录
					log.Debug(index, qu.BsonIdToSId(v["_id"]), "//开标记录")
					continue
				}
				var j, jf *ju.Job
				var isSite bool
				if ext.IsFileField && (v["projectinfo"] != nil || v["attach_text"] != nil) {
					v["isextFile"] = true
					j, jf, isSite = ext.PreInfo(v)
				} else {
					j, _, isSite = ext.PreInfo(v)
				}
				go ext.ExtractProcess(j, jf, isSite)
				index++
				ext.TaskInfo.ProcessPool <- true
			}
			list2, _ := ext.TaskInfo.FDB.Find(ext.TaskInfo.FromColl+"_back", query, nil, Fields, false, -1, -1)
			for _, v := range *list2 {
				if spidercode[qu.ObjToString(v["spidercode"])] {
					log.Debug(index, qu.BsonIdToSId(v["_id"]), "//开标记录")
					continue
				}
				var j, jf *ju.Job
				var isSite bool
				if ext.IsFileField && (v["projectinfo"] != nil || v["attach_text"] != nil) {
					v["isextFile"] = true
					j, jf, isSite = ext.PreInfo(v)
				} else {
					j, _, isSite = ext.PreInfo(v)
				}
				go ext.ExtractProcess(j, jf, isSite)
				index++
				ext.TaskInfo.ProcessPool <- true
			}
			db.Mgo.UpdateById("esctask", (*tsk)["_id"], map[string]interface{}{
				"$set": map[string]interface{}{
					"InstanceId": instanceId[0],
					"oktime":     time.Now().Format(qu.Date_Full_Layout),
					"state":      1,
				},
			})
			db.Mgo.Update("ecs", `{"InstanceId":"`+instanceId[0]+`"}`,
				map[string]interface{}{
					"$inc": map[string]interface{}{
						"totalnum": count1 + count2,
						"step":     1,
					},
				}, true, false)
		}
		log.Debug("抽取完成", "index：", index, "bidtotal：", ext.BidTotal)
	} else {
		//普通抽取
		query := bson.M{"_id": bson.M{"$gt": bson.ObjectIdHex(sid), "$lte": bson.ObjectIdHex(eid)}}
		count := ext.TaskInfo.FDB.Count(ext.TaskInfo.FromColl, query)
		log.Debug("查询条件为:", query, "查询条数:", count)
		pageNum := (count + PageSize - 1) / PageSize
		limit := PageSize
		if count < PageSize {
			limit = count
		}
		wg := sync.WaitGroup{}
		for i := 0; i < pageNum; i++ {
			query = bson.M{"_id": bson.M{"$gt": bson.ObjectIdHex(sid)}}
			fmt.Printf("page=%d,query=%v\n", i+1, query)
			list, _ := ext.TaskInfo.FDB.Find(ext.TaskInfo.FromColl, query, `{"_id":1}`, Fields, false, 0, limit)
			for _, v := range *list {
				if IsExtStop {
					break
				}
				if spidercode[qu.ObjToString(v["spidercode"])] { //临时开标记录
					log.Debug(index, qu.BsonIdToSId(v["_id"]), "//开标记录")
					continue
				}
				_id := qu.BsonIdToSId(v["_id"])
				var j, jf *ju.Job
				var isSite bool
				if ext.IsFileField && (v["projectinfo"] != nil || v["attach_text"] != nil) {
					v["isextFile"] = true
					j, jf, isSite = ext.PreInfo(v)
				} else {
					j, _, isSite = ext.PreInfo(v)
				}
				ext.TaskInfo.ProcessPool <- true
				wg.Add(1)
				go func(wg *sync.WaitGroup, j, jf *ju.Job) {
					defer wg.Done()
					ext.ExtractProcess(j, jf, isSite)
				}(&wg, j, jf)
				index++
				if index%1000 == 0 {
					log.Debug("index：", index, ",页码：", i+1, ",_id：", _id)
				}
				sid = _id
				if sid >= eid {
					break
				}
			}
		}
		wg.Wait()
		ext.BidSave(false)
		log.Debug("抽取完成,", "count：", count, ",index：", index, ",bidtotal：", ext.BidTotal, ",eid：", eid)
	}
}

func ExtractByUdpPre(n_sid, n_eid string, f_sid, f_eid string) {
	defer qu.Catch()
	if ext == nil {
		ext = nil
		ext = &ExtractTask{}
		ext.Id = qu.ObjToString(ju.Config["udptaskid"])
		ext.InitTaskInfo()
		ext.TaskInfo.FDB = db.MgoFactory(3, 5, 600, ext.TaskInfo.FromDbAddr, ext.TaskInfo.FromDB)
		ext.TaskInfo.TDB = db.MgoFactory(3, 5, 600, ext.TaskInfo.ToDbAddr, ext.TaskInfo.ToDB)
		ext.InitSite()
		ext.InitRulePres()
		ext.InitRuleBacks(false)
		ext.InitRuleBacks(true)
		ext.InitRuleCore(false)
		ext.InitRuleCore(true)
		ext.InitBlockRule()
		ext.InitPkgCore()
		ext.InitTag(false)
		ext.InitTag(true)
		ext.InitClearFn(false)
		ext.InitClearFn(true)
		ext.Lock()
		if ext.IsExtractCity { //版本上控制是否开始城市抽取
			ext.InitCityInfo()
			ext.InitAreaCode()
			ext.InitPostCode()
		}
		ext.Unlock()
		//质量审核
		ext.InitAuditFields()
		ext.InitAuditRule()
		ext.InitAuditClass()
		ext.InitAuditRecogField()
		//品牌抽取是否开启
		ju.IsBrandGoods, _ = ju.Config["brandgoods"].(bool)
		ext.ResultSave(true)
		ext.BidSave(true)
		ext.InitFile()
		ext.IsRun = true
		ext.BidTotal = 0
	} else {
		ext.BidTotal = 0
	}
	query1 := bson.M{"_id": bson.M{"$gt": bson.ObjectIdHex(n_sid), "$lte": bson.ObjectIdHex(n_eid)}}
	query2 := bson.M{"_id": bson.M{"$gt": bson.ObjectIdHex(f_sid), "$lte": bson.ObjectIdHex(f_eid)}}
	count1 := ext.TaskInfo.FDB.Count("bidding_nomal", query1)
	count2 := ext.TaskInfo.FDB.Count("bidding_file", query2)
	log.Debug("待抽取数量：", count1+count2)
	list1, _ := ext.TaskInfo.FDB.Find("bidding_nomal", query1, nil, Fields, false, -1, -1)
	list2, _ := ext.TaskInfo.FDB.Find("bidding_file", query2, nil, Fields, false, -1, -1)
	new_list := []map[string]interface{}{}
	for _, v := range *list1 {
		v["fromtable"] = "bidding_nomal"
		new_list = append(new_list, v)
	}
	for _, v := range *list2 {
		v["fromtable"] = "bidding_file"
		new_list = append(new_list, v)
	}
	now_time := time.Now().Unix()
	total := 0
	wg_mgo := sync.WaitGroup{}
	for _, v := range new_list {
		if total%1000 == 0 {
			log.Debug("cur index ：", total, v["_id"])
		}
		total++
		if spidercode[qu.ObjToString(v["spidercode"])] {
			continue
		}
		if qu.ObjToString(v["biddingid"]) != "" {
			continue
		}
		ext.TaskInfo.ProcessPool <- true
		wg_mgo.Add(1)
		go func(wg_mgo *sync.WaitGroup, v map[string]interface{}) {
			defer wg_mgo.Done()
			var j, jf *ju.Job
			var isSite bool
			j, _, isSite = ext.PreInfo(v)
			ext.ExtractProcess(j, jf, isSite)
		}(&wg_mgo, v)
	}
	wg_mgo.Wait()
	log.Debug("抽取完成：", total, ",耗时：", time.Now().Unix()-now_time)
}

// 中标预测信息抽取，ossid为附件识别后的id
var exF *ExtractTask
