package icssessionmanager

import (
	"fmt"
	"strings"
	"sync"
	"time"
	"net"

	"gitlab.com/ics_cinnamon/joy4/format"
	"gitlab.com/ics_cinnamon/voicegateway/icscbtimer"
	"gitlab.com/ics_cinnamon/voicegateway/icsconf"
	"gitlab.com/ics_cinnamon/voicegateway/icsdtmf"
	"gitlab.com/ics_cinnamon/voicegateway/icserror"
	"gitlab.com/ics_cinnamon/voicegateway/icsevent"
	"gitlab.com/ics_cinnamon/voicegateway/icslog"
	"gitlab.com/ics_cinnamon/voicegateway/icsmediaconv"
	"gitlab.com/ics_cinnamon/voicegateway/icsnet"
	"gitlab.com/ics_cinnamon/voicegateway/icspacketparser"
	"gitlab.com/ics_cinnamon/voicegateway/icssvc"
)

type IcsSession struct {
	ID int

	goroutineID int

	//agent config
	AgentInfo *icsconf.AgentInfo
	AgentConf *icsconf.AgentConfig

	sipNeter *icsnet.IcsNeter

	callID string
	//regiCallID string
	uri       string //register from
	srcPort   int
	dstPort   int
	direction bool

	eventSystem *icsevent.EventSystem

	//manage session
	lastTimestamp time.Time
	isStart       bool

	//media
	invitePayloads []string
	payloadType    icspacketparser.PayloadType
	isFoundPayload bool
	TxConverter    *icsmediaconv.Converter
	RxConverter    *icsmediaconv.Converter
	txSeq          int //first TX seq num
	rxSeq          int //first RX seq num
	RTPCBStatus    int
	//TTS data
	tts []byte
	
	remoteSIPAddr *net.UDPAddr

	//call info
	StartTimeStamp int64
	EndTimeStamp   int64
	Station        string
	ChannelID      string
	ServerID       int
	CustID         string

	/*//////////////////
	rxFile *os.File
	txFile *os.File
	*/ //////////////////

	//Voice agent conn
	//CallSignalNeter icsnet.IcsNeter
	VoiceNeter icsnet.IcsNeter

	MethodAutomata int

	//agent
	AgentName  string
	AgentName2 string
	//RTP port
	RTPPort int

	//register callback timer
	RegisterCallBackTimer *icscbtimer.IcsCBTimer
	registerStatus        int
	agentStatus           int
	//OPTIONS callback timer
	OPTIONSCallBackTimer *icscbtimer.IcsCBTimer
	//RTP sender callback timer
	RTPSenderCallBackTimer *icscbtimer.IcsCBTimer
	//INVITE callback timer
	INVITECallBackTimer *icscbtimer.IcsCBTimer

	//rtp/rtcp Tx/Rx neter
	/*
			rtpTxPort   int
			rtpTxIP     string
			rtpTxAddr *icsnet.IcsNetAddr
			rtcpTxPort  int
			rtcpTxIP    string
			rtcpTxAddr *icsnet.IcsNetAddr
		rtpTxNeter  icsnet.IcsNeter
		rtcpTxNeter icsnet.IcsNeter
	*/

	//remoteMediaAddr *icsnet.IcsNetAddr
	rtpMediaNeter  icsnet.IcsNeter
	rtcpMediaNeter icsnet.IcsNeter
	//media format
	mediaFormat []string
	//rtp info
	rtpSSRC uint32
	rtpSeq  uint16
	rtpTS   uint32

	//call statistic
	rxCnt         int
	txCnt         int
	callStartTime time.Time
	callEndTime   time.Time

	//cseq
	Cseq     uint32
	RegiCseq uint32

	CurrentSIP *icspacketparser.SIP
	InviteSIP  *icspacketparser.SIP

	//dtmf handle
	dtmf *icsdtmf.ICSDTMF

	// botStatus
	BotStatus uint32
	//tmp refer to
	referto string
	simLoopCount int
	// ExpTimer *icscbtimer.IcsCBTimer
	expRead time.Time
	readTimer time.Ticker
	expCheck bool
	mychannel chan bool


	m *sync.Mutex
}

type IcsSessionID int

const MAX_SESSION_IDLE = time.Hour

const (
	ICS_INBOUND_CALL  = true
	ICS_OUTBOUND_CALL = false
)

//////////////////////////////////////////////////////////////////
//session routine

//init ffmpeg
func init() {
	format.RegisterAll()
}

//on/off check the session idle
func (s *IcsSession) Start() {
	s.SetTimestamp(time.Now())
	s.isStart = true
}

func (s *IcsSession) Stop() {
	l := icslog.GetIcsLog()
	l.Print(icslog.LOG_LEVEL_INFO, s.ID, "Session Stop")

	s.isStart = false
}

