package simphone

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
	"gitlab.com/ics_cinnamon/voicegateway/icsconf"
	"gitlab.com/ics_cinnamon/voicegateway/icserror"
	"gitlab.com/ics_cinnamon/voicegateway/icslog"
	"gitlab.com/ics_cinnamon/voicegateway/icsmediaconv"
	"gitlab.com/ics_cinnamon/voicegateway/icsnet"
	"gitlab.com/ics_cinnamon/voicegateway/icssvc"
	"gitlab.com/ics_cinnamon/voicegateway/sim/icspacketgen"
	"gitlab.com/ics_cinnamon/voicegateway/sim/simprocnew"
)

type SimPhone struct {
	isCaller      bool
	ID            int
	CallID        string
	PayloadType   int
	SSRC          uint32
	Seq           uint16
	Timestamp     uint32
	TSIPPort      int
	SIPPort       int
	RTPTxPort     int
	RTPCBTxPort   int
	TRTPTxPort    int
	lsaddr        *icsnet.IcsNetAddr //local sip addr
	laddr         *icsnet.IcsNetAddr //local rtp addr
	rsaddr        *icsnet.IcsNetAddr //remote sip addr
	raddr         *icsnet.IcsNetAddr //remote rtp addr
	localRTPUDP   *icsnet.IcsUDPNet
	sentTime      int
	sendRTPStatus bool
	expTime       time.Time
	mediaStart    int
	wgMedia       sync.WaitGroup
	rtptimer      *time.Timer
}

func NewSimPhone(id int) *SimPhone {
	s := &SimPhone{}

	simconf := icsconf.GetSimConfig()

	s.ID = id
	if strings.Compare("YES", strings.ToUpper(simconf.IsCaller)) == 0 {
		s.CallID = uuid.New().String()
		s.isCaller = true
		//fmt.Println("Call ID -", s.CallID)
	}
	s.SSRC = icspacketgen.GenSSRC()
	s.Seq = 1
	s.PayloadType = icsmediaconv.ICS_PT_ALAW
	s.SIPPort = simconf.SimPhoneConfig.SIPPort
	s.TSIPPort = simconf.TargetPhoneConfig.TSIPPort
	s.TRTPTxPort = simconf.TargetPhoneConfig.TRTPPort + (s.ID * 2) //targetRTPTxPort
	s.RTPTxPort = simconf.SimPhoneConfig.RTPPort + (s.ID * 2)      //localRTPTxPort
	la1 := icsnet.NewNetAddrWithIPPort(simconf.SimPhoneConfig.MYIP, s.SIPPort)
	s.lsaddr = &la1
	la2 := icsnet.NewNetAddrWithIPPort(simconf.SimPhoneConfig.MYIP, s.RTPTxPort)
	s.laddr = &la2
	s.sentTime = 0
	s.sendRTPStatus = false

	s.wgMedia = sync.WaitGroup{}

	return s
}

func (s *SimPhone) SetRemoteAddr(saddr *icsnet.IcsNetAddr, addr *icsnet.IcsNetAddr) { //sip addr, rtp addr
	s.rsaddr = saddr
	s.raddr = addr
}

