//go:build windows

package yu_curl

import (
	yu_http "gogs.qqck.cn/s/tools/http"
	yu_strings "gogs.qqck.cn/s/tools/strings"
	yu_sys "gogs.qqck.cn/s/tools/sys"
	"runtime"
	"strings"
	"unsafe"
)

// Request
// @Description: 请求对象
type Request struct {
	curl    uintptr
	perform uintptr

	headers        map[string]string
	headers_setopt bool
	headers_list   uintptr

	useragent string
	referer   string

	cookies        map[string]string
	cookies_setopt bool
	cookies_updata bool

	connect_to        map[string]string
	connect_to_setopt bool
	connect_to_list   uintptr

	// -------------------------------------------------------------------resp
	resp_body        []byte
	resp_headers_raw []byte
	resp_headers     map[string]string
	resp_cookies     map[string]string
}

// NewRequest
//
//	@Description: 创建请求对象
func NewRequest() (t *Request) {
	t = &Request{}
	runtime.SetFinalizer(t, _finalizer)
	t.curl, _, _ = curl_easy_init.Call()
	curl_easy_setopt.Call(t.curl, OPT_NOSIGNAL, 1)
	curl_easy_setopt.Call(t.curl, OPT_SUPPRESS_CONNECT_HEADERS, 1)

	curl_easy_setopt.Call(t.curl, OPT_PREREQFUNCTION, callback_PREREQFUNCTION_ptr)
	curl_easy_setopt.Call(t.curl, OPT_PREREQDATA, unsafe.Pointer(t))

	curl_easy_setopt.Call(t.curl, OPT_HEADERFUNCTION, callback_HEADERFUNCTION_ptr)
	curl_easy_setopt.Call(t.curl, OPT_HEADERDATA, unsafe.Pointer(t))

	curl_easy_setopt.Call(t.curl, OPT_WRITEFUNCTION, callback_WRITEFUNCTION_ptr)
	curl_easy_setopt.Call(t.curl, OPT_WRITEDATA, unsafe.Pointer(t))

	// ------------------------------------------------init
	t.SetRdirect(true)
	t.SetSslVerifyPeer(false)
	t.SetTimeout(60000)
	return
}

func _finalizer(t *Request) {
	t.Close()
}

// Close
//
//	@Description: 关闭并销毁请求对象
func (t *Request) Close() {
	if t.curl != 0 {
		curl_easy_cleanup.Call(t.curl)
		t.curl = 0
	}

	t.headers = nil
	if t.headers_list != 0 {
		curl_slist_free_all.Call(t.headers_list)
		t.headers_list = 0
	}

	t.cookies = nil

	t.connect_to = nil
	if t.connect_to_list != 0 {
		curl_slist_free_all.Call(t.connect_to_list)
		t.connect_to_list = 0
	}

	t.resp_body = nil
	t.resp_headers_raw = nil
	t.resp_headers = nil
	t.resp_cookies = nil
}

func (t *Request) opt() {
	if t.headers_setopt {
		if t.headers_list != 0 {
			curl_slist_free_all.Call(t.headers_list)
			t.headers_list = 0
			curl_easy_setopt.Call(t.curl, OPT_HTTPHEADER, 0)
		}
		for j_key, j_val := range t.headers {
			t.headers_list, _, _ = curl_slist_append.Call(t.headers_list, yu_sys.S{yu_sys.Gbk, j_key + ":" + j_val})
		}
		curl_easy_setopt.Call(t.curl, OPT_HTTPHEADER, t.headers_list)
		t.headers_setopt = false
	}
	if t.cookies_setopt {
		curl_easy_setopt.Call(t.curl, OPT_COOKIE, yu_sys.S{yu_sys.Gbk, t.GetCookies()})
		t.cookies_setopt = false
	}
	if t.connect_to_setopt {
		if t.connect_to_list != 0 {
			curl_slist_free_all.Call(t.connect_to_list)
			t.connect_to_list = 0
		}
		for j_host, j_to := range t.connect_to {
			t.connect_to_list, _, _ = curl_slist_append.Call(t.connect_to_list, yu_sys.S{yu_sys.Gbk, j_host + ":" + j_to})
		}
		curl_easy_setopt.Call(t.curl, OPT_CONNECT_TO, t.connect_to_list)
		t.connect_to_setopt = false
	}
}

// -----------------------------------------------------------------------------------------------------------------------------------------------Headers