func (s *IcsSession) Init() *icserror.IcsError {
	l := icslog.GetIcsLog()
	//conf := icsconf.GetIcsConfig()

	s.RTPCBStatus = 0

	/*
		s.AgentName = s.AgentConf.Name
		s.AgentName2 = fmt.Sprintf("agent%s", s.AgentConf.Name)
	*/

	//media format
	s.mediaFormat = strings.Split(s.AgentConf.MediaConfig.Format, " ")
	//s.mediaFormat = strings.Split(conf.AgentConfig[s.ID].MediaConfig.Format, " ")
	//fmt.Println(len(s.mediaFormat), s.mediaFormat)

	//create rx rtp/rtcp listener
	s.RTPPort = s.AgentConf.MediaConfig.Port
	if s.rtpMediaNeter == nil {
		//s.RTPPort = conf.AgentConfig[s.ID].MediaConfig.Port
		RTPAddrStr := fmt.Sprintf("0.0.0.0:%d", s.RTPPort)
		RTPAddr := icsnet.NewNetAddrWithIPAddr(RTPAddrStr)
		s.rtpMediaNeter = icsnet.NewUDP(&RTPAddr, nil)
		RTCPAddrStr := fmt.Sprintf("0.0.0.0:%d", s.RTPPort+1)
		RTCPAddr := icsnet.NewNetAddrWithIPAddr(RTCPAddrStr)
		s.rtcpMediaNeter = icsnet.NewUDP(&RTCPAddr, nil)

		//set rtp send callback
		s.RTPPort = s.AgentConf.MediaConfig.Port
		s.RTPSenderCallBackTimer = icscbtimer.NewCBTimer(time.Millisecond*20, s.SendRTPCB)

		err1 := s.rtpMediaNeter.Listen()
		if err1 != nil {
			l.Print(icslog.LOG_LEVEL_FATAL, s.ID, err1.GetMessage())
			l.Printf(icslog.LOG_LEVEL_FATAL, s.ID, "%s", err1.GetError())
			return err1
		}
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Listening RTP port[%d]", s.RTPPort)
		err1 = s.rtcpMediaNeter.Listen()
		if err1 != nil {
			l.Print(icslog.LOG_LEVEL_FATAL, s.ID, err1.GetMessage())
			return err1
		}
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Listening RTCP port[%d]", s.RTPPort+1)

		s.dtmf = icsdtmf.NewICSDTMF()

		go s.ReadRTP()
		go s.ReadRTCP()
	}

	return nil
}

func (s *IcsSession) Run() *icserror.IcsError {
	mySessionID := s.GetSessionID()
	l := icslog.GetIcsLog()
	conf := icsconf.GetIcsConfig()

	s.goroutineID = goid()

	l.Printf(icslog.LOG_LEVEL_DEBUG2, int(mySessionID), "Session [%d] READY\n", mySessionID)

	//start callback timer func
	if strings.Compare(strings.ToUpper(conf.AgentConfig[s.ID].Value), "TRUE") == 0 {
		s.SetRegisterStatus(STATUS_REGISTER_READY)
		s.RegisterCallBackTimer.Start()
		defer s.RegisterCallBackTimer.Stop()
		s.OPTIONSCallBackTimer.Start()
		defer s.OPTIONSCallBackTimer.Stop()
		//s.INVITECallBackTimer.Start()
		//defer s.INVITECallBackTimer.Stop()
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Started [%s] callback timer", conf.AgentConfig[s.ID].Name)
	}

	svc := icssvc.GetServiceStatus()
	for !svc.GetExit() && !svc.GetStop() {
		//get event
		h := icsevent.NewEventH()
		evt, evterr := h.GetEvent(int(mySessionID))
		if evt == nil {
			if evterr != nil {
				//evterr.Print()
				time.Sleep(time.Millisecond)
				continue
			}

			if strings.Compare(strings.ToUpper(conf.AgentConfig[s.ID].Value), "TRUE") == 0 {
				duration := time.Since(s.lastTimestamp)
				if s.isStart && duration >= MAX_SESSION_IDLE {
					//Session expired!
					err := icserror.ICSERRSESSMaxSessionIdle

					logmsg := fmt.Sprintf("Session expired. Duration: %v(%v) - %s\n", duration, s.lastTimestamp, err.GetMessage())
					l.Print(icslog.LOG_LEVEL_INFO, s.ID, logmsg)

					s.Stop()
					serr := s.RemoveSession()
					if serr != nil {
						//serr.Print()
						l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "RemoveSession Error %s", serr.GetMessage())
					}

					//return err
				}
			}

			time.Sleep(time.Millisecond)
			continue
		}

		//refresh session timestamp
		s.SetTimestamp(time.Now())

		//l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get Event[%d]", evt.ID)

		data := evt.GetData()

		switch v := (*data).(type) {
		case icspacketparser.SIP:
			l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get SIP Event[%d]", evt.ID)
			sip := v
			s.CurrentSIP = &sip
			s.Processing(&sip)
			//s.analyzeSIP(&sip)
		case icspacketparser.RTP:
			//l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get RTP Event[%d]", evt.ID)
			rtp := v
			//fmt.Println(rtp)
			s.analyzeRTP(&rtp)
		default:
			// fmt.Println(time.Now(), "NOT DEFINED EVENT TYPE!!!!!!!!!!!!!!!!!")
		}
	}

	l.Print(icslog.LOG_LEVEL_DEBUG2, s.ID, "Stoped Session!")

	return nil
}
