package SunnyProxy

import (
	"Sunny/SunnyProxy/sock5/proxy"
	"Sunny/src/net/http"
	"Sunny/websocket"
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net"
	"net/textproto"
	"net/url"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

var (
	tunnelConnectionEstablished = []byte("HTTP/1.1 200 Connection Established\r\n\r\n") // 通道连接建立
	internalServerErr           = []byte(fmt.Sprintf("HTTP/1.1 %d %s\r\n\r\n", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)))
)

type Proxy struct {
	delegate Delegate
	dns      *Dns
}

var Nullbyts = []byte("")

func NewWithDelegate(delegate Delegate, rootCa, rootKey string) *Proxy {
	Init(rootCa, rootKey)
	return &Proxy{delegate: delegate, dns: &DefaultDns}
}

func (proxy *Proxy) AddDnsRecord(host, record string) {
	proxy.dns.Add(map[string]string{host: record})
}

func SubString(str, left, Right string) string {
	s := strings.Index(str, left)
	if s < 0 {
		return ""
	}
	s += len(left)
	e := strings.Index(str[s:], Right)
	if e+s <= s {
		return ""
	}
	return str[s : s+e]

}

var upgrader = websocket.Upgrader{
	//此处给CheckOrigin默认一个返回true保证，否则会出现报错自动跳转
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
} // use default options

