//go:build windows

package yu_proxy_pool

import (
	yu_curl "gogs.qqck.cn/s/tools/curl"
	yu_proxy "gogs.qqck.cn/s/tools/proxy"
	yu_strconv "gogs.qqck.cn/s/tools/strconv"
	"strings"
	"sync"
	"time"
)

// Pool 代理池
type Pool struct {
	// 并发锁
	lock sync.Mutex

	// 代理池
	list []*pool_item

	// 代理API
	// 提取链接或直连地址
	url string

	// 0：返回代理列表，1：直连地址，默认为0
	url_type byte

	// 代理有效时长，默认为30秒，单位：秒
	valid_time int64

	// 代理池内有效时间低于该值就视为无效代理，默认为10秒，单位：秒
	failure_time int64

	// 0：http/https，1：socks5
	_type byte

	// 认证账号密码，如果代理需要身份认证的情况下填写
	user, pass string

	domain bool

	// 访问代理API的提取最少间隔，默认不限制
	request_space int64

	// 最后调用时间，单位：秒
	time_last int64

	// 单个 IP 最多使用次数，默认为：1
	get_max int

	// 已提取计数
	get_count int

	response string // 最后提取信息
}

type pool_item struct {
	*yu_proxy.Info
	i int // 已使用计数
}

func New() (t *Pool) {
	t = &Pool{}
	t.list = make([]*pool_item, 0)
	t.SetUrlList().SetValidTime(30).SetFailureTime(10).SetHttp().SetDomain(true).SetGetMax(1)
	return
}

// -----------------------------------------------------------------------------------------------------API

// SetUrl 设置提取代理api链接
func (t *Pool) SetUrl(v string) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.url = v
	return t
}

// GetUrl 获取提取代理api链接
func (t *Pool) GetUrl() string {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.url
}

// SetUrlList 设置提取代理 api 链接 返回类型为：代理列表
func (t *Pool) SetUrlList() *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.url_type = 0
	return t
}

// SetUrlTunnel 设置提取代理 api 链接 返回类型为：隧道代理，直连地址
func (t *Pool) SetUrlTunnel() *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.url_type = 1
	return t
}

// SetValidTime 设置代理有效时长，默认为30秒，单位：秒
func (t *Pool) SetValidTime(v int64) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	if v < 1 {
		v = 30
	}
	t.valid_time = v
	return t
}

// GetValidTime 获取代理有效时长，单位：秒
func (t *Pool) GetValidTime() int64 {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.valid_time
}

// SetFailureTime 设置代理池内有效时间低于该值就视为无效代理，默认为10秒，单位：秒
func (t *Pool) SetFailureTime(v int64) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	if v < 1 {
		v = 10
	}
	t.failure_time = v
	return t
}

// GetFailureTime 获取代理池内有效时间低于该值就视为无效代理，单位：秒
func (t *Pool) GetFailureTime() int64 {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.failure_time
}

// SetHttp 设置代理类型为：http/https
func (t *Pool) SetHttp() *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t._type = 0
	return t
}

// SetSocks5 设置代理类型为：socks5
func (t *Pool) SetSocks5() *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t._type = 1
	return t
}

// SetUser 设置代理认证账号，如果代理需要身份认证的情况下填写
func (t *Pool) SetUser(v string) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.user = v
	return t
}

// GetUser 获取代理认证账号
func (t *Pool) GetUser() string {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.user
}

// SetPass 设置代理认证密码，如果代理需要身份认证的情况下填写
func (t *Pool) SetPass(v string) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.pass = v
	return t
}

// GetPass 获取代理认证密码
func (t *Pool) GetPass() string {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.pass
}

// SetDomain
//
//	@Description: 设置是否由代理解析域名，默认：true
func (t *Pool) SetDomain(value bool) *Pool {
	t.domain = value
	return t
}

// SetRequestSpace 设置访问代理API的提取最少间隔，默认不限制
func (t *Pool) SetRequestSpace(v int64) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.request_space = v
	return t
}

// GetRequestSpace 获取访问代理API的提取最少间隔
func (t *Pool) GetRequestSpace() int64 {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.request_space
}

// SetGetMax 单个 IP 最多使用次数，默认为：1
func (t *Pool) SetGetMax(n int) *Pool {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.get_max = n
	return t
}

// Size 取当前IP池内剩余IP数
func (t *Pool) Size() int {
	t.lock.Lock()
	defer t.lock.Unlock()
	return len(t.list)
}

// Clear 清空代理池提取的代理信息
func (t *Pool) Clear() {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.list = make([]*pool_item, 0)
}

// -----------------------------------------------------------------------------------------------------Get

// Get 计次提取
func (t *Pool) Get() *yu_proxy.Info {
	t.lock.Lock()
	defer t.lock.Unlock()
	if t.get_max < 1 {
		return nil
	}
	var j_get bool
_get:
	for len(t.list) > 0 {
		if !t.list[0].IsValid() {
			t.list = t.list[1:]
			continue
		}
		if t.list[0].i >= t.get_max {
			t.list = t.list[1:]
			continue
		}
		t.list[0].i++
		return t.list[0].Copy()
	}
	if j_get = !j_get; j_get && t.request_url() {
		goto _get
	}
	return nil
}

