package main

/*
#include <stdlib.h>
*/
import "C"
import (
	"Sunny/SunnyProxy"
	"Sunny/sock5/proxy"
	"bufio"
	"bytes"
	"crypto/tls"
	"errors"
	"net"
	"sync"
	"time"
	"unsafe"
)

type SocketClient struct {
	err         error
	wb          *net.Conn
	call        int
	Pointer     unsafe.Pointer
	Context     int
	tls         *tls.Conn
	BufferSize  int
	synchronous bool
	R           *bufio.Reader
}

var SocketMap = make(map[int]interface{})
var SocketMapLock sync.Mutex

//export CreateSocketClient
// 创建 TCP客户端
func CreateSocketClient() int {
	w := &SocketClient{}
	i := int(uintptr(unsafe.Pointer(w)))
	Context := GetContextID(i)
	w.Context = Context
	SocketMapLock.Lock()
	SocketMap[Context] = w
	SocketMapLock.Unlock()
	return Context
}
func LoadSocketContext(Context int) *SocketClient {
	SocketMapLock.Lock()
	s := SocketMap[Context]
	SocketMapLock.Unlock()
	if s == nil {
		return nil
	}
	return s.(*SocketClient)
}

//export RemoveSocketClient
// 释放 TCP客户端
func RemoveSocketClient(Context int) {
	k := LoadSocketContext(Context)
	if k != nil {
		if k.Pointer != nil {
			C.free(k.Pointer)
		}
		if k.wb != nil {
			k.Close()
		}
	}
	DelClientContext(Context)
	DelContextID(Context)
}
func DelContextID(id int) {
	ContextIDL.Lock()
	delete(ContextMap, id)
	ContextIDL.Unlock()
}

func DelClientContext(Context int) {
	SocketMapLock.Lock()
	delete(SocketMap, Context)
	SocketMapLock.Unlock()
}

//export SocketClientGetErr
//  TCP客户端 取错误
func SocketClientGetErr(Context int) *C.char {
	k := LoadSocketContext(Context)
	if k != nil {
		if k.Pointer != nil {
			C.free(k.Pointer)
		}
		if k.err != nil {
			n := C.CString(AuthUtf8ToGbk(k.err.Error()))
			k.Pointer = unsafe.Pointer(n)
			return n
		}
	}
	return NullString
}

//export SocketClientSetBufferSize
//  TCP客户端 置缓冲区大小
func SocketClientSetBufferSize(Context, BufferSize int) bool {
	k := LoadSocketContext(Context)
	if k != nil {
		k.BufferSize = BufferSize
		if k.BufferSize < 1 {
			k.BufferSize = 4096
		}
		return true
	}
	return false
}

//export SocketClientDial
//  TCP客户端 连接
func SocketClientDial(Context int, addr *C.char, call int, istls, synchronous bool, ProxyIP, ProxyUser, ProxyPass *C.char, CertificateConText int) bool {

	w := LoadSocketContext(Context)
	if w == nil {
		return false
	}
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	w.call = call
	if w.BufferSize < 1 {
		w.BufferSize = 4096
	}
	adds, err := net.ResolveTCPAddr("tcp4", AuthCharUTF8(addr))

	if err != nil {
		w.err = err
		return false
	}
	_ProxyIP := C.GoString(ProxyIP)

	if _ProxyIP != "" {

		auth := &proxy.Auth{User: C.GoString(ProxyUser), Password: C.GoString(ProxyPass)}
		var _Dialer proxy.Dialer
		_Dialer, _, w.err = proxy.SOCKS5("tcp", _ProxyIP, auth, proxy.Direct)
		if w.err != nil {

			return false
		}
		a, b := _Dialer.Dial("tcp", adds.String())
		w.wb = &a
		w.err = b

	} else {

		a, b := net.Dial("tcp", adds.String())
		w.wb = &a
		w.err = b
	}

	w.synchronous = synchronous
	if istls {
		fig := LoadCertificateContext(CertificateConText)
		if fig == nil {
			fig = &SyCer{tls: &tls.Config{InsecureSkipVerify: true}}
		}
		if fig.tls == nil {
			fig.tls = &tls.Config{InsecureSkipVerify: true}
		}
		w.tls = tls.Client(*w.wb, fig.tls)
		w.err = w.tls.Handshake()
	}

	if w.err != nil {
		w.Close()
		return false
	}
	if istls {
		w.R = bufio.NewReaderSize(w.tls, w.BufferSize)
	} else {
		w.R = bufio.NewReaderSize(*w.wb, w.BufferSize)
	}
	if synchronous == false {
		go w.SocketClientRead()
	}
	return true
}

