package client

import (
	"github.com/cnlh/easyProxy/utils"
	"log"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type TRPClient struct {
	svrAddr      string
	tcpNum       int
	connPoolSize int
	tunnelNum    int64
	tunnel       chan bool
	serverStatus bool
	sync.Mutex
	vKey string
}

//new client
func NewRPClient(svraddr string, tcpNum int, vKey string) *TRPClient {
	c := new(TRPClient)
	c.svrAddr = svraddr
	c.tcpNum = tcpNum
	c.vKey = vKey
	c.tunnel = make(chan bool)
	c.connPoolSize = 5
	return c
}

//start
func (s *TRPClient) Start() error {
	for i := 0; i < s.tcpNum; i++ {
		go s.NewConn()
	}
	for i := 0; i < 5; i++ {
		go s.dealChan()
	}
	go s.session()
	return nil
}

//新建
func (s *TRPClient) NewConn() error {
	s.Lock()
	s.serverStatus = false
	conn, err := net.Dial("tcp", s.svrAddr)
	if err != nil {
		log.Println("连接服务端失败,五秒后将重连")
		time.Sleep(time.Second * 5)
		s.Unlock()
		go s.NewConn()
		return err
	}
	s.Unlock()
	return s.processor(utils.NewConn(conn))
}
//处理
func (s *TRPClient) processor(c *utils.Conn) error {
	s.serverStatus = true
	c.SetAlive()
	if _, err := c.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
		return err
	}
	c.WriteMain()
	for {
		flags, err := c.ReadFlag()
		if err != nil {
			log.Println("服务端断开,五秒后将重连", err)
			go s.NewConn()
			break
		}
		switch flags {
		case utils.VERIFY_EER:
			log.Fatalln("vkey:", s.vKey, "不正确,服务端拒绝连接,请检查")
		case utils.WORK_CHAN: //隧道模式，每次开启10个，加快连接速度
		case utils.RES_CLOSE:
			log.Fatal("该vkey被另一客户连接")
		case utils.RES_MSG:
			log.Println("服务端返回错误。")
		default:
			log.Println("无法解析该错误。", flags)
		}
	}
	return nil
}

//隧道模式处理
func (s *TRPClient) dealChan() {
	var err error
	//创建一个tcp连接
	conn, err := net.Dial("tcp", s.svrAddr)
	if err != nil {
		log.Println("connect to ", s.svrAddr, "error:", err)
		return
	}
	//验证
	if _, err := conn.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
		log.Println("connect to ", s.svrAddr, "error:", err)
		return
	}
	//默认长连接保持
	c := utils.NewConn(conn)
	c.SetAlive()
	//写标志
	c.WriteChan()
re:
	atomic.AddInt64(&s.tunnelNum, 1)
	//获取连接的host type(tcp or udp)
	typeStr, host, en, de, crypt, mux, err := c.GetHostFromConn()
	s.tunnel <- true
	atomic.AddInt64(&s.tunnelNum, -1)
	if err != nil {
		c.Close()
		return
	}
	s.ConnectAndCopy(c, typeStr, host, en, de, crypt, mux)
	if mux {
		utils.FlushConn(conn)
		goto re
	} else {
		c.Close()
	}
}

func (s *TRPClient) session() {
	t := time.NewTicker(time.Millisecond * 1000)
	for {
		select {
		case <-s.tunnel:
		case <-t.C:
		}
		if s.serverStatus && s.tunnelNum < 5 {
			go s.dealChan()
		}
	}
}

func (s *TRPClient) ConnectAndCopy(c *utils.Conn, typeStr, host string, en, de int, crypt, mux bool) {
	//与目标建立连接,超时时间为3
	server, err := net.DialTimeout(typeStr, host, time.Second*3)
	if err != nil {
		log.Println("connect to ", host, "error:", err, mux)
		c.WriteFail()
		return
	}
	c.WriteSuccess()
	utils.ReplayWaitGroup(c.Conn, server, en, de, crypt, mux, nil)
}