func (proxy *Proxy) handleWss(w http.ResponseWriter, req *Entity, c net.Conn, b *bufio.ReadWriter, Tsl bool, TheonlyId int64, pid, ip, auth string, timeout int) bool {
	if req.Request.Header.Get("Upgrade") != "" && req.Request.Header.Get("Upgrade") != "mmtls" {
		GlobalLock.Lock()
		//先获取设置的全局代理
		var ProxyType = "http"
		var ProxyAddr = GlobalProxyIp
		var ProxyAuth = GlobalProxyAuth
		var ProxyUser = ""
		var ProxyPass = ""
		GlobalLock.Unlock()
		if ProxyAddr == "" {
			//如果没有设置全局代理 在判断有没有设置条件代理。
			ProxyAddr = ip
			ProxyAuth = auth
		} else {
			arr2 := strings.Split(ProxyAddr, "|")
			if len(arr2) >= 2 {
				ProxyAddr = arr2[1]
				if strings.ToLower(arr2[1]) == "s5" {
					ProxyType = "s5"
				}
			}
		}
		arr := strings.Split(ProxyAuth, ":")
		if len(arr) == 2 {
			ProxyUser = arr[0]
			ProxyPass = arr[1]
		}

		PessPid, _ := strconv.Atoi(pid)
		req.Pid = pid
		/************进行协议升级************/
		upgrader.Subprotocols = []string{req.Request.Header.Get("Sec-WebSocket-Protocol")}
		//upgrader.Upgrade内部会返回握手信息，我们做代理需要将dialer.Dial客户端收到的下层返回的握手信息返回给上层，源码Upgrade函数中将
		//Sec-WebSocket-Protocol这个头去掉了，所以我们给加上,保证在upgrader.Upgrade调用的上方加上
		//func (u *Upgrader) Upgrade(w net.ResponseWriter, r *net.Request, responseHeader net.Header) (*Conn, error) {}
		//upgrader.Subprotocols是属性，upgrader.Upgrade是方法，属性可以初始化，方法在函数定义前进行关联
		//具体实现在 https://github.com/gorilla/websocket/blob/master/server.go
		c_this, err := upgrader.Upgrade(w, req.Request, nil, c, b)
		if err != nil {
			//fmt.Println(err)
			return true
		}
		defer c_this.Close()
		var u url.URL
		/******************启动websocket转发客户端*********/
		//此处使用req.URL.Path+req.URL.RawQuer代替 req.URL.String()是因为使用后者之后下面的u.String()使用url加密，导致url错误造成404
		const Wss_ConnectionOK = 3
		const Wss_User_Send = 4
		const Wss_Server_Send = 5
		const Wss_Disconnect = 6
		const Ws_ConnectionOK = 7
		const Ws_User_Send = 8
		const Ws_Server_Send = 9
		const Ws_Disconnect = 10
		ConnectionOK := 0
		User_Send := 0
		Server_Send := 0
		Disconnect := 0
		if Tsl {
			u = url.URL{Scheme: "wss", Host: req.Request.Host, Path: req.Request.URL.Path, RawQuery: req.Request.URL.RawQuery}
			ConnectionOK = Wss_ConnectionOK
			User_Send = Wss_User_Send
			Server_Send = Wss_Server_Send
			Disconnect = Wss_Disconnect
		} else {
			u = url.URL{Scheme: "ws", Host: req.Request.Host, Path: req.Request.URL.Path, RawQuery: req.Request.URL.RawQuery}
			ConnectionOK = Ws_ConnectionOK
			User_Send = Ws_User_Send
			Server_Send = Ws_Server_Send
			Disconnect = Ws_Disconnect
		}

		//添加头部的时候不能照搬，需要去掉几个固有的，因为库里面已经给你加了，所以代理的时候把重复的去掉，详见源码219行
		//https://github.com/gorilla/websocket/blob/master/client.go
		headers := make(http.Header)
		for k, v := range req.Request.Header {
			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 {
				headers.Set(kk, v[0])
				//fmt.Println("set ==>", k, v[0])
			}
		}
		var dialer websocket.Dialer
		if Tsl {
			dialer = websocket.Dialer{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
		} else {
			dialer = websocket.Dialer{}
		}

		c_to_next, _, err := dialer.Dial(u.String(), headers, ProxyAddr, ProxyUser, ProxyPass, ProxyType)
		if err != nil {
			return true
		}
		//fmt.Println(resp.Header)
		defer c_to_next.Close()

		/*****************接收返回并转回给浏览器*******************/

		go func() {
			as := &WebsocketMsg{This: c_to_next, Mt: 255}
			defer func() {
				DelContextID(int(uintptr(unsafe.Pointer(as))))
			}()
			for {
				mt, message, err := c_to_next.ReadMessage()
				if err != nil {
					break
				}
				as.As = message
				as.Mt = mt
				proxy.delegate.WssRequest(req, Server_Send, as, TheonlyId, PessPid)
				err = c_this.WriteMessage(mt, as.As)
				if err != nil {
					break
				}
			}
		}()
		/*****************接收浏览器信息并转发*******************/
		//此处不能再协程了，否则会defer c_to_next.Close()
		as := &WebsocketMsg{This: c_to_next, Mt: 255}
		defer func() {
			DelContextID(int(uintptr(unsafe.Pointer(as))))
		}()
		proxy.delegate.WssRequest(req, ConnectionOK, as, TheonlyId, PessPid)
		for {
			mt1, message1, err1 := c_this.ReadMessage()
			as.As = message1
			as.Mt = mt1
			if err1 != nil {
				proxy.delegate.WssRequest(req, Disconnect, as, TheonlyId, PessPid)
				break
			}
			proxy.delegate.WssRequest(req, User_Send, as, TheonlyId, PessPid)
			err1 = c_to_next.WriteMessage(mt1, as.As)
			if err1 != nil {
				proxy.delegate.WssRequest(req, Disconnect, as, TheonlyId, PessPid)
				break
			}
		}
		/********************************************/
		return true
	}
	return false
}

// 请求目标服务器
func (proxya *Proxy) doRequest(entity *Entity) (*http.Response, *net.Conn, error) {

	GlobalLock.Lock()
	//先获取设置的全局代理
	var LocalProxy = GlobalProxyIp
	var LocalAuth = GlobalProxyAuth
	var LocalProxyTimeout = GlobalProxyProxyTimeout
	GlobalLock.Unlock()
	if LocalProxy == "" {
		//如果没有设置全局代理 在判断有没有设置条件代理。
		LocalProxy = entity.Ip
		LocalAuth = entity.Auth
		LocalProxyTimeout = entity.ProxyTimeout
	}
	if LocalProxyTimeout == 0 {
		LocalProxyTimeout = 25
	}
	//判断是否S5代理
	arr := strings.Split(LocalProxy, "|")
	if len(arr) >= 2 {
		if strings.ToLower(arr[0]) == "s5" && arr[1] == "" {
			arr = arr[0 : len(arr)-1]
		}
	}
	if len(arr) < 2 {
		//如果不是S5代理 使用HTTP代理请求
		w := NewWinhttp()
		if LocalProxy != "" {
			w.SetProxyIP(LocalProxy)
			if len(LocalAuth) > 0 {
				w.SetProxyBasicAuth(base64.StdEncoding.EncodeToString([]byte(LocalAuth)))
			}
		}
		w.Open(entity.Request.Method, entity.Request.URL.String())
		w.SetTimeouts(time.Duration(LocalProxyTimeout)*time.Second, time.Duration(LocalProxyTimeout)*time.Second, time.Duration(LocalProxyTimeout)*time.Second)

		for header, values := range entity.Request.Header {
			for _, value := range values {
				w.SetHeader(header, value)

			}
		}

		var bodyBytes []byte
		if entity.Request != nil {
			if entity.Request.Body != nil {
				bodyBytes, _ = ioutil.ReadAll(entity.Request.Body)
				entity.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
			}
		}
		return w.SendBin(bodyBytes)
	} else if strings.ToLower(arr[0]) == "s5" {
		//如果是S5代理
		authAA := &proxy.Auth{}
		//判断设置的账号密码
		if len(arr) == 3 {
			authAA.User = arr[2]
		} else if len(arr) == 4 {
			authAA.User = arr[2]
			authAA.Password = arr[3]
		} else if LocalAuth != "" {
			arr1 := strings.Split(LocalAuth, ":")
			if len(arr1) == 1 {
				authAA.User = arr1[0]
			} else if len(arr1) == 2 {
				authAA.User = arr1[0]
				authAA.Password = arr1[1]
			}
		}
		//使用S5代理
		dialerx, cn, err := proxy.SOCKS5("tcp", arr[1], authAA, proxy.Direct)

		if err != nil {
			return nil, nil, err
		}

		var ClientCertificate *tls.Certificate
		if entity.Request.URL != nil {
			s := LoadP12Certificate(entity.Request.URL.Host)
			if s != nil {
				ClientCertificate = s.CryptoTLSCert
			}
		}
		Trans := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			Dial:            dialerx.Dial,
		}
		if ClientCertificate != nil {
			Trans.TLSClientConfig.Certificates = []tls.Certificate{*ClientCertificate}
		}
		Trans.DisableCompression = true
		clt := http.Client{
			Timeout:   time.Duration(LocalProxyTimeout) * time.Second,
			Transport: Trans,
		}
		//重新构建HTTP请求
		url := entity.Request.URL
		url.Host = entity.Request.URL.Host
		var bodyBytes []byte
		if entity.Request.Body != nil {
			bodyBytes, _ = ioutil.ReadAll(entity.Request.Body)
			entity.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
		}
		Body := ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
		proxyReq, err := http.NewRequest(entity.Request.Method, url.String(), Body)
		if err != nil {
			return nil, nil, err
		}
		proxyReq.Header.Set("Host", entity.Request.Host)
		for header, values := range entity.Request.Header {
			for _, value := range values {
				proxyReq.Header.Add(header, value)
			}
		}
		resp, err := clt.Do(proxyReq)

		if err != nil {
			return nil, nil, err
		}

		return resp, cn, nil
	}
	return nil, nil, nil
}

