package icsnet

import (
	"net"
	"strings"
	"time"

	"gitlab.com/cinnamon/voiceagent/icserror"
)

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 {
	if t.laddr == nil {
		return icserror.ICSERRNETResolveAddrError
	}

	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 {
			//fmt.Println(addr, v, aerr)
			icserror.ICSERRNETListenError.SetError(aerr)
			return icserror.ICSERRNETListenError
		}
	}

	return nil
}

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

	return nil
}

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

	switch v := laddr.(type) {
	case net.TCPAddr:
		var err error
		t.conn.ICSTCPConn, err = net.DialTCP("tcp", nil, &v)
		if err != nil {
			icserror.ICSERRNETConnectError.SetError(err)
			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
	}

	bsize := len(b)
	var wlen int
	for wlen = 0; ; {
		wsize, err := t.conn.ICSTCPConn.Write(b[wlen:])
		if err == nil {
			wlen += wsize
			if wlen >= bsize {
				return wlen, nil
			}
		} else {
			icserror.ICSERRNETWriteError.SetError(err)
			return -1, icserror.ICSERRNETWriteError
		}
	}
}

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)
			//buf = append(buf, 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 (t *IcsTCPNet) LocalAddr() net.Addr {
	return t.conn.ICSTCPConn.LocalAddr()
}

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

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

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) (sendlen int, err *icserror.IcsError) {
	t := NewTCP(laddr, raddr)
	//connect to VoiceAgent
	err = t.Connect()
	if err != nil {
		return -1, err
	}
	defer t.Close()

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

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

	return sendlen, err
}

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

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

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

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

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

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

	return sendlen, err
}
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())
		for {
			if accepterr := t.Accept(); accepterr != nil {
				continue
			}
			go f(t, 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
}