package main

/*
#include <stdlib.h>
*/
import "C"
import (
	"Sunny/SunnyProxy"
	"Sunny/src/net/http"
	"Sunny/websocket"
	"crypto/tls"
	"errors"
	"net/textproto"
	"strings"
	"sync"
	"time"
	"unsafe"
)

var WebSocketMap = make(map[int]interface{})
var WebSocketMapLock sync.Mutex

type WebsocketClient struct {
	err         error
	wb          *websocket.Conn
	call        int
	Pointer     unsafe.Pointer
	Context     int
	synchronous bool
	l           sync.Mutex
}

func LoadWebSocketContext(Context int) *WebsocketClient {
	WebSocketMapLock.Lock()
	s := WebSocketMap[Context]
	WebSocketMapLock.Unlock()
	return s.(*WebsocketClient)
}

//export CreateWebsocket
// 创建 Websocket客户端 对象
func CreateWebsocket() int {
	w := &WebsocketClient{}
	i := int(uintptr(unsafe.Pointer(w)))
	Context := GetContextID(i)
	w.Context = Context
	WebSocketMapLock.Lock()
	WebSocketMap[Context] = w
	WebSocketMapLock.Unlock()
	return Context
}
func DelWebSocketContext(Context int) {
	WebSocketMapLock.Lock()
	delete(WebSocketMap, Context)
	WebSocketMapLock.Unlock()
}

//export RemoveWebsocket
// 释放 Websocket客户端 对象
func RemoveWebsocket(Context int) {
	k := LoadWebSocketContext(Context)
	if k != nil {
		if k.Pointer != nil {
			C.free(k.Pointer)
		}
		if k.wb != nil {
			_ = k.wb.Close()
		}
	}
	DelWebSocketContext(Context)
	DelContextID(Context)
}

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

	}
	return NullString
}

//export WebsocketDial
// Websocket客户端 连接
func WebsocketDial(Context int, url_, head_ *C.char, call int, synchronous bool, ProxyType, ProxyAddr, ProxyUser, ProxyPass *C.char, CertificateConText int) bool {

	w := LoadWebSocketContext(Context)
	if w == nil {
		return false
	}
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	w.call = call

	xurl := AuthCharUTF8(url_)
	head := strings.ReplaceAll(AuthCharUTF8(head_), "\r", "")
	var dialer websocket.Dialer
	Headers := make(http.Header)
	arr := strings.Split(head, "\n")
	for _, v := range arr {
		arr1 := strings.Split(v, ":")
		if len(arr1) >= 2 {
			k := arr1[0]
			kk := textproto.CanonicalMIMEHeaderKey(k)
			if kk == textproto.CanonicalMIMEHeaderKey("Upgrade") ||
				kk == textproto.CanonicalMIMEHeaderKey("Connection") ||
				kk == textproto.CanonicalMIMEHeaderKey("Sec-Websocket-Key") ||
				kk == textproto.CanonicalMIMEHeaderKey("Sec-Websocket-Version") ||
				kk == textproto.CanonicalMIMEHeaderKey("Sec-Websocket-Extensions") {
			} else {
				val := strings.TrimSpace(strings.Replace(v, arr1[0]+":", "", 1))
				Headers.Set(textproto.TrimString(k), val)
			}
		}
	}
	murl := strings.ToLower(xurl)
	if strings.HasPrefix(murl, "https") || strings.HasPrefix(murl, "wss") {
		cr := LoadCertificateContext(CertificateConText)
		if cr == nil {
			cr = &SyCer{}
		}
		if cr.tls == nil {
			cr.tls = &tls.Config{InsecureSkipVerify: true}
		}
		dialer = websocket.Dialer{TLSClientConfig: cr.tls}
	} else {
		dialer = websocket.Dialer{}
	}
	w.synchronous = synchronous

	w.wb, _, w.err = dialer.Dial(xurl, Headers, C.GoString(ProxyAddr), C.GoString(ProxyUser), C.GoString(ProxyPass), C.GoString(ProxyType))

	//w.wb, w.err = websocket.DialConfig(config)
	if w.err != nil {
		return false
	}
	if w.synchronous == false {
		go w.WebsocketRead()
	}
	return true
}

//export WebsocketClose
// Websocket客户端 断开
func WebsocketClose(Context int) {
	w := LoadWebSocketContext(Context)
	if w == nil {
		return
	}
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	if w.wb != nil {
		_ = w.wb.Close()
	}
}

//export WebsocketReadWrite
//Websocket客户端  发送数据
func WebsocketReadWrite(Context int, val uintptr, vallen int, types int) bool {
	data := CStringBytes(val, vallen)
	w := LoadWebSocketContext(Context)
	if w == nil {
		return false
	}
	w.l.Lock()
	defer w.l.Unlock()
	if w.Pointer != nil {
		C.free(w.Pointer)
		w.Pointer = nil
	}
	i := types
	if i != 1 && i != 2 && i != 8 && i != 9 && i != 10 {
		/*
			TextMessage = 1
			BinaryMessage = 2
			CloseMessage = 8
			PingMessage = 9
			PongMessage = 10
		*/
		i = 1
	}
	if w.wb == nil {
		return false
	}
	err := w.wb.WriteMessage(i, data)
	if err != nil {
		s := err.Error()
		WebsocketSendCall([]byte(s), w.call, 3, Context, 255)
		_ = w.wb.Close()
		return false
	}
	return true
}
func (w *WebsocketClient) WebsocketRead() {
	for {
		if w.wb == nil {
			WebsocketSendCall([]byte("Pointer = null"), w.call, 2, w.Context, 255)
			return
		}
		m, msg, err := w.wb.ReadMessage()
		if err != nil {
			s := err.Error()
			WebsocketSendCall([]byte(s), w.call, 2, w.Context, 255)
			_ = w.wb.Close()
			return
		}
		go WebsocketSendCall(msg, w.call, 1, w.Context, m)
	}
}

//export WebsocketClientReceive
//Websocket客户端 同步模式下 接收数据 返回数据指针 失败返回0 lenx=返回数据长度
func WebsocketClientReceive(Context, OutTimes int, messageType, lenx *int) uintptr {
	w := LoadWebSocketContext(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 = 3000
	}
	if w.wb == nil {
		return 0
	}
	w.err = w.wb.SetReadDeadline(time.Now().Add(time.Duration(_OutTime) * time.Millisecond))

	var Buff []byte
	*messageType, Buff, w.err = w.wb.ReadMessage()

	*lenx = len(Buff)
	if *lenx > 0 {
		return uintptr(unsafe.Pointer(&Buff[0]))
	}
	return 0
}
func WebsocketSendCall(b []byte, call, types, Context, messageType int) {
	if call > 10 {
		SunnyProxy.Call(call, Context, types, b, len(b), messageType)
	}

}