// remove hop header
func removeHopHeader(header http.Header) {
	var hopToHopHeader = []string{
		"Keep-Alive",
		"Transfer-Encoding",
		"TE",
		"Connection",
		"Trailer",
		"Upgrade",
		"Proxy-Authorization",
		"Proxy-Authenticate",
		"Connection",
	}
	for _, hop := range hopToHopHeader {
		if value := header.Get(hop); len(value) != 0 {
			if strings.EqualFold(hop, "Connection") {
				for _, customerHeader := range strings.Split(value, ",") {
					header.Del(strings.Trim(customerHeader, " "))
				}
			}
			header.Del(hop)
		}
	}
}
func (proxy *Proxy) Error(entity *Entity, net net.Conn, TheonlyId int64, error error) {
	proxy.delegate.ErrorLog(entity, error, TheonlyId)
	if net != nil {
		_, _ = net.Write(internalServerErr)
		if error != nil {
			_, _ = net.Write([]byte(error.Error()))
		}
	}

}

func (proxy *Proxy) ErrorLog(entity *Entity, TheonlyId int64, error error) {
	proxy.delegate.ErrorLog(entity, error, TheonlyId)
}

type WebsocketMsg struct {
	As      []byte
	This    *websocket.Conn
	Mt      int
	tcp     net.Conn //TCP相关
	TcpIp   string   //TCP相关
	TcpUser string   //TCP相关
	TcpPass string   //TCP相关
}

type TCPMsg struct {
	As   []byte
	This *websocket.Conn
	Mt   int
}

var WebsocketMsgNull = &WebsocketMsg{}

func Test() {
	/*
		w := newWinhttp()
		w.SetProxyIP("192.168.31.110:8888")
		w.Open("POST", "https://im.moji126.com/im/login")
		w.SetHeader("host", "im.moji126.com")
		w.SetHeader("user-agent", "Platform.Android")
		w.SetHeader("content-type", "application/json; charset=utf-8")
		w.SetHeader("accept-encoding", "gzip")
		w.SetHeader("content-length", "120")
		a, _, e := w.sendBin([]byte(`{"deviceId":"deviceId","deviceInfo":"deviceInfo","password":"58c55fe34ab59b425ee686b089da01e6","username":"13800138000"}`))
		fmt.Println(a, e)
	*/
	w := NewWinhttp()
	w.SetProxyIP("192.168.31.110:8888")
	w.Open("GET", "http://185.25.49.180/test.ssh.txt")
	w.SetHeader("Pragma", "no-cache")
	w.SetHeader("Accept-Encoding", "Platform.Android")
	w.SetHeader("content-type", "gzip")
	w.SetHeader("Host", "185.25.49.180")
	a, _, e := w.SendBin([]byte(``))
	fmt.Println(a, e)
}