//export SocketClientReceive
//  TCP客户端 同步模式下 接收数据
func SocketClientReceive(Context, OutTimes int, len *int) uintptr {
	w := LoadSocketContext(Context)
	if w == nil {
		w.err = errors.New("The Context does not exist ")
		return 0
	}
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	if w.synchronous == false {
		w.err = errors.New("Not synchronous mode ")
		return 0
	}
	_OutTime := OutTimes
	if _OutTime < 1 {
		_OutTime = 100
	}
	if w.tls != nil {
		_ = w.tls.SetReadDeadline(time.Now().Add(time.Duration(_OutTime) * time.Millisecond))
	} else {
		_ = (*w.wb).SetReadDeadline(time.Now().Add(time.Duration(_OutTime) * time.Millisecond))
	}
	var Buff = make([]byte, w.BufferSize)
	*len, w.err = w.R.Read(Buff[0:])
	if *len > 0 {
		return uintptr(unsafe.Pointer(&Buff[0]))
	}
	return 0
}

//export SocketClientClose
//  TCP客户端 断开连接
func SocketClientClose(Context int) {
	w := LoadSocketContext(Context)
	if w == nil {
		return
	}
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	w.Close()
}

//export SocketClientWrite
//  TCP客户端 发送数据
func SocketClientWrite(Context, OutTimes int, val uintptr, vallen int) int {
	data := CStringBytes(val, vallen)
	w := LoadSocketContext(Context)
	if w == nil {
		return 0
	}
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	_OutTimes := OutTimes
	if _OutTimes < 0 {
		_OutTimes = 30000
	}
	m, err := w.Write(data, _OutTimes)
	if err != nil {
		s := err.Error()
		SocketClientSendCall([]byte(s), w.call, 3, Context)
		w.Close()
		return m
	}
	return m
}
func (w SocketClient) Write(b []byte, OutTimes int) (int, error) {
	if w.tls != nil {
		_ = w.tls.SetWriteDeadline(time.Now().Add(time.Duration(OutTimes) * time.Millisecond))
		return w.tls.Write(b)
	}
	if w.wb != nil && (*w.wb) != nil {
		_ = (*w.wb).SetWriteDeadline(time.Now().Add(time.Duration(OutTimes) * time.Millisecond))
		return (*w.wb).Write(b)
	}
	return 0, errors.New("No Write Object ")
}
func (w SocketClient) Close() {
	if w.tls != nil {
		_ = w.tls.Close()
	}
	if w.wb != nil && (*w.wb) != nil {
		_ = (*w.wb).Close()
	}
}
func (w *SocketClient) SocketClientRead() {
	defer func() {
		if err := recover(); err != nil {

		}
	}()
	non := 0
	for {
		if w.tls != nil {
			_ = w.tls.SetReadDeadline(time.Time{})
		} else {
			_ = (*w.wb).SetReadDeadline(time.Time{})
		}
		response, err := w.readAllShut()
		if len(response) == 0 {
			non++
			if non > 10 {
				SocketClientSendCall([]byte(AuthUtf8ToGbk("连接可能被关闭")), w.call, 2, w.Context)
				w.Close()
				return
			}
			continue
		} else {
			non = 0
			SocketClientSendCall(response, w.call, 1, w.Context)
		}
		if err != nil {
			SocketClientSendCall([]byte(err.Error()), w.call, 2, w.Context)
			w.Close()
			return
		}
	}
}
func (w *SocketClient) readAllShut() ([]byte, error) {
	re := bytes.NewBuffer(nil)
	_bytes := make([]byte, w.BufferSize)
	length, err := w.R.Read(_bytes[0:])
	re.Write(_bytes[:length])
	if err != nil {
		if _, ok := err.(*net.OpError); ok {
			re.Reset()
			rb := re.Bytes()
			re.Reset()
			return rb, err
		}
	}
	rb := re.Bytes()
	re.Reset()
	return rb, nil
}
func SocketClientSendCall(b []byte, call, types, Context int) {
	if call > 0 {
		SunnyProxy.Call(call, Context, types, b, len(b))
	}
}

var CStringBytes = func(r uintptr, datalen int) []byte {
	data := make([]byte, 0)
	for i := 0; i < datalen; i++ {
		data = append(data, *(*byte)(unsafe.Pointer(r + uintptr(i))))
	}
	return data
}
