/*
****************************
struct Event
****************************
실제 이벤트 데이터

Copy()
 이벤트 데이터 복사

****************************
struct EventH
****************************
struct Event 핸들링 구조체. singleton이며 전역 변수.
struct Event를 array로 가진다.

AllocEvent()
 array에서 빈 event item을 확보하고 데이터를 넣는다.

RemoveEvent()
 array에서 해당 event를 삭제.

PostEvent()
 이벤트를 받을 세션에 이벤트를 보낸다.

GetEvent()
 이벤트 수신.
 polling 방식

*/

package icsevent

import (
	"sync"

	"gitlab.com/ics_cinnamon/voiceStatistics/icserror"
)

const (
	MAX_EVENT_NUM   = 655360
	MAX_CHANNEL_NUM = 256
)

///////////////////////////////////////////////////////////////
// 단위 이벤트
type Event struct {
	Data *interface{}
	ID   int
	//m    *sync.Mutex
}

func (e *Event) GetData() *interface{} {
	return e.Data
}

///////////////////////////////////////////////////////////////
// 전역 이벤트 배열
type EventH struct {
	evtArray []*Event
	m        *sync.Mutex
}

var eventData []*Event
var onceEvent sync.Once
var onceEventInit sync.Once

var channelNum int = MAX_CHANNEL_NUM
var eventNum int = MAX_EVENT_NUM
var lastPos int

func getEventInstance() []*Event {
	onceEvent.Do(func() {
		eventData = make([]*Event, eventNum)
		for iter := 0; iter < eventNum; iter++ {
			eventData[iter] = new(Event)
			//eventData[iter].m = &sync.Mutex{}
		}
		lastPos = 0
	})

	return eventData
}

//make global Event Array
func NewEventH() *EventH {
	//s := icssvc.NewService()
	//channelNum = s.GetIcsConfig().GetChannelNum()
	//TODO: add recover

	h := &EventH{}
	h.evtArray = getEventInstance()
	h.m = &sync.Mutex{}

	return h
}

//initialize Event Array
func (h *EventH) Init() {
	onceEventInit.Do(func() {
		for iter := 0; iter < eventNum; iter++ {
			h.evtArray[iter].Data = nil
			h.evtArray[iter].ID = iter
		}
	})
}

//put data to event array
func (h *EventH) AllocEvent(data interface{}) (*Event, *icserror.IcsError) {
	count := 0
	for iter := lastPos; count < eventNum; iter++ {
		if iter == eventNum {
			iter = 0
		}

		//h.evtArray[iter].m.Lock()
		h.m.Lock()
		if h.evtArray[iter].Data == nil {
			h.evtArray[iter].Data = &data
			lastPos = iter + 1
			//h.evtArray[iter].m.Unlock()
			h.m.Unlock()
			return h.evtArray[iter], nil
		}
		//h.evtArray[iter].m.Unlock()
		h.m.Unlock()
		count++
	}

	return nil, icserror.ICSERREVTAlloc
}

//remove data from Event Array
func (h *EventH) RemoveEvent(event *Event) *icserror.IcsError {
	if event == nil {
		return icserror.ICSERRInvalidParam
	}

	if h.evtArray[event.ID].Data == nil {
		return icserror.ICSERREVTRemove
	}

	//h.evtArray[event.ID].m.Lock()
	h.m.Lock()
	h.evtArray[event.ID].Data = nil
	//h.evtArray[event.ID].m.Unlock()
	h.m.Unlock()

	return nil
}

func (h *EventH) removeEvent(id int) *icserror.IcsError {
	if h.evtArray[id].Data == nil {
		return icserror.ICSERREVTRemove
	}

	//h.evtArray[id].m.Lock()
	h.m.Lock()
	h.evtArray[id].Data = nil
	//h.evtArray[id].m.Unlock()
	h.m.Unlock()

	return nil
}

func (h *EventH) Copy(src *Event, dst *Event) *icserror.IcsError {
	if dst == nil {
		return icserror.ICSERRInvalidParam
	}

	//e.m.Lock()
	h.m.Lock()
	dst.Data = src.Data
	dst.ID = src.ID
	h.m.Unlock()
	//e.m.Unlock()

	return nil
}

func (h *EventH) PostEvent(targetID int, event *Event) *icserror.IcsError {
	if targetID < 0 || event == nil {
		return icserror.ICSERRInvalidParam
	}

	target := GetEvtSystemInstance()
	q := target[targetID]
	esErr := q.PutJob(event)
	if esErr != nil {

		return esErr
	}

	return nil
}

func (h *EventH) GetEvent(targetID int) (*Event, *icserror.IcsError) {
	if targetID < 0 {
		return nil, icserror.ICSERRInvalidParam
	}

	target := GetEvtSystemInstance()
	q := target[targetID]
	pos, esErr := q.GetJob()
	if esErr != nil || pos < 0 {
		return nil, esErr
	}

	e := &Event{}
	//h.evtArray[pos].Copy(e)
	h.Copy(h.evtArray[pos], e)
	h.RemoveEvent(h.evtArray[pos])

	return e, nil
}

//FOR ONLY DEBUG. DO NOT USE THIS FUNCTION-getEvt()!
func (h *EventH) getEvt(eventID int) *Event {
	return h.evtArray[eventID]
}