func (s *SimPhone) Run() *icserror.IcsError {
	svc := icssvc.GetServiceStatus()

	l := icslog.GetIcsLog()
	l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Phone[%d] Started! SIP addr: %v, RTP addr: %v\n",
		s.ID, s.lsaddr, s.laddr)
	l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "%s\n", s.String())

	simconf := icsconf.GetSimConfig()

	//make rtp event timer
	s.rtptimer = time.NewTimer(time.Millisecond)
	rtpReadTimer := time.NewTicker(time.Second)
	wgRTPListen := sync.WaitGroup{}

	//create rtp liesten goroutine
	s.localRTPUDP = icsnet.NewUDP(s.laddr, nil)
	lerr := s.localRTPUDP.Listen()
	if lerr == nil {
		wgRTPListen.Add(1)
		go func() {
			// fmt.Println("############### start rtp", s.laddr)
			l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Start RTP Listen[%v]\n", s.laddr)
			totalRlen := 0
			for s.mediaStart != 2 || !svc.GetExit() || !svc.GetStop() {
				_, _, rlen, rtperr := s.localRTPUDP.ReadRTP()
				if rtperr == nil {
					s.expTime = time.Now()
					totalRlen += rlen
				} else {
					// fmt.Printf("\nTotal Recved RTP Length[%d]\n", totalRlen)
					// fmt.Println("##### id : ", s.ID)
					break
				}
			}
			l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Stoped[%d] RTP Listen[%v] Total Recved RTP length %d\n", s.mediaStart, s.laddr, totalRlen)
			wgRTPListen.Done()
		}()
	} else {
		l.Printf(icslog.LOG_LEVEL_FATAL, s.ID, "Failed to listen RTP[%v] - %v\n", s.laddr, lerr)
		return nil
	}

	///////////////////////////
	//CALLER SIDE
	l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "CALLER SIDE")

	id := fmt.Sprintf("%d", s.ID+7000)
	toid := fmt.Sprintf("%d", s.ID+21016)
	// fmt.Println("############################################################################## id : ", id)
	// fmt.Println("############################################################################## id : ", toid)
	// fmt.Println("############################################################################## id : ", s.TRTPTxPort)

	ra := icsnet.NewNetAddrWithIPPort(simconf.GetSimTargetIP(), s.TSIPPort)
	s.rsaddr = &ra
	l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "SIP address [%v] [%v]", s.lsaddr, s.rsaddr)

	localSIPUDP := icsnet.NewUDP(s.lsaddr, s.rsaddr)
	serr := localSIPUDP.Connect(s.lsaddr, s.rsaddr)
	if serr != nil {
		l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to Connect [%v]->[%v]", s.lsaddr, s.rsaddr)
		return serr
	}

	/////////////////////
	// send regi sip
	// fmt.Println("###################### regi")
	if simconf.RegiCount != 0 {
		regiCount := 0
		for {
			buf, rlen, err := simprocnew.NewReadSIP(s.ID, s.CallID, simconf.SimPhoneConfig.MYIP, s.TSIPPort, ra, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP)
			if err != nil {
				// fmt.Println(err)
			}
			n := bytes.Index(buf[:rlen], []byte{32})
			if string(buf[:n]) == "REGISTER" {
				regiSip := simprocnew.NewSIPSignal("REGI", s.ID, id, toid, ra, s.CallID, simconf.SimPhoneConfig.MYIP, s.TSIPPort, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP, buf, rlen)
				if regiSip != nil {
					l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Send Invite error [%s]", regiSip)
					return regiSip
				}
				regiCount += 1
			}
			if regiCount == simconf.RegiCount {
				break
			}
		}
	}

	// time.Sleep(time.Second * 2)

	////////////////////
	// get call flow //
	///////////////////
	// ra = icsnet.NewNetAddrWithIPPort(simconf.GetSimTargetIP(), s.TRTPTxPort)
	// l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Start sending RTP[%s:%d]", ra.IPv4String, ra.Port)
	// s.raddr = &ra
	// s.localRTPUDP.SetRemoteAddr(s.raddr)

	simScenarioConf := icsconf.GetSimScenarioConfig()
	// var stopByeTimer *time.Ticker
	for _, ord := range simScenarioConf.SipOrder.Order {
		// fmt.Println("######################################################## : ", strings.ToUpper(ord))
		switch strings.ToUpper(ord) {
		// case "WAIT": // set send bye time
		// 	stopByeTimer = time.NewTicker(time.Second * time.Duration(simScenarioConf.SipOrder.StopTime))
		case "BYE":
			go func() {
				// if stopByeTimer != nil {
				// 	<-stopByeTimer.C
				// }
				sendSip := simprocnew.NewSIPSignal("BYE", s.ID, id, toid, ra, s.CallID, simconf.SimPhoneConfig.MYIP, s.TRTPTxPort, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP, nil, 0)
				if sendSip != nil {
					l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Send bye error [%s]", sendSip)
					return
				}
			}()
		case "ACK":
			sendSip := simprocnew.NewSIPSignal(strings.ToUpper(ord), s.ID, id, toid, ra, s.CallID, simconf.SimPhoneConfig.MYIP, s.TRTPTxPort, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP, nil, 0)
			if sendSip != nil {
				l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Send SIP error (except BYE) [%s]", sendSip)
				return sendSip
			} else {
				go func() {
					buf, rlen, err := simprocnew.NewReadSIP(s.ID, s.CallID, simconf.SimPhoneConfig.MYIP, s.TRTPTxPort, ra, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP)
					if err != nil {
						l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to recv BYE - %v", err)
						return
					} else {
						// fmt.Println(string(buf[:rlen]))
						l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Receved BYE Message :  %s", string(buf[:rlen]))
						// stopByeTimer.Stop()
						sendSip := simprocnew.NewSIPSignal("BYE200", s.ID, id, toid, ra, s.CallID, simconf.SimPhoneConfig.MYIP, s.TRTPTxPort, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP, nil, 0)
						if sendSip != nil {
							l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Send bye error [%s]", sendSip)
							return
						} else {
							// s.mediaStart = 1
							// s.wgMedia.Wait()
						}
					}
				}()
			}
		case "TTS":
			go s.ExpireReadRTP(rtpReadTimer)
			for {
				if s.sendRTPStatus {
					break
				}
			}
		case "STT":
			// send rtp
			ra = icsnet.NewNetAddrWithIPPort(simconf.GetSimTargetIP(), s.TRTPTxPort)
			l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Start sending RTP[%s:%d]", ra.IPv4String, ra.Port)
			s.raddr = &ra
			s.localRTPUDP.SetRemoteAddr(s.raddr)
			s.wgMedia.Add(1)
			go s.RunMedia(s.raddr.Port, s.PayloadType)
			// s.mediaStart = 1
			s.wgMedia.Wait()
			l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Sending RTP Done!")
			s.mediaStart = 2
		case "DTMF":
		default:
			sendSip := simprocnew.NewSIPSignal(strings.ToUpper(ord), s.ID, id, toid, ra, s.CallID, simconf.SimPhoneConfig.MYIP, s.TRTPTxPort, s.laddr, s.rsaddr, s.lsaddr, localSIPUDP, nil, 0)
			if sendSip != nil {
				l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Send SIP error (except BYE) [%s]", sendSip)
				return sendSip
			}
		}
	}

	s.localRTPUDP.Close()

	wgRTPListen.Wait()

	s.rtptimer.Stop()

	return nil
}