// SetHeader
//
//	@Description: 设置单个协议头
func (t *Request) SetHeader(name, value string) *Request {
	name = yu_http.Normalize(name)
	if name == "Cookie" {
		t.SetCookies(value)
	} else if name == "User-Agent" {
		t.SetUserAgent(value)
	} else if name == "Referer" {
		t.SetReferer(value)
	} else {
		if t.headers == nil {
			t.headers = make(map[string]string)
		}
		if value == "" {
			t.headers[name] = " " // " " 才可以删除
		} else {
			t.headers[name] = value
		}
		t.headers_setopt = true
	}
	return t
}

// SetHeaders
//
//	@Description: 清空之前的协议头并设置新的协议头
func (t *Request) SetHeaders(headers string) *Request {
	if headers == "" {
		t.headers = nil
		t.headers_setopt = true
	} else {
		t.headers = make(map[string]string)
		t.AddHeaders(headers)
	}
	return t
}

// AddHeaders
//
//	@Description: 保留之前的协议头并添加新的协议头
func (t *Request) AddHeaders(headers string) *Request {
	if headers != "" {
		var j_headers []string
		if strings.Index(headers, "\r\n") > -1 {
			j_headers = strings.Split(headers, "\r\n")
		} else if strings.Index(headers, "\n") > -1 {
			j_headers = strings.Split(headers, "\n")
		}
		for j_i := 0; j_i < len(j_headers); j_i++ {
			j_key, j_value := yu_strings.Left(j_headers[j_i], ":"), yu_strings.EmptyLeft(yu_strings.Right(j_headers[j_i], ":"))
			if j_key == "" && j_value == "" {
				if j_header := yu_strings.EmptyRight(j_headers[j_i]); strings.HasSuffix(j_header, ";") {
					t.SetHeader(j_header, "")
				}
			} else if j_key != "" {
				t.SetHeader(j_key, j_value)
			}
		}
	}
	return t
}

// GetHeader
//
//	@Description: 获取单个协议头
func (t *Request) GetHeader(name string) string {
	name = yu_http.Normalize(name)
	if name == "Cookie" {
		return t.GetCookies()
	} else if name == "User-Agent" {
		return t.GetUserAgent()
	} else if name == "Referer" {
		return t.GetReferer()
	} else {
		if t.headers[name] == " " {
			return ""
		}
		return t.headers[name]
	}
}

// GetHeaders
//
//	@Description: 获取全部协议头
func (t *Request) GetHeaders() string {
	if len(t.headers) == 0 {
		return ""
	}
	j_headers := make([]byte, 0, 4096)
	for j_name, j_value := range t.headers {
		j_headers = append(j_headers, j_name...)
		j_headers = append(j_headers, ':')
		j_headers = append(j_headers, ' ')
		j_headers = append(j_headers, j_value...)
		j_headers = append(j_headers, 13, 10)
	}
	return string(j_headers)
}

// -----------------------------------------------------------------------------------------------------------------------------------------------Content-Type

// SetContentType
//
//	@Description: 设置提交的"ContentType"类型
func (t *Request) SetContentType(value string) *Request {
	t.SetHeader("Content-Type", value)
	return t
}

// -----------------------------------------------------------------------------------------------------------------------------------------------Headers-Utils

// SetUserAgent
//
//	@Description: 设置请求的 UserAgent
func (t *Request) SetUserAgent(value string) *Request {
	t.useragent = value
	curl_easy_setopt.Call(t.curl, OPT_USERAGENT, yu_sys.S{yu_sys.Gbk, t.useragent})
	return t
}

// GetUserAgent
//
//	@Description: 获取请求的 UserAgent
func (t *Request) GetUserAgent() string {
	return t.useragent
}

// SetReferer
//
//	@Description: 设置来路地址
func (t *Request) SetReferer(value string) *Request {
	t.referer = value
	curl_easy_setopt.Call(t.curl, OPT_REFERER, yu_sys.S{yu_sys.Gbk, t.referer})
	return t
}

// GetReferer
//
//	@Description: 获取来路地址
func (t *Request) GetReferer() string {
	return t.referer
}

// SetAutoReferer
//
//	@Description: 自动更新引用标头，启用后，会自动将 HTTP 请求中的 Referer: 标头字段设置为完整的 URL，它位于 Location: 重定向之后。默认为：false, 禁用
func (t *Request) SetAutoReferer(enable bool) *Request {
	curl_easy_setopt.Call(t.curl, OPT_AUTOREFERER, enable)
	return t
}

