package icsnet

import (
	"net"
	"strings"
	"time"

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

const (
	NET_BUFFER_LENGTH = 4096
)

type IcsTCPNet struct {
	laddr    *IcsNetAddr
	raddr    *IcsNetAddr
	conn     *IcsConn
	listener *net.TCPListener
}

func NewTCP(laddr, raddr *IcsNetAddr) (tcpnet *IcsTCPNet) {
	tcpnet = &IcsTCPNet{
		laddr: laddr,
		raddr: raddr,
		conn:  &IcsConn{},
	}

	return tcpnet
}

func (t *IcsTCPNet) Listen() *icserror.IcsError {
	addr, err := t.laddr.ResolveIcsNetAddr("tcp")
	if err != nil {
		return err
	}

	var aerr error
	switch v := addr.(type) {
	case net.TCPAddr:
		t.listener, aerr = net.ListenTCP("tcp", &v)
		if aerr != nil {
			icserror.ICSERRNETListenError.SetError(aerr)
			return icserror.ICSERRNETListenError
		}
	}

	return nil
}

func (t *IcsTCPNet) Accept() (conn *net.TCPConn, ierr *icserror.IcsError) {
	var err error
	if t.listener != nil {
		conn, err = t.listener.AcceptTCP()
		//t.conn.ICSTCPConn, err = t.listener.AcceptTCP()
		if err != nil {
			icserror.ICSERRNETAcceptError.SetError(err)
			return nil, icserror.ICSERRNETAcceptError
		}
	} else {
		return nil, icserror.ICSERRNETAcceptError
	}

	return conn, nil
}

func (t *IcsTCPNet) Connect(localAddr, remoteAddr *IcsNetAddr) *icserror.IcsError {
	if remoteAddr != nil {
		t.raddr = remoteAddr
	}
	raddr, rerr := t.raddr.ResolveIcsNetAddr("tcp")
	if rerr != nil {
		return rerr
	}

	var aerr error
	switch v := raddr.(type) {
	case net.TCPAddr:
		t.conn.ICSTCPConn, aerr = net.DialTCP("tcp", nil, &v)
		if aerr != nil {
			icserror.ICSERRNETConnectError.SetError(aerr)
			return icserror.ICSERRNETConnectError
		}
	}

	return nil
}

func (t *IcsTCPNet) Close() *icserror.IcsError {
	if t.conn.ICSTCPConn == nil {
		return icserror.ICSERRNETNotConnectError
	}

	aerr := t.conn.ICSTCPConn.Close()
	if aerr != nil {
		icserror.ICSERRNETNotConnectError.SetError(aerr)
		return icserror.ICSERRNETNotConnectError
	}

	return nil
}

func (t *IcsTCPNet) Write(b []byte) (int, *icserror.IcsError) {
	if t.conn.ICSTCPConn == nil {
		return 0, icserror.ICSERRNETNotConnectError
	}

	wsize, err := t.conn.ICSTCPConn.Write(b)
	if err != nil {
		icserror.ICSERRNETWriteError.SetError(err)
		return -1, icserror.ICSERRNETWriteError
	}

	return wsize, nil
}

func (u *IcsTCPNet) WriteSIP(b []byte) (int, *icserror.IcsError) {
	return 0, nil
}

func (u *IcsTCPNet) WriteRTP(b []byte) (int, *icserror.IcsError) {
	return 0, nil
}

func (t *IcsTCPNet) Read(size int) ([]byte, int, *icserror.IcsError) {
	var rlen int = 0
	buf := make([]byte, size)

	for rlen = 0; ; {
		len, err := t.conn.ICSTCPConn.Read(buf[rlen:])
		//len, err := t.conn.ICSTCPConn.Read(buf)
		if err == nil {
			rlen += len
			if rlen >= size {
				return buf, rlen, nil
			}
		} else {
			icserror.ICSERRNETReadError.SetError(err)
			return nil, -1, icserror.ICSERRNETReadError
		}
	}
}

func (t *IcsTCPNet) ReadS(length int, lineEnd string) ([]byte, int, *icserror.IcsError) {
	var rlen int = 0
	size := NET_BUFFER_LENGTH
	if length > 0 {
		size = length
	}
	buf := make([]byte, size)
	//fmt.Println("Reads make size:", size, "buf size:", len(buf))

	for rlen = 0; ; {
		rbuf := make([]byte, size)
		len, err := t.conn.ICSTCPConn.Read(rbuf)
		if err == nil {
			rlen += len
			copy(buf[:rlen], rbuf)
			if rlen >= size {
				return buf, rlen, nil
			} else if strings.Contains(string(buf), lineEnd) {
				return buf, rlen, nil
			}
		} else {
			icserror.ICSERRNETReadError.SetError(err)
			return nil, -1, icserror.ICSERRNETReadError
		}
	}
}

func (u *IcsTCPNet) ReadSIP() ([]byte, *net.UDPAddr, int, *icserror.IcsError) {
	return nil, nil, 0, nil
}

func (u *IcsTCPNet) ReadRTP() ([]byte, *net.UDPAddr, int, *icserror.IcsError) {
	return nil, nil, 0, nil
}

func (t *IcsTCPNet) LocalAddr() net.Addr {
	return t.conn.ICSTCPConn.LocalAddr()
}

func (t *IcsTCPNet) RemoteAddr() net.Addr {
	return t.conn.ICSTCPConn.RemoteAddr()
}

func (t *IcsTCPNet) SetRemoteAddr(raddr *IcsNetAddr) {
	t.raddr = raddr
}

// func (t *IcsTCPNet) SetLinger(sec int) {
// 	t.conn.ICSTCPConn.SetLinger(sec)
// }

func (t *IcsTCPNet) SetLinger() {
	conf := icsconf.GetIcsConfig()
	t.conn.ICSTCPConn.SetLinger(conf.Linger)
}

func (t *IcsTCPNet) SetDeadline(sec int64) {
	t.conn.ICSTCPConn.SetDeadline(time.Now().Add(time.Second * time.Duration(sec)))
}

func (t *IcsTCPNet) SetWriteDeadline(sec int64) {
	t.conn.ICSTCPConn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(sec)))
}

