package icssessionmanager

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

	"gitlab.com/ics_cinnamon/voicegateway/icsconf"
	"gitlab.com/ics_cinnamon/voicegateway/icserror"
	"gitlab.com/ics_cinnamon/voicegateway/icslog"
	"gitlab.com/ics_cinnamon/voicegateway/icspacketparser"
)

//////////////////////////////////////////////////////////////////
//session operator
func findSessionWithCallID(callId string) *IcsSession {
	sessions := getSessionInstance(nil)
	for _, session := range sessions {
		session.m.Lock()
		// fmt.Printf("findSessionWithCallID %d [%s][%s]\n", iter, session.callID, callId)

		if strings.Compare(session.callID, callId) == 0 {
			//if strings.Compare(session.callID, callId) == 0 || strings.Compare(session.regiCallID, callId) == 0 {
			// fmt.Printf("findSessionWithCallID session found!  %d [%s][%s]\n", iter, session.callID, callId)
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

func findSessionWithFromURI(uri string) *IcsSession {
	sessions := getSessionInstance(nil)
	for _, session := range sessions {
		session.m.Lock()
		//fmt.Println("findSessionWithFromURI", session.uri)
		if strings.Contains(session.uri, uri) {
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

func findSessionWithRTPPort(srcPort int, dstPort int) *IcsSession {
	sessions := getSessionInstance(nil)
	for _, session := range sessions {
		session.m.Lock()
		if (session.srcPort == srcPort && session.dstPort == dstPort) || (session.srcPort == dstPort && session.dstPort == srcPort) {
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

func findSessionWithAgentName(agentName string) *IcsSession {
	sessions := getSessionInstance(nil)
	for _, session := range sessions {
		session.m.Lock()
		//fmt.Printf(">>>>findSessionWithAgentName: name: %s, name2: %s, arg: %s\n", session.AgentName, session.AgentName2, agentName)
		if strings.Compare(session.AgentName, agentName) == 0 {
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

// representative
func findSessionWithSessionStatus(callID string) *IcsSession {
	conf := icsconf.GetIcsConfig()
	sessions := getSessionInstance(nil)
	for idx, session := range sessions {
		session.m.Lock()
		// fmt.Println(session.AgentName, session.agentStatus)
		if strings.Compare(conf.AgentConfig[idx].Value, "true") == 0 && session.agentStatus == 64 {
			// session.callID = callID
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

func findSessionWithSessionStatusForINVITE() *IcsSession {
	conf := icsconf.GetIcsConfig()
	sessions := getSessionInstance(nil)
	for idx, session := range sessions {
		session.m.Lock()
		if strings.Compare(conf.AgentConfig[idx].Value, "true") == 0 && session.agentStatus == 64 {
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

func findSessionWithAgentName2(agentName string) *IcsSession {
	sessions := getSessionInstance(nil)
	for _, session := range sessions {
		session.m.Lock()
		//fmt.Printf(">>>>findSessionWithAgentName2: name: %s, name2: %s, arg: %s\n", session.AgentName, session.AgentName2, agentName)
		if strings.Compare(session.AgentName2, agentName) == 0 {
			session.m.Unlock()
			return session
		}
		session.m.Unlock()
	}

	return nil
}

/* 미사용 코드로 임시 주석 처리 ( csma 210512 )
func FindSessionID(sip icspacketparser.SIP) (IcsSessionID, *icserror.IcsError) {
	callID := sip.GetCallID()
	if len(callID) > 0 {
		session := findSessionWithCallID(callID)
		if session != nil {
			return IcsSessionID(session.ID), nil
		}
	}

	return IcsSessionID(-1), icserror.ICSERRSESSNotFoundSession
}
*/

func FindSession(data interface{}) (*IcsSession, *icserror.IcsError) {
	//func FindSession(sip icspacketparser.SIP) (*IcsSession, *icserror.IcsError) {
	l := icslog.GetIcsLog()
	conf := icsconf.GetIcsConfig()
	defer func() {
		if err := recover(); err != nil {
			l.Printf(icslog.LOG_LEVEL_WARN, -1, "%s\n%s",
				icserror.ICSERRNETNotConnectError.GetMessage(), debug.Stack())
		}
	}()
	switch v := data.(type) {
	case icspacketparser.SIP:
		sip := v
		callID := sip.GetCallID()
		uri := sip.GetURI()
		name1 := strings.SplitN(sip.To, "@", 2)
		name2 := strings.SplitN(name1[0], ":", 2)
		agentname := name2[1]
		/*
			if sip.Method == icspacketparser.ICS_SIP_METHOD_BYE || sip.Method == icspacketparser.ICS_SIP_METHOD_CANCEL {
				//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>", callID)
			}
		*/

		// if conf.Representative.Value && sip.Method == icspacketparser.ICS_SIP_METHOD_SIP20 && strings.SplitN(sip.Cseq, " ", 2)[1] == "INVITE" {
		if conf.Representative.Value && sip.Method == icspacketparser.ICS_SIP_METHOD_INVITE { // invite, botstatus
			if len(agentname) > 0 {
				l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "FindSession>findSessionWithAgentName> agent name[%s]", agentname)
				// session := findSessionWithCallID(callID)
				session := findSessionWithSessionStatus(callID) // 대표번호 agentname
				if session != nil {
					return session, nil
				}
			}
		} else if conf.Representative.Value && sip.Method == icspacketparser.ICS_SIP_METHOD_SIP20 && strings.SplitN(sip.Cseq, " ", 2)[1] == "INVITE" { // callid
			if len(callID) > 0 {
				l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "FindSession>findSessionWithCallID> callid [%s]", callID)
				session := findSessionWithCallID(callID)
				if session != nil {
					return session, nil
				}
			}
		} else {
			if len(callID) > 0 {
				l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "FindSession>findSessionWithCallID> callid [%s]", callID)
				session := findSessionWithCallID(callID)
				if session != nil {
					return session, nil
				}
			}
			// if len(agentname) > 0 {
			// 	l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "FindSession>findSessionWithAgentName> agent name[%s]", agentname)
			// 	session := findSessionWithAgentName(agentname)
			// 	if session != nil {
			// 		return session, nil
			// 	}
			// }
			if len(uri) > 0 {
				l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "FindSession>findSessionWithFromURI> uri [%s]", uri)
				//fmt.Println("findsession", uri)
				session := findSessionWithFromURI(uri)
				if session != nil {
					return session, nil
				}
			}
		}
	case icspacketparser.RTP:
		rtp := v
		srcPort := rtp.SrcAddr.Port
		dstPort := rtp.DstAddr.Port
		session := findSessionWithRTPPort(srcPort, dstPort)
		if session != nil {
			return session, nil
		}
	}

	return nil, icserror.ICSERRSESSNotFoundSession
}

/* 미사용 코드로 임시 주석 처리 ( csma 200512 )
func AllocSessionID(callId string, direction bool) (IcsSessionID, *icserror.IcsError) {
	if len(callId) == 0 {
		return IcsSessionID(-1), icserror.ICSERRInvalidParam
	}

	sessions := getSessionInstance()

	var id int
	for _, session := range sessions {
		id++
		if len(session.callId) == 0 {
			session.callId = callId
			session.direction = direction
			return IcsSessionID(session.ID), nil
		}
	}

	if id >= channelNum {
		return IcsSessionID(-1), icserror.ICSERRSESSMaxSessionNumber
	}

	return IcsSessionID(-1), icserror.ICSERRSESSNotFoundSession
}
*/

func goid() int {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	id, err := strconv.Atoi(idField)
	if err != nil {
		return -1
	}
	return id
}

func AllocSession(station, callID string) (*IcsSession, *icserror.IcsError) {
	if len(callID) == 0 {
		return nil, icserror.ICSERRInvalidParam
	}

	l := icslog.GetIcsLog()
	sessions := getSessionInstance(nil)

	var id int = 0
	for _, session := range sessions {
		id++
		session.m.Lock()
		if len(session.callID) == 0 && strings.Compare(station, session.AgentName) == 0 {
			session.callID = callID

			session.m.Unlock()
			l.Printf(icslog.LOG_LEVEL_INFO, session.ID,
				"Session Allocated[%d] Call ID: %s, Station: %s==========================",
				session.goroutineID, session.callID, station)
			return session, nil
		}
		session.m.Unlock()
	}

	if id >= channelNum {
		return nil, icserror.ICSERRSESSMaxSessionNumber
	}

	return nil, icserror.ICSERRSESSNotFoundSession
}

func (s *IcsSession) SetCallID(callID string) {
	s.callID = callID
}

func (s *IcsSession) CloseSession() *icserror.IcsError {

	return nil
}

func (s *IcsSession) RemoveSession() *icserror.IcsError {
	l := icslog.GetIcsLog()
	defer func() {
		if err := recover(); err != nil {
			switch v := err.(type) {
			case error:
				icserror.ICSERRNETNotConnectError.SetError(v)
				l.Printf(icslog.LOG_LEVEL_WARN, s.ID,
					"PANIC! %s\n%s",
					icserror.ICSERRNETNotConnectError.GetError().Error(), string(debug.Stack()))
			default:
				l.Print(icslog.LOG_LEVEL_WARN, s.ID, icserror.ICSERRNETNotConnectError.GetMessage())
			}
		}
	}()
	//s.VoiceNeter.Close()
	//s.CallSignalNeter.Close()

	//id := s.ID
	//s = &IcsSession{ID: id}

	//s.rxFile.Close()
	//s.txFile.Close()

	if s.CurrentSIP.Method == icspacketparser.ICS_SIP_METHOD_SIP20 && strings.Contains(s.CurrentSIP.Cseq, " REGISTER") {
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "REGI!!!")
		s.callID = ""
		return nil
	} else {
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "--------------------------")
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Call duration : %d ms", time.Since(s.callStartTime)/time.Millisecond)
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Rx packets : %d", s.rxCnt)
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Tx packets : %d", s.txCnt)
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Call Ended. Station: %s Call ID: %s", s.AgentName, s.callID)
		l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "==========================")

		// fmt.Println("### s.readTimer Stop")
		// s.readTimer.Stop()
		// s.mychannel <- true
		// if s.rtpMediaNeter != nil {
		// 	s.rtpMediaNeter.Close()
		// 	s.rtpMediaNeter = nil
		// }
		// fmt.Println("### s.readTimer Stop", s.AgentName)
		s.RTPSenderCallBackTimer.Stop()
		if s.rtpMediaNeter != nil {
			// fmt.Println("voice neter nil!!")
			s.rtpMediaNeter.Close()
			s.rtpMediaNeter = nil
		}
		if s.rtcpMediaNeter != nil {
			s.rtcpMediaNeter.Close()
			s.rtcpMediaNeter = nil
		}

	}

	e := &IcsSession{}
	s.m.Lock()
	s.eventSystem.ClearJob()

	/*
		s.AgentConf = nil
		s.AgentInfo = nil
		s.AgentName = ""
		s.AgentName2 = ""
	*/

	s.callID = ""

	s.tts = nil

	s.srcPort = e.srcPort
	s.dstPort = e.dstPort
	s.direction = e.direction
	s.isStart = false
	s.isFoundPayload = false
	//s.RxConverter.Close()
	//s.TxConverter.Close()
	if s.RxConverter != nil {
		s.RxConverter.Close()
		s.RxConverter = nil
	}
	if s.TxConverter != nil {
		s.TxConverter.Close()
		s.TxConverter = nil
	}
	s.txSeq = -1
	s.rxSeq = -1
	s.MethodAutomata = 32767
	// s.referto = ""
	s.SetAgentStatus(STATUS_AGENT_READY)
	s.SetRegisterStatus(STATUS_REGISTER_READY)
	// fmt.Println("REMOVE SESSION STATUS: ", s.GetAgentStatus())
	s.m.Unlock()

	return nil
}

func GetSession(sessionID IcsSessionID) (*IcsSession, *icserror.IcsError) {
	if sessionID < IcsSessionID(0) {
		return nil, icserror.ICSERRInvalidParam
	}

	sessions := getSessionInstance(nil)
	return sessions[int(sessionID)], nil
}

func (s IcsSession) GetSessionID() IcsSessionID {
	return IcsSessionID(s.ID)
}

func (s *IcsSession) CheckSignalMethod(sip icspacketparser.SIP) *icserror.IcsError {
	if s.CheckAutomata(&sip) {
		if s.MethodAutomata != ICS_SIP_AUTOMATA_CANCEL {
			s.SetSessionMethod(sip)
		}
	} else {
		//fmt.Println("^^^^^", "unMatched Automata", s.MethodAutomata, sip.Method)
	}

	if sip.Method == icspacketparser.ICS_SIP_METHOD_SIP20 && strings.Contains(sip.Cseq, " BYE") || s.MethodAutomata == ICS_SIP_AUTOMATA_CANCEL && sip.Method == icspacketparser.ICS_SIP_METHOD_ACK {
		//fmt.Println("^^^^^", "call s.RemoveSession() sip.Method, s.MethodAutomata ", sip.Method, s.MethodAutomata)
		serr := s.RemoveSession()
		if serr != nil {
			//serr.Print()
			return serr
		}
	}

	return nil
}

//////////////////////////////////////////////////////////////////
//
func (s *IcsSession) SetTimestamp(now time.Time) {
	s.lastTimestamp = now
}

func (s IcsSession) GetPayloadType() icspacketparser.PayloadType {
	return s.payloadType
}

func (s *IcsSession) GetDirection() bool {
	return s.direction
}

func (s *IcsSession) SetDirection(direction bool) {
	s.direction = direction
}

func (s *IcsSession) SetAgentInfo(agentInfo *icsconf.AgentInfo) {
	s.AgentInfo = agentInfo
}

func FindAgentInfo(agentID string) *icsconf.AgentInfo {
	conf := icsconf.GetIcsConfig()

	//a := strings.SplitN(agentID, string(0), 2)
	for _, agentconf := range conf.VoiceAgentConfig.AgentInfo {
		//for _, agentconf := range conf.AgentConfig {
		//fmt.Printf("308LINE iter: %d, [%v] [%v] [%v]\n", iter, []byte(agentconf.Name), []byte(agentID), []byte(a[0]))
		if strings.Contains(agentID, agentconf.Name) {
			//fmt.Println(">>>>", iter, agentconf.Name)
			return &agentconf
		}
	}
	return nil
}

func (s *IcsSession) SetAgentConfig(agentConfig *icsconf.AgentConfig) {
	s.AgentConf = agentConfig
}

func FindAgentConfig(agentID string) *icsconf.AgentConfig {
	conf := icsconf.GetIcsConfig()

	//a := strings.SplitN(agentID, string(0), 2)
	for _, agentconf := range conf.AgentConfig {
		//for _, agentconf := range conf.AgentConfig {
		//fmt.Printf("308LINE iter: %d, [%v] [%v] [%v]\n", iter, []byte(agentconf.Name), []byte(agentID), []byte(a[0]))
		if strings.Contains(agentID, agentconf.Name) {
			//fmt.Println(">>>>", iter, agentconf.Name)
			return &agentconf
		}
	}

	return nil
}