func (s *SimPhone) RunMedia(port int, pt int) {
	// fmt.Println("#### RunMedia")
	defer func() {
		s.wgMedia.Done()
	}()
	l := icslog.GetIcsLog()
	simconf := icsconf.GetSimConfig()

	var voiceFileName string
	if strings.Compare(simconf.IsCaller, "yes") == 0 {
		voiceFileName = "./voice/9012-RX-1649749470147018876.raw"
	} else {
		voiceFileName = "./voice/9012-RX-1649749470147018876.raw"
	}
	fp, err := os.Open(voiceFileName)
	if err != nil {
		l.Printf(icslog.LOG_LEVEL_FATAL, s.ID, "Failed to Voice file-%v", err)
		if s.isCaller {
			s.wgMedia.Done()
		}
		return
	}
	defer fp.Close()

	fs, err := os.Stat(voiceFileName)
	if err != nil {
		l.Printf(icslog.LOG_LEVEL_FATAL, s.ID, "Failed to Voice file-%v", err)
		if s.isCaller {
			s.wgMedia.Done()
		}
		return
	}

	fileLen := fs.Size()
	voiceIdx := 0
	voiceBuf := make([]byte, fileLen)
	flen, err := fp.Read(voiceBuf)
	if flen <= 0 || err != nil {
		l.Printf(icslog.LOG_LEVEL_FATAL, s.ID, "Failed to Voice file-%v", err)
		if s.isCaller {
			s.wgMedia.Done()
		}
		return
	}

	ra := icsnet.NewNetAddrWithIPPort(simconf.GetSimTargetIP(), s.TRTPTxPort)
	s.localRTPUDP.SetRemoteAddr(&ra)

	before := time.Now()
	for {
		<-s.rtptimer.C
		s.rtptimer.Reset(time.Millisecond)

		if s.mediaStart == 0 {
			time.Sleep(time.Millisecond * 2)
			// continue
		} else if s.mediaStart == 2 {
			break
		}
		now := time.Now()
		var delta time.Duration = (time.Millisecond * 20) - now.Sub(before)
		if delta < 0 {
			time.Sleep((time.Millisecond * 20) + delta)
		} else {
			time.Sleep((time.Millisecond * 20) - delta)
		}
		before = now

		r := icspacketgen.NewRTP()
		rtppacket := r.MakeRTPData(uint8(pt), &s.Seq, &s.Timestamp, s.SSRC, voiceBuf[voiceIdx:voiceIdx+160])
		voiceIdx += 160
		if int64(voiceIdx+160) >= fileLen {
			voiceIdx = len(voiceBuf) - 1
			rtppacket = r.MakeRTPData(uint8(pt), &s.Seq, &s.Timestamp, s.SSRC, voiceBuf[voiceIdx:])
			break
		}

		wlen, werr := s.localRTPUDP.WriteRTP(rtppacket)
		if werr != nil {
			l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to write RTP packet %d-%s{%v}", wlen, werr.GetMessage(), werr.GetError())
			if s.isCaller {
				s.wgMedia.Done()
			}
			return
		}
	}

	l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Sent Voice data length %d-%d", voiceIdx, s.sentTime)

	s.sendRTPStatus = false
}

func (s *SimPhone) String() string {
	return fmt.Sprintf("ID: %d\nCall ID: %s\nPayload Type: %d\nSIP Port: %d\nTRTPTxPort: %d",
		s.ID, s.CallID, s.PayloadType, s.SIPPort, s.TRTPTxPort)
}

func (s *SimPhone) ExpireReadRTP(t *time.Ticker) {
	// s.sendRTPStatus = false
	s.expTime = time.Now()
	for t := range t.C {
		// if s.sendRTPStatus {
		// 	s.sendRTPStatus = false
		// } else
		if time.Now().Sub(s.expTime) >= time.Second {
			// fmt.Println(t)
			s.sendRTPStatus = true
			break
		} else if s.sendRTPStatus {
			continue
		}
	}
}