func (t *IcsTCPNet) SetReadDeadline(sec int64) {
	t.conn.ICSTCPConn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(sec)))
}

func SendCallSignal(laddr, raddr *IcsNetAddr, buf []byte) (t *IcsTCPNet, sendlen int, err *icserror.IcsError) {
	t = NewTCP(laddr, raddr)
	//connect to VoiceAgent
	err = t.Connect(laddr, raddr)
	if err != nil {
		return nil, -1, err
	}
	//defer t.Close()

	// t.SetLinger(1)
	t.SetLinger()
	t.SetDeadline(180)

	//send call signal
	sendlen, err = t.Write(buf)
	if err != nil {
		return nil, -1, err
	}

	return t, sendlen, err
}

//func ListenAndServeTCP(laddr, raddr *IcsNetAddr, bufend string, f func(*net.TCPConn, string)) (t *IcsTCPNet, err *icserror.IcsError) {
func ListenAndServeTCP(laddr, raddr *IcsNetAddr, bufend string, f func(*IcsTCPNet, string)) (t *IcsTCPNet, err *icserror.IcsError) {
	t = NewTCP(laddr, raddr)
	err = t.Listen()
	if err != nil {
		return nil, err
	}
	//defer t.listener.Close()

	l := icslog.GetIcsLog()
	go func() {
		//fmt.Printf("Started TCP Listen - %s\n", laddr.String())
		l.Printf(icslog.LOG_LEVEL_INFO, -1, "Started TCP Listen - %s", laddr.String())

		for {
			var conn *net.TCPConn
			var accepterr *icserror.IcsError
			if conn, accepterr = t.Accept(); accepterr != nil {
				//if accepterr := t.Accept(); accepterr != nil {
				continue
			}
			newT := NewTCP(nil, nil)
			newT.conn.ICSTCPConn = conn
			// newT.SetLinger(1)
			newT.SetLinger()
			newT.SetDeadline(180)
			l.Printf(icslog.LOG_LEVEL_DEBUG, -1, "Connected - [%s][%s]", conn.LocalAddr(), conn.RemoteAddr())
			//go f(conn, bufend)
			go f(newT, bufend)
		}
	}()

	return
}

func (t *IcsTCPNet) CloseListener() *icserror.IcsError {
	if t.listener == nil {
		return icserror.ICSERRNETNotConnectError
	}

	aerr := t.listener.Close()
	if aerr != nil {
		icserror.ICSERRNETNotConnectError.SetError(aerr)
		return icserror.ICSERRNETNotConnectError
	}

	return nil
}
