package server

import (
	"errors"
	"github.com/astaxie/beego"
	"github.com/cnlh/easyProxy/bridge"
	"github.com/cnlh/easyProxy/utils"
	"log"
	"reflect"
	"strings"
	"sync"
)

var (
	Bridge    *bridge.Tunnel
	RunList   map[string]interface{} //运行中的任务
	CsvDb     *Csv
	VerifyKey string
)

func init() {
	RunList = make(map[string]interface{})
}

//从csv文件中恢复任务
func InitFromCsv() {
	for _, v := range CsvDb.Tasks {
		if v.Start == 1 {
			log.Println("启动模式：", v.Mode, "监听端口：", v.TcpPort, "客户端令牌：", v.VerifyKey)
			AddTask(v)
		}
	}
}

//start a new server
func StartNewServer(bridgePort int, cnf *ServerConfig) {
	Bridge = bridge.NewTunnel(bridgePort, RunList)
	if err := Bridge.StartTunnel(); err != nil {
		log.Fatalln("服务端开启失败", err)
	}
	if svr := NewMode(Bridge, cnf); svr != nil {
		RunList[cnf.VerifyKey] = svr
		err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
		if err.Interface() != nil {
			log.Println(err)
		}
	} else {
		log.Fatalln("启动模式不正确")
	}
}

//new a server by mode name
func NewMode(Bridge *bridge.Tunnel, config *ServerConfig) interface{} {
	switch config.Mode {
	case "tunnelServer":
		return NewTunnelModeServer(ProcessTunnel, Bridge, config)
	case "socks5Server":
		return NewSock5ModeServer(Bridge, config)
	case "httpProxyServer":
		return NewTunnelModeServer(ProcessHttp, Bridge, config)
	case "udpServer":
		return NewUdpModeServer(Bridge, config)
	case "webServer":
		InitCsvDb()
		InitFromCsv()
		p, _ := beego.AppConfig.Int("hostPort")
		t := &ServerConfig{
			TcpPort:      p,
			Mode:         "httpHostServer",
			Target:       "",
			VerifyKey:    "",
			U:            "",
			P:            "",
			Compress:     "",
			Start:        1,
			IsRun:        0,
			ClientStatus: 0,
		}
		AddTask(t)
		return NewWebServer(Bridge)
	case "hostServer":
		return NewHostServer(config)
	case "httpHostServer":
		return NewTunnelModeServer(ProcessHost, Bridge, config)
	}
	return nil
}

//stop server
func StopServer(cFlag string) error {
	if v, ok := RunList[cFlag]; ok {
		reflect.ValueOf(v).MethodByName("Close").Call(nil)
		delete(RunList, cFlag)
		if VerifyKey == "" { //多客户端模式关闭相关隧道
			Bridge.DelClientSignal(cFlag)
			Bridge.DelClientTunnel(cFlag)
		}
		if t, err := CsvDb.GetTask(cFlag); err != nil {
			return err
		} else {
			t.Start = 0
			CsvDb.UpdateTask(t)
		}
		return nil
	}
	return errors.New("未在运行中")
}

//add task
func AddTask(t *ServerConfig) error {
	t.CompressDecode, t.CompressEncode = utils.GetCompressType(t.Compress)
	if svr := NewMode(Bridge, t); svr != nil {
		RunList[t.VerifyKey] = svr
		go func() {
			err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
			if err.Interface() != nil {
				log.Println("客户端", t.VerifyKey, "启动失败，错误：", err)
				delete(RunList, t.VerifyKey)
			}
		}()
	} else {
		return errors.New("启动模式不正确")
	}
	return nil
}

//start task
func StartTask(vKey string) error {
	if t, err := CsvDb.GetTask(vKey); err != nil {
		return err
	} else {
		AddTask(t)
		t.Start = 1
		CsvDb.UpdateTask(t)
	}
	return nil
}

//delete task
func DelTask(vKey string) error {
	if err := StopServer(vKey); err != nil {
		return err
	}
	return CsvDb.DelTask(vKey)
}

//init csv from file
func InitCsvDb() *Csv {
	var once sync.Once
	once.Do(func() {
		CsvDb = NewCsv(RunList)
		CsvDb.Init()
	})
	return CsvDb
}

//get key by host from x
func GetKeyByHost(host string) (h *HostList, t *ServerConfig, err error) {
	for _, v := range CsvDb.Hosts {
		s := strings.Split(host, ":")
		if s[0] == v.Host {
			h = v
			t, err = CsvDb.GetTask(v.Vkey)
			return
		}
	}
	err = errors.New("未找到host对应的内网目标")
	return
}

//get task list by page num
func GetServerConfig(start, length int, typeVal string) ([]*ServerConfig, int) {
	list := make([]*ServerConfig, 0)
	var cnt int
	for _, v := range CsvDb.Tasks {
		if v.Mode != typeVal {
			continue
		}
		cnt++
		if start--; start < 0 {
			if length--; length > 0 {
				if _, ok := RunList[v.VerifyKey]; ok {
					v.IsRun = 1
				} else {
					v.IsRun = 0
				}
				if s, ok := Bridge.SignalList[getverifyval(v.VerifyKey)]; ok {
					if s.Len() > 0 {
						v.ClientStatus = 1
					} else {
						v.ClientStatus = 0
					}
				} else {
					v.ClientStatus = 0
				}
				list = append(list, v)
			}
		}

	}
	return list, cnt
}

//get verify value
//when mode is webServer and vKey is not none
func getverifyval(vkey string) string {
	if VerifyKey != "" {
		return utils.Md5(VerifyKey)
	}
	return utils.Md5(vkey)
}
