package public

import (
    "bytes"
    "encoding/hex"
    "errors"
    "fmt"
    "net"
    "strings"
)


// 源码地址
// https://github.com/zzustu/wol
// 网络唤醒魔包技术白皮书地址
// https://www.amd.com/system/files/TechDocs/20213.pdf

func Wol(mac string) error{
    //nic := ""


    hw := strings.Replace(strings.Replace(mac, ":", "", -1), "-", "", -1)
    if len(hw) != 12 {
		return fmt.Errorf("MAC: [%s] 输入不正确.\n", mac)
    }

    macHex, err := hex.DecodeString(hw)
    if err != nil {
        return fmt.Errorf("MAC: [%s] 输入不正确.\n", mac)
    }

    // 广播MAC地址 FF:FF:FF:FF:FF:FF
    var bcast = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
    var buff bytes.Buffer
    buff.Write(bcast)
    for i := 0; i < 16; i++ {
        buff.Write(macHex)
    }

    // 获得唤醒魔包
    mp := buff.Bytes()
    if len(mp) != 102 {
        return  fmt.Errorf("MAC: [%s] 输入不正确.\n", mac)
    }

    return sendMagicPacket(mp, "")
}

// 向指定网卡发送唤醒魔包
func sendMagicPacket(mp []byte, nic string)error{
    sender := net.UDPAddr{}
    if len(nic) != 0 {
        ip, err := interfaceIPv4ByName(nic)
        if err != nil {
            return fmt.Errorf("网卡[%s]错误: %s", nic, err)
        }

        sender.IP = ip
    }

    target := net.UDPAddr{
        IP: net.IPv4bcast,
    }
    conn, err := net.DialUDP("udp", &sender, &target)
    if err != nil {
        return fmt.Errorf("创建UDP错误：%v", err)
    }
    defer func() {
        _ = conn.Close()
    }()

    _, err = conn.Write(mp)
    if err != nil {
    	return fmt.Errorf("唤醒失败：[%s]", err)
    } else {
		return nil
    }
}

// 通过网卡名称获取该网卡绑定的IPv4
func interfaceIPv4ByName(nic string) (net.IP, error) {
    inter, err := net.InterfaceByName(nic)
    if err != nil {
        return nil, err
    }

    // 检查网卡是否正在工作
    if (inter.Flags & net.FlagUp) == 0 {
        return nil, errors.New("网卡未工作")
    }

    addrs, err := inter.Addrs()
    if err != nil {
        return nil, err
    }

    for _, addr := range addrs {
        if ip, ok := addr.(*net.IPNet); ok {
            if ipv4 := ip.IP.To4(); ipv4 != nil {
                return ipv4, nil
            }
        }
    }

    return nil, errors.New("找不到网卡绑定的IP")
}