package utils

import (
	"bytes"
	"context"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"
)

type GamemodeId int

const (
	survival GamemodeId = iota
	sandbox
	attack
	pvp
	editor
)

type Gamemode struct {
	Name string     `json:"name"`
	Id   GamemodeId `json:"id"`
}

type ServerInfo struct {
	Host        string   `json:"host"`
	Port        int      `json:"port"`
	Status      string   `json:"status"`
	Name        string   `json:"name"`
	Maps        string   `json:"maps"`
	Players     int      `json:"players"`
	Version     int      `json:"version"`
	Wave        int      `json:"wave"`
	Vertype     string   `json:"vertype"`
	Gamemode    Gamemode `json:"gamemode"`
	Description string   `json:"description"`
	Modename    string   `json:"modename"`
	Limit       int      `json:"limit"`
	Ping        int      `json:"ping"`
}

type InfoBuffer struct {
	*bytes.Reader
}

func (g GamemodeId) Name() string {
	switch g {
	case survival:
		return "生存"
	case sandbox:
		return "沙盒"
	case attack:
		return "进攻"
	case pvp:
		return "PVP"
	case editor:
		return "编辑"
	default:
		return "Error"
	}
}

func (r *InfoBuffer) New(b []byte) {
	r.Reader = bytes.NewReader(b)
}

func (r *InfoBuffer) readString() string {
	l, _ := r.ReadByte()
	buf := make([]byte, l)
	r.Read(buf)
	return string(buf)
}

func (r *InfoBuffer) getInt() int {
	var t int32
	binary.Read(r, binary.BigEndian, &t)
	return int(t)
}

func (r *InfoBuffer) get() byte {
	b, _ := r.ReadByte()
	return b
}

func connectServer(host string) (buf InfoBuffer, err error) {
	socket, err := net.Dial("udp", host)
	if err != nil {
		return InfoBuffer{}, err
	}
	defer socket.Close()
	sendData, _ := hex.DecodeString("FE01")
	_, err = socket.Write(sendData)
	if err != nil {
		return InfoBuffer{}, err
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
	defer cancel()
	workDone := make(chan struct{}, 1)
	data := make([]byte, 2048)
	go func() {
		_, err = socket.Read(data)
		workDone <- struct{}{}
	}()
	select {
	case <-workDone:
		buf.New(data)
	case <-ctx.Done():
		return InfoBuffer{}, fmt.Errorf("超时:%v", err)
	}
	return buf, nil
}
func ping(host string) (int64, error) {
	startTime := time.Now()
	conn, err := net.DialTimeout("tcp", host, time.Second*2)
	if err != nil {
		return 0, err
	}
	conn.Close()
	conn.RemoteAddr()
	elapsedTime := time.Since(startTime).Milliseconds()
	return elapsedTime, nil
}
func GetServerInfo(host string) (ServerInfo, error) {
	var info ServerInfo
	ip := strings.Split(host, ":")
	if len(ip) == 1 {
		info.Host = host
		info.Port = 6567
	} else {
		info.Host = ip[0]
		port, err := strconv.Atoi(ip[1])
		if err != nil {
			return info, err
		}
		info.Port = port
	}
	add := fmt.Sprintf("%s:%d", info.Host, info.Port)
	d, err := ping(add)
	if err != nil {
		info.Status = "Offline"
		return info, err
	}
	info.Ping = int(d)
	buf, err := connectServer(add)
	if err != nil {
		info.Status = "Offline"
		return info, err
	}
	info.Status = "Online"
	info.Name = buf.readString()
	info.Maps = buf.readString()
	info.Players = buf.getInt()
	info.Wave = buf.getInt()
	info.Version = buf.getInt()
	info.Vertype = buf.readString()
	info.Gamemode.Id = GamemodeId(buf.get())
	info.Gamemode.Name = info.Gamemode.Id.Name()
	info.Limit = buf.getInt()
	info.Description = buf.readString()
	info.Modename = buf.readString()
	return info, nil
}