// -----------------------------------------------------------------------------------------------------------------------------------------------Cookies

// SetCookie
//
//	@Description: 设置单个 Cookie
func (t *Request) SetCookie(name, value string) *Request {
	if value == "" {
		delete(t.cookies, name)
	} else {
		if t.cookies == nil {
			t.cookies = make(map[string]string)
		}
		t.cookies[name] = value
	}
	t.cookies_setopt = true
	return t
}

// SetCookies
//
//	@Description: 清空之前的 Cookies 并设置新的 Cookies
func (t *Request) SetCookies(value string) *Request {
	if value == "" {
		t.cookies = nil
		t.cookies_setopt = true
	} else {
		t.cookies = make(map[string]string)
		t.AddCookies(value)
	}
	return t
}

// AddCookies
//
//	@Description: 保留之前的 Cookies 并增加新的 Cookies
func (t *Request) AddCookies(val string) *Request {
	if val != "" {
		for _, j_cookie := range strings.Split(val, ";") {
			if j_key, j_val := yu_strings.Left(j_cookie, "="), yu_strings.Empty(yu_strings.Right(j_cookie, "=")); j_key != "" {
				t.SetCookie(j_key, j_val)
			}
		}
	}
	return t
}

// GetCookie
//
//	@Description: 获取单个 Cookie
func (t *Request) GetCookie(name string) string {
	return t.cookies[name]
}

// GetCookies
//
//	@Description: 获取全部 Cookie
func (t *Request) GetCookies() string {
	if len(t.cookies) == 0 {
		return ""
	}
	j_cookies := make([]byte, 0, 1024)
	for j_name, j_value := range t.cookies {
		j_cookies = append(j_cookies, j_name...)
		j_cookies = append(j_cookies, '=')
		j_cookies = append(j_cookies, j_value...)
		j_cookies = append(j_cookies, ';')
		j_cookies = append(j_cookies, ' ')
	}
	return string(j_cookies)
}

// SetAutoCookies
//
//	@Description: 设置自动合并更新Cookies，初始值为"false"
func (t *Request) SetAutoCookies(enable bool) *Request {
	t.cookies_updata = enable
	return t
}

// -----------------------------------------------------------------------------------------------------------------------------------------------Utils

// SetTimeout
//
//	@Description: 设置整个请求的超时，单位：毫秒，默认超时为 60000（60秒），设置为零这意味着它在传输过程中永远不会超时。
func (t *Request) SetTimeout(s int) *Request {
	if s < 0 {
		s = 0
	}
	curl_easy_setopt.Call(t.curl, OPT_TIMEOUT_MS, s)
	return t
}

// SetRdirect
//
//	@Description: 是否遵循 HTTP 3xx 重定向，默认：true
func (t *Request) SetRdirect(enable bool) *Request {
	curl_easy_setopt.Call(t.curl, OPT_FOLLOWLOCATION, enable)
	return t
}

// SetProxy 设置代理
//
//	@Description:
//	@param proxy 格式 ProxyType://user:pass@host:port，ProxyType: http, socks4, socks4a, socks5, socks5h，设置为""以取消代理
func (t *Request) SetProxy(proxy string) *Request {
	curl_easy_setopt.Call(t.curl, OPT_PROXY, yu_sys.S{yu_sys.Gbk, proxy})
	return t
}

// SetMaxConnects
//
//	@Description: 设置的数量将是 libcurl 可以在与此句柄关联的池中缓存的同时打开的最大持久连接数。默认值为 5，更改此值没有多大意义，
//	 除非您完全了解它的工作原理并更改 libcurl 的行为。这涉及使用任何支持持久连接的协议的连接。
//	 当达到最大限制时，curl 会关闭缓存中最早的一个，以防止增加打开的连接数。
//	 如果您已经使用此 curl 句柄执行传输，则设置比以前更小的CURLOPT_MAXCONNECTS可能会导致打开的连接不必要地关闭。
//	 如果将此简单句柄添加到多句柄，则不会确认此设置，您必须改为使用curl_multi_setopt和CURLMOPT_MAXCONNECTS选项。
//	 默认5个
//	@param s
func (t *Request) SetMaxConnects(s int) *Request {
	if s < 0 {
		s = 0
	}
	curl_easy_setopt.Call(t.curl, OPT_MAXCONNECTS, s)
	return t
}

