package main

import (
	"runtime/debug"
	"strconv"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	"lc/common/models"
	"lc/common/util"
)

// DeviceAlarmId 开关灯,离线,上线事件
var DeviceAlarmId = "device_alarm_id"

var _EventMgrOnce sync.Once
var _EventMgrSingle *EventMgr

func GetEventMgr() *EventMgr {
	_EventMgrOnce.Do(func() {
		_EventMgrSingle = &EventMgr{
			EventQueue:   util.NewQueue(10000),
			MapAlarm:     make(map[string]int64),
			MapLampEvent: make(map[string]int64),
		}
	})
	return _EventMgrSingle
}

type EventObject struct {
	ID        string
	EventType models.EventType
	Time      time.Time
	Value     float64
}

type EventMgr struct {
	EventQueue   *util.MlQueue
	MapAlarm     map[string]int64
	MapLampEvent map[string]int64
}

func (o *EventMgr) PushEvent(eo *EventObject) {
	if eo.ID == "" {
		logrus.Debugf("事件的设备ID为空:%s", string(debug.Stack()))
	}
	o.EventQueue.Put(eo)
}

func (o *EventMgr) Handler(args ...interface{}) interface{} {
	defer func() {
		if err := recover(); err != nil {
			gopool.Add(o.Handler, args)
			logrus.Errorf("EventMgr.Handler发生异常:%s", string(debug.Stack()))
		}
	}()
	for {
		msg, ok, _ := o.EventQueue.Get()
		if !ok {
			time.Sleep(200 * time.Millisecond)
			continue
		}

		eo, ok := msg.(*EventObject)
		if !ok {
			continue
		}
		//事件保存
		o.SaveEventObject(eo)
		//在线离线告警处理
		if eo.EventType == models.ET_ONLINE || eo.EventType == models.ET_OFFLINE {
			o.CheckOffLine(eo)
		} else if eo.EventType == models.ET_ONLAMP || eo.EventType == models.ET_OFFLAMP {
			o.CheckLampOnOff(eo)
		}
	}
}

func (o *EventMgr) SaveEventObject(eo *EventObject) {
	oo := models.DeviceEvents{DID: eo.ID, EType: eo.EventType, Time: eo.Time}
	if err := models.G_db.Create(&oo).Error; err != nil {
		logrus.Errorf("设备[%s]事件入库失败:%s", eo.ID, err.Error())
	}
}

func (o *EventMgr) CheckOffLine(eo *EventObject) {
	if eo.EventType == models.ET_ONLINE { //上线事件
		ai, ok := o.MapAlarm[eo.ID]
		if !ok {
			if str, err := redisCltRawData.HGet(DeviceAlarmId, eo.ID).Result(); err == nil {
				if id, err := strconv.Atoi(str); err == nil {
					ai = int64(id)
				}
			}
		}
		if ai > 0 {
			oo := models.DeviceAlarm{ID: ai, TEnd: eo.Time, EValue: float32(0)}
			if err := oo.Update(); err != nil {
				logrus.Errorf("更新告警[%d]信息失败:%s", ai, err.Error())
			}
			delete(o.MapAlarm, eo.ID)
			if err := redisCltRawData.HDel(DeviceAlarmId, eo.ID).Err(); err != nil {
				logrus.Errorf("更新告警[%d]信息失败:%s", ai, err.Error())
			}
		}
	} else if eo.EventType == models.ET_OFFLINE { //掉线事件
		oo := models.DeviceAlarm{
			DID:       eo.ID,
			TStart:    eo.Time,
			Threshold: 0,
			SValue:    0,
			Content:   "离线",
			AlarmType: 0,
			Level:     2, //设备离线为严重告警
		}
		if err := models.G_db.Create(&oo).Error; err != nil {
			logrus.Errorf("告警信息[%v]入库失败:%s", eo, err.Error())
		} else {
			o.MapAlarm[eo.ID] = oo.ID
			if err := redisCltRawData.HSet(DeviceAlarmId, eo.ID, oo.ID).Err(); err != nil {
				logrus.Errorf("设备[%s]告警数据[%d]缓存失败:%s", eo.ID, oo.ID, err.Error())
			}
		}
	}
}

func (o *EventMgr) CheckLampOnOff(eo *EventObject) {
	if eo.EventType == models.ET_OFFLAMP { //关灯事件
		RID, ok := o.MapLampEvent[eo.ID]
		if !ok {
			if str, err := redisCltRawData.HGet(LampEventId, eo.ID).Result(); err == nil {
				if id, err := strconv.Atoi(str); err == nil {
					RID = int64(id)
				}
			}
		}
		if RID > 0 {
			oo := models.DeviceLampEvents{
				ID:   uint(RID),
				DID:  eo.ID,
				TEnd: eo.Time,
			}
			if err := oo.Update(); err != nil {
				logrus.Errorf("灯控[%s]关灯数据[ID=%d]入库失败:%s", eo.ID, RID, err.Error())
			}
			delete(o.MapLampEvent, eo.ID)

			if err := oo.Split(); err != nil {
				logrus.Errorf("分割开灯事件[%d]失败:%s", oo.ID, err.Error())
			}
			if err := redisCltRawData.HDel(LampEventId, eo.ID).Err(); err != nil {
				logrus.Errorf("灯控[%s]关灯数据[ID=%d]删除失败:%s", eo.ID, RID, err.Error())
			}
		}
	} else if eo.EventType == models.ET_ONLAMP { //开灯事件
		oo := models.DeviceLampEvents{
			DID:        eo.ID,
			TStart:     eo.Time,
			Brightness: uint8(eo.Value),
		}
		if err := models.G_db.Create(&oo).Error; err != nil {
			logrus.Errorf("灯控[%s]开灯数据入库失败:%s", eo.ID, err.Error())
		} else {
			o.MapLampEvent[eo.ID] = int64(oo.ID)
			if err := redisCltRawData.HSet(LampEventId, eo.ID, oo.ID).Err(); err != nil {
				logrus.Errorf("灯控[%s]开灯数据[ID=%d]缓存失败:%s", eo.ID, oo.ID, err.Error())
			}
		}
	}
}
