package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/url"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"github.com/gorilla/websocket"
)

/*
go build -o xxx.exe -ldflags "-s -w -H=windowsgui"
运行命令
shell:startup
把程序扔到里面
*/
func GetMac() string {
	netInterfaces, err := net.Interfaces()
	if err != nil {
		return ""
	}
	for _, i2 := range netInterfaces {
		//fmt.Println(i2.Name)
		mac := i2.HardwareAddr
		//fmt.Println(mac)
		return mac.String()
	}
	return ""
}
func main() {
	wss := "ws"
	//获取电脑主机名
	mac := GetMac()
	mac = strings.TrimSpace(mac)
	passwd := "abcdefg"
	room := "computer"
	w := WebsocketClient{}
	w.Setup("192.168.0.254", 80, "/websocket", 10, wss, mac, passwd, room, true).Start()

}

type WebsocketClient struct {
	conn          *websocket.Conn
	addr          *string
	path          string
	Userinfo      interface{}
	sendMsgChan   chan string
	isAlive       bool //联网状态
	timeout       int
	wstype        string //连接 wss 还是ws
	user          string
	passwd        string
	room          string
	StartDate     string // 上线时间
	Ch            chan os.Signal
	authorization bool
}

// 初始化 Setup("192.168.0.254", 443, "/websocket", 10, wss,user, password, room,true).Start()
func (ggg *WebsocketClient) Setup(addrIp string, addrPort int, path string, timeout int, wstype, user, passwd, room string, authorization bool) *WebsocketClient {
	ggg.authorization = authorization
	var conn *websocket.Conn
	ggg.conn = conn
	temp := addrIp + ":" + fmt.Sprintf("%d", addrPort)
	ggg.addr = &temp
	ggg.path = path
	ggg.sendMsgChan = make(chan string, 10)
	ggg.isAlive = false
	ggg.timeout = timeout
	ggg.wstype = wstype
	ggg.user = user
	ggg.passwd = passwd
	ggg.room = room
	ggg.Ch = make(chan os.Signal, 1)
	ggg.StartDate = time.Now().Format("2006-01-02 15:04")

	return ggg
}

// 开启服务并重连
func (ggg *WebsocketClient) Start() {
	for {
		if !ggg.isAlive {
			if ggg.authorization {
				ggg.AuthorizationDail() //Authorization 头信息登录方式
			} else {
				ggg.dail()
			}
			if ggg.isAlive {
				ggg.sendMsgThread()
				ggg.readMsgThread()
			}
		}
		time.Sleep(time.Second * time.Duration(ggg.timeout))
	}
}

// 链接服务端
func (ggg *WebsocketClient) AuthorizationDail() {
	// 替换为实际的 WebSocket 服务器地址
	url := ggg.wstype + "://" + *ggg.addr + ggg.path
	fmt.Println(url)
	var err error
	// 创建自定义请求头，包含 Authorization 信息
	header := make(map[string][]string)
	header["Authorization"] = []string{"user=" + ggg.user + "&passwd=" + ggg.passwd + "&room=" + ggg.room}
	ggg.conn, _, err = websocket.DefaultDialer.Dial(url, header)
	if err != nil {
		fmt.Println(err)
		ggg.isAlive = false
		//os.Exit(1)
		return
	}
	ggg.isAlive = true
	fmt.Println("链接成功！！！")
	ggg.StartDate = time.Now().Format("2006-01-02 15:04")
	ggg.conn.WriteMessage(websocket.TextMessage, []byte("上线时间"+ggg.StartDate))
	ggg.conn.PongHandler()
	ggg.conn.SetPingHandler(func(appData string) error {
		fmt.Println("单片机返回ping 消息是" + appData)
		//设置pong超时时间 因为这个只能生效一次,所以每次接受到pong都设置一次
		ggg.conn.SetReadDeadline(time.Now().Add(time.Second * 50)) //20秒ping一次50秒2次没收到返回pong认定超时
		return nil
	})
}

// 链接服务端
func (ggg *WebsocketClient) dail() {
	var err error
	u := url.URL{Scheme: ggg.wstype, Host: *ggg.addr, Path: ggg.path, ForceQuery: true, RawQuery: "user=" + ggg.user + "&passwd=" + ggg.passwd + "&room=" + ggg.room}
	fmt.Println("connecting to " + u.String())
	ggg.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		fmt.Println(err)
		ggg.isAlive = false
		//os.Exit(1)
		return
	}

	ggg.isAlive = true
	fmt.Println("链接成功！！！")
	ggg.StartDate = time.Now().Format("2006-01-02 15:04")
	ggg.conn.WriteMessage(websocket.TextMessage, []byte("上线时间"+ggg.StartDate))

}

// 发送消息
func (ggg *WebsocketClient) sendMsgThread() {
	go func() {
		for {
			msg := <-ggg.sendMsgChan
			err := ggg.conn.WriteMessage(websocket.TextMessage, []byte(msg))
			if err != nil {
				fmt.Println("write:", err)
				continue
			}
		}
	}()
}

// 读取消息
func (ggg *WebsocketClient) readMsgThread() {
	go func() {
		for {
			if ggg.conn != nil {
				msgType, msg, err := ggg.conn.ReadMessage()
				if err != nil {
					fmt.Println("read:超时", err)
					ggg.isAlive = false
					return
				}
				if msgType == 9 {
					//响应服务器主动发来的pong 表示我还在线
					//ggg.conn.WriteMessage(websocket.PongMessage, []byte{})
					ggg.conn.WriteControl(websocket.PongMessage, []byte{}, time.Now().Add(time.Second))
				}
				var msgs map[string]string
				json.Unmarshal(msg, &msgs)
				fmt.Println(msgs["message"])
				if len(msgs["message"]) != 0 {
					if strings.Contains(msgs["message"], ggg.user+".") {
						if msgs["message"] == ggg.user+".shutdown" {
							ggg.conn.WriteMessage(websocket.TextMessage, []byte("执行关机中..."))
							//主动向服务端发送断开连接消息
							ggg.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(4001, "close"), time.Now().Add(time.Second))
							time.Sleep(time.Millisecond * 200)
							ShutDown()
							return
						}
						x := WindowsAlert{}
						a := []rune(msgs["message"])
						x.ShowMessage("弹窗提示", string(a))
					}
				}
			}
		}
	}()
	go func() {
		for {
			s := <-ggg.Ch
			switch s {
			case syscall.SIGINT:
				ggg.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(4001, "close"), time.Now().Add(time.Millisecond*100))
				//fmt.Println("退出前执行")
			case syscall.SIGILL:
				//fmt.Println("null")
			default:
				//fmt.Println("default")
			}
		}
	}()
}

func ShutDown() {
	arg := []string{"-s", "-t", "0"}
	cmd := exec.Command("shutdown", arg...)
	cmd.CombinedOutput()
}

type WindowsAlert struct {
}

func (ggg *WindowsAlert) StrPtr(s string) uintptr {
	utf16Ptr, _ := syscall.UTF16PtrFromString(s)
	return uintptr(unsafe.Pointer(utf16Ptr))
}
func (ggg *WindowsAlert) ShowMessage(title, text string) {
	user32dll, _ := syscall.LoadLibrary("user32.dll")
	user32 := syscall.NewLazyDLL("user32.dll")
	MessageBoxW := user32.NewProc("MessageBoxW")
	defer syscall.FreeLibrary(user32dll)
	MessageBoxW.Call(uintptr(0), ggg.StrPtr(text), ggg.StrPtr(title), uintptr(0))
	time.Sleep(time.Second * 3)
}