// SetSslVerifyPeer
//
//	@Description: 验证 SSL 证书，默认为：false
func (t *Request) SetSslVerifyPeer(enable bool) *Request {
	curl_easy_setopt.Call(t.curl, OPT_SSL_VERIFYPEER, enable)
	return t
}

// SetSslVerifyHost
//
//	@Description: 验证 SSL 证书中的主机名
//	 指定要验证的内容。
//	 此选项确定是否验证服务器证书是否适用于已知的服务器。
//	 在协商TLS和SSL连接时，服务器会发送一个表明其身份的证书。
//	 当为2时，该证书必须表明服务器是您要连接的服务器，否则连接失败。简单地说，这意味着它必须在证书中与您操作的URL中具有相同的名称。
//	 当证书中的“通用名称”字段或“使用者备用名称”字段与您要求Curl连接的URL中的主机名匹配时，Curl认为服务器是预期的服务器。
//	 如果验证值设置为1：
//	 在7.28.0及更早版本中：被视为某种调试选项，由于经常导致程序员错误而不再受支持。
//	 从7.28.1到7.65.3：将其设置为1则无效，并保持标志不变。
//	 从7.66.0开始：对1和2进行相同处理。
//	 当验证值为0时，无论证书中的名称如何，连接都会成功。小心使用这个能力！
//	 此选项的默认值为 2。
//	 此选项控制检查服务器证书声明的身份。服务器可能在撒谎。要控制说谎，请参见 "SetSslVerifyPeer"。
func (t *Request) SetSslVerifyHost(verify uintptr) *Request {
	curl_easy_setopt.Call(t.curl, OPT_SSL_VERIFYHOST, verify)
	return t
}

// SetHttplVersion
//
//	@Description: 传递一个长版本，设置为下面描述的值之一。他们要求 libcurl 使用特定的 HTTP 版本。
//	  HTTP_VERSION_NONE
//	  我们不关心库使用什么版本。libcurl 将使用它认为合适的任何内容。
//
//	  HTTP_VERSION_1_0
//	  强制执行 HTTP 1.0 请求。
//
//	  HTTP_VERSION_1_1
//	  执行 HTTP 1.1 请求。
//
//	  HTTP_VERSION_2_0
//	  尝试 HTTP 2 请求。如果无法与服务器协商 HTTP 2，libcurl 将退回到 HTTP 1.1。（在 7.33.0 中添加）
//
//	  当 libcurl 在 HTTPS 上使用 HTTP/2 时，它本身并不坚持使用 TLS 1.2 或更高版本，即使这是规范所要求的。用户可以使用CURLOPT_SSLVERSION添加此版本要求。
//	  在 7.43.0 中添加了别名CURL_HTTP_VERSION_2以更好地反映实际的协议名称。
//
//	  HTTP_VERSION_2TLS
//	  仅尝试通过 TLS (HTTPS) 的 HTTP 2。如果无法与 HTTPS 服务器协商 HTTP 2，libcurl 将回退到 HTTP 1.1。对于明文 HTTP 服务器，libcurl 将使用 1.1。（在 7.47.0 中添加）
//
//	  HTTP_VERSION_2_PRIOR_KNOWLEDGE
//	  在没有 HTTP/1.1 升级的情况下使用 HTTP/2 发出非 TLS HTTP 请求。它需要事先知道服务器直接支持 HTTP/2。HTTPS 请求仍将以标准方式执行 HTTP/2，并在 TLS 握手中协商协议版本。（在 7.49.0 中添加）
//
//	  HTTP_VERSION_3
//	  （在 7.66.0 中添加）此选项使 libcurl 尝试对 URL 中给定的主机使用 HTTP/3，并在需要时回退到早期的 HTTP 版本。
func (t *Request) SetHttplVersion(ver uintptr) *Request {
	curl_easy_setopt.Call(t.curl, OPT_HTTP_VERSION, ver)
	return t
}

