package simproc

import (
	"strconv"
	"strings"

	"gitlab.com/ics_cinnamon/voicegateway/icsnet"
)

const (
	INVITE_STATE = 1 << iota
	BYE_STATE
	ACK_STATE
	OK200_STATE
	NONE_STATE    = INVITE_STATE
	UNKNOWN_STATE = 0xFFFF
)

const (
	INVITE_METH  = INVITE_STATE
	ACK_METH     = INVITE_STATE
	BYE_METH     = INVITE_STATE
	OK200_METH   = INVITE_STATE | BYE_STATE
	UNKNOWN_METH = UNKNOWN_STATE
)

const (
	MNAME_INVITE = 1 + iota
	MNAME_ACK
	MNAME_BYE
	MNAME_OK200
	MNAME_UNKNOWN
)

type SIPProc struct {
	curState  int
	SIPHeader *SIPHeader
	SDP       *SDP
}

type SIPHeader struct {
	ID          int
	Method      string
	From        string
	To          string
	CallID      string
	CSeq        string
	ContentType string
	Payload     string
}

type SDP struct {
	Connection  string
	Port        int
	PayloadType int
}

func (p SIPProc) GetCurStatus() int {
	return p.curState
}

func (p SIPProc) SetCurStatus(method int) {
	p.curState = method
}

func (p SIPProc) GetMethod() int {
	return p.SIPHeader.ID
}

func (p SIPProc) IsAllowMethod(method int) int {
	return p.curState & method
}

func NewSIPProc(sip []byte) *SIPProc {
	p := &SIPProc{curState: NONE_STATE}

	p.SIPParse(sip)
	p.SDPParse(p.SIPHeader.Payload)

	return p
}

func NewSIPHeader() *SIPHeader {
	h := &SIPHeader{}
	return h
}

func (s *SIPProc) SIPParse(sip []byte) (method string) {
	msg := string(sip)

	hp := strings.Split(msg, "\r\n\r\n")
	msgline := strings.Split(hp[0], "\r\n")
	//linenum := len(msgline)

	h := NewSIPHeader()

	for iter, line := range msgline {
		//fmt.Println(len(line), line)
		if iter == 0 { //method line
			method := strings.SplitN(line, " ", 2)
			h.Method = method[0]
			h.ID = getMethodID(h.Method)
		} else {
			headerline := strings.SplitN(line, ": ", 2)
			switch strings.ToUpper(headerline[0]) {
			case "FROM":
				h.From = headerline[1]
			case "TO":
				h.To = headerline[1]
			case "CALL-ID":
				h.CallID = headerline[1]
			case "CSEQ":
				h.CSeq = headerline[1]
			case "CONTENT-TYPE":
				h.ContentType = headerline[1]
			}
		}
	}
	if len(hp) == 2 {
		h.Payload = hp[1]
	}
	s.SIPHeader = h

	/*
		fmt.Println(s)
		fmt.Println(*s.Header)
		fmt.Println(">>>", s.Header.Method)
	*/

	return h.Method
}

func (s SIPProc) GetCSeq() string {
	return s.SIPHeader.CSeq
}

func (s SIPProc) GetFrom() string {
	return s.SIPHeader.From
}

func (s SIPProc) GetTo() string {
	return s.SIPHeader.To
}

func (s SIPProc) GetCallID() string {
	return s.SIPHeader.CallID
}

func getMethodID(method string) int {
	switch method {
	case "INVITE":
		return INVITE_METH
	case "BYE":
		return BYE_METH
	case "ACK":
		return ACK_METH
	case "SIP/2.0":
		return OK200_METH
	default:
		return UNKNOWN_METH
	}
}

func (s SIPProc) GetMethodNameID() int {
	method := s.SIPHeader.Method
	switch method {
	case "INVITE":
		return MNAME_INVITE
	case "BYE":
		return MNAME_BYE
	case "ACK":
		return MNAME_ACK
	case "SIP/2.0":
		return MNAME_OK200
	default:
		return MNAME_UNKNOWN
	}
}

func GetMethodName(id int) (method string) {
	switch id {
	case MNAME_INVITE:
		method = "INVITE"
	case MNAME_BYE:
		method = "BYE"
	case MNAME_ACK:
		method = "ACK"
	case MNAME_OK200:
		method = "200 OK"
	case MNAME_UNKNOWN:
		method = "UNKNOWN"
	}

	return method
}

func NewSDP() *SDP {
	s := &SDP{}
	return s
}

func (s *SIPProc) SDPParse(sdp string) {
	if len(sdp) == 0 {
		return
	}

	d := NewSDP()

	sdplines := strings.Split(sdp, "\r\n")
	for _, line := range sdplines {
		sepline := strings.SplitN(line, "=", 2)
		switch v := strings.ToUpper(sepline[0]); v {
		case "C":
			l := strings.Split(sepline[1], " ")
			d.Connection = l[len(l)-1]
		case "M":
			l := strings.Split(sepline[1], " ")
			d.Port, _ = strconv.Atoi(l[1])
			d.PayloadType, _ = strconv.Atoi(l[3])
		}
	}

	s.SDP = d
	//fmt.Println("Connection:", d.Connection, ", Port:", d.Port, ", PayloadType:", d.PayloadType)
}

func (s SIPProc) GetRemoteRTPAddr() *icsnet.IcsNetAddr {
	addr := icsnet.NewNetAddrWithIPPort(s.SDP.Connection, s.SDP.Port)
	return &addr
}