// GetCheck 计次提取并使用校验函数校验是否可用(只在从列表提取新的IP的时候才会校验)
func (t *Pool) GetCheck(on func(*yu_proxy.Info) bool) *yu_proxy.Info {
	if on == nil {
		return t.Get()
	}
	var j_info *pool_item
	j_check := false
	t.lock.Lock()
	if t.get_max > 0 {
		var j_get bool
	_get:
		for len(t.list) > 0 {
			if !t.list[0].IsValid() {
				t.list = t.list[1:]
				continue
			}
			if t.list[0].i >= t.get_max {
				t.list = t.list[1:]
				continue
			}
			if t.list[0].i == 0 {
				j_info, j_check, t.list = t.list[0], true, t.list[1:]
				break
			}
			t.list[0].i++
			j_info = t.list[0]
			break
		}
		if j_get = !j_get; j_info == nil && j_get && t.request_url() {
			goto _get
		}
	}
	t.lock.Unlock()
	if j_info == nil {
		return nil
	}
	if !j_check {
		return j_info.Copy()
	}
	if !on(j_info.Info) {
		return nil
	}
	j_info.i++
	t.lock.Lock()
	t.list = append(t.list, nil)
	for j_i := len(t.list) - 1; j_i > 0; j_i-- {
		t.list[j_i] = t.list[j_i-1]
	}
	t.list[0] = j_info
	t.lock.Unlock()
	return j_info.Copy()
}

// GetCheckEx 计次提取并使用校验函数校验是否可用(不管是不是从列表提取新的IP都进行校验)
func (t *Pool) GetCheckEx(on func(*yu_proxy.Info) bool) *yu_proxy.Info {
	if on == nil {
		return t.Get()
	}
	var j_info *pool_item
	t.lock.Lock()
	if t.get_max > 0 {
		var j_get bool
	_get:
		for len(t.list) > 0 {
			if !t.list[0].IsValid() {
				t.list = t.list[1:]
				continue
			}
			if t.list[0].i >= t.get_max {
				t.list = t.list[1:]
				continue
			}
			j_info = t.list[0]
			break
		}
		if j_get = !j_get; j_info == nil && j_get && t.request_url() {
			goto _get
		}
	}
	t.lock.Unlock()
	if j_info == nil {
		return nil
	}
	if !on(j_info.Info) {
		return nil
	}
	j_info.i++
	t.lock.Lock()
	t.list = append(t.list, nil)
	for j_i := len(t.list) - 1; j_i > 0; j_i-- {
		t.list[j_i] = t.list[j_i-1]
	}
	t.list[0] = j_info
	t.lock.Unlock()
	return j_info.Copy()
}

func (t *Pool) GetResponse() string {
	return t.response
}

func (t *Pool) request_url() bool {
	if t.url_type == 0 {
		j_time := time.Now().Unix()
		if j_time-t.time_last < t.request_space {
			return false
		}
		j_curl := yu_curl.NewRequest().SetSslVerifyHost(yu_curl.SSLVERSION_DEFAULT).SetSslVerifyPeer(false).SetTimeout(1000 * 30)
		defer j_curl.Close()
		if j_curl.Get(t.url).RespErr() != nil || j_curl.RespStatusCode() != 200 {
			return false
		}
		t.response = j_curl.RespBodyS()
		j_proxy_list := []string(nil)
		if strings.Index(t.response, "\r\n") > -1 {
			j_proxy_list = strings.Split(t.response, "\r\n")
		} else if strings.Index(t.response, "\\r\\n") > -1 {
			j_proxy_list = strings.Split(t.response, "\\r\\n")
		} else if strings.Index(t.response, "\r") > -1 {
			j_proxy_list = strings.Split(t.response, "\r")
		} else if strings.Index(t.response, "\\r") > -1 {
			j_proxy_list = strings.Split(t.response, "\\r")
		} else if strings.Index(t.response, "\n") > -1 {
			j_proxy_list = strings.Split(t.response, "\n")
		} else if strings.Index(t.response, "\\n") > -1 {
			j_proxy_list = strings.Split(t.response, "\\n")
		} else if strings.Index(t.response, "    ") > -1 {
			j_proxy_list = strings.Split(t.response, "    ")
		} else if strings.Index(t.response, "<br/>") > -1 {
			j_proxy_list = strings.Split(t.response, "<br/>")
		} else {
			j_proxy_list = append(j_proxy_list, t.response)
		}
		t.time_last, j_time = j_time, j_time+t.valid_time-t.failure_time
		for j_i := 0; j_i < len(j_proxy_list); j_i++ {
			j_proxy_info := &pool_item{Info: yu_proxy.NewInfo().SetAuthorization(t.user, t.pass)}
			if strings.Index(j_proxy_list[j_i], " ") > -1 {
				if j_ip_port := strings.Split(j_proxy_list[j_i], " "); len(j_ip_port) == 3 {
					j_proxy_list[j_i] = j_ip_port[0]
					j_proxy_info.User, j_proxy_info.Pass = j_ip_port[1], j_ip_port[2]
				}
			}
			if strings.Count(j_proxy_list[j_i], ".") == 3 && strings.Count(j_proxy_list[j_i], ":") == 1 {
				j_addr := strings.Split(j_proxy_list[j_i], ":")
				j_proxy_info.Host, j_proxy_info.Port, j_proxy_info.Type, j_proxy_info.Timeout, j_proxy_info.Domain = j_addr[0], yu_strconv.ParseInt(j_addr[1]), t._type, j_time, t.domain
				t.list = append(t.list, j_proxy_info)
			}
		}
		return len(t.list) > 0
	} else if t.url_type == 1 {
		j_len := 100
		t.list = make([]*pool_item, j_len)
		for j_num := 0; j_num < j_len; j_num++ {
			t.list[j_num] = &pool_item{}
			t.list[j_num].Info = yu_proxy.NewInfo()
			t.list[j_num].Host = t.url
			t.list[j_num].User = t.user
			t.list[j_num].Pass = t.pass
			t.list[j_num].Type = t._type
			t.list[j_num].Timeout = -1
			t.list[j_num].Domain = t.domain
		}
		return true
	}
	return false
}