// SetConnectTo
//
//	@Description: 设置DNS转向
//	@param old 要拦截的 域名:端口，数字IPv6地址必须写在[括号]内，例如："example.com"、"example.com:443"、"17.59.98.164:"、"17.59.98.164:80"、"[240e:e9:6002:189:0:ff:b06d:69df]"、"[240e:e9:6002:189:0:ff:b06d:69df]:443"
//	@param new 目标 域名:端口，数字IPv6地址必须写在[括号]内，例如："example.com"、"example.com:443"、"17.59.98.164:"、"17.59.98.164:80"、"[240e:e9:6002:189:0:ff:b06d:69df]"、"[240e:e9:6002:189:0:ff:b06d:69df]:443"
func (t *Request) SetConnectTo(old, new string) *Request {
	if t.connect_to == nil {
		t.connect_to = make(map[string]string)
	}
	if new == "" {
		delete(t.connect_to, old)
	} else {
		t.connect_to[old] = new
	}
	t.connect_to_setopt = true
	return t
}

// CleanConnectTo
//
//	@Description: 清空DNS转向
func (t *Request) CleanConnectTo() *Request {
	t.connect_to = nil
	t.connect_to_setopt = true
	return t
}

// SetSSLVersion
//
//	@Description:传递一个长参数来控制要使用的 SSL/TLS 版本的版本范围。
//	  SSL 和 TLS 版本通常从最不安全的版本发展到越来越安全的顺序：SSL v2、SSLv3、TLS v1.0、TLS v1.1、TLS v1.2 和最新的 TLS v1 .3.
//	  为此目的使用可用的定义之一。可用的选项是：
//
//	  SSLVERSION_DEFAULT
//	  默认可接受的版本范围。自 7.39.0 起，最低可接受版本默认为 TLS v1.0（除非 TLS 库有更严格的规则）。
//
//	  SSLVERSION_TLSv1
//	  # TLS v1.0 或更高版本
//
//	  SSLVERSION_SSLv2
//	  # SSL v2 - 拒绝
//
//	  SSLVERSION_SSLv3
//	  # SSL v3 - 拒绝
//
//	  SSLVERSION_TLSv1_0
//	  TLS v1.0 或更高版本（在 7.34.0 中添加）
//
//	  SSLVERSION_TLSv1_1
//	  TLS v1.1 或更高版本（在 7.34.0 中添加）
//
//	  SSLVERSION_TLSv1_2
//	  TLS v1.2 或更高版本（在 7.34.0 中添加）
//
//	  SSLVERSION_TLSv1_3
//	  TLS v1.3 或更高版本（在 7.52.0 中添加）
func (t *Request) SetSSLVersion(ver uintptr) *Request {
	curl_easy_setopt.Call(t.curl, OPT_SSLVERSION, ver)
	return t
}

// -----------------------------------------------------------------------------------------------------------------------------------------------Send

// Get
//
//	@Description: 发起Get请求
func (t *Request) Get(url string) *Request {
	t.SetContentType("")
	t.SetHeader("Content-Encoding", "")
	curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url})
	curl_easy_setopt.Call(t.curl, OPT_HTTPGET, 1)
	t.opt()
	t.perform, _, _ = curl_easy_perform.Call(t.curl)
	return t
}

// Post
//
//	@Description: 发起Post请求
func (t *Request) Post(url string, data []byte) *Request {
	curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url})
	curl_easy_setopt.Call(t.curl, OPT_POST, 1)
	curl_easy_setopt.Call(t.curl, OPT_POSTFIELDSIZE, len(data))
	if len(data) > 0 {
		curl_easy_setopt.Call(t.curl, OPT_COPYPOSTFIELDS, data)
	}
	t.opt()
	t.perform, _, _ = curl_easy_perform.Call(t.curl)
	return t
}

// PostS
//
//	@Description: 发起Post请求
func (t *Request) PostS(url string, data string) *Request {
	curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url})
	curl_easy_setopt.Call(t.curl, OPT_POST, 1)
	curl_easy_setopt.Call(t.curl, OPT_POSTFIELDSIZE, len(data))
	if len(data) > 0 {
		curl_easy_setopt.Call(t.curl, OPT_COPYPOSTFIELDS, data)
	}
	t.opt()
	t.perform, _, _ = curl_easy_perform.Call(t.curl)
	return t
}

// // OpenWs
// //
// //	@Description: 发起 Ws 请求
// func (t *Request) OpenWs(url string) *Request {
// 	t.SetContentType("")
// 	t.SetHeader("Content-Encoding", "")
// 	curl_easy_setopt.Call(t.curl, OPT_URL, yu_sys.S{yu_sys.Gbk, url})
// 	curl_easy_setopt.Call(t.curl, OPT_CONNECT_ONLY, 2)
// 	t.opt()
// 	t.perform, _, _ = curl_easy_perform.Call(t.curl)
// 	return t
// }
