package main

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"

	"golang.org/x/text/encoding/simplifiedchinese"
)

func execcmd_base(cmdargs string) (string, []string, error) {
	if cmdargs == "" {
		return "", nil, fmt.Errorf("NULL COMMAND")
	}

	var app string
	var appargs []string

	spaceindex := strings.Index(cmdargs, " ")
	if spaceindex != -1 {
		app = cmdargs[:spaceindex]
		appargs = strings.Split(cmdargs[spaceindex+1:], " ")
	} else {
		app = cmdargs
		appargs[0] = ""
	}

	return app, appargs, nil
}
func Execcmd_nowait(cmdargs string) {
	app, appargs, err := execcmd_base(cmdargs)
	if err != nil {
		fmt.Print(err)
		return
	}
	if err := exec.Command(app, appargs...).Start(); err != nil {
		fmt.Print(err)
		return
	}
}
func Execcmd_wait(cmdargs string) {
	app, appargs, err := execcmd_base(cmdargs)
	if err != nil {
		return
	}
	cmd := exec.Command(app, appargs...)
	if err := cmd.Start(); err != nil {
	}
	err = cmd.Wait()
	if err != nil {
	}
}
func PSCommandOutput(cmd string) (string, error) {
	if cmd == "" {
		return "", fmt.Errorf("Lost Paramter")
	}
	cmd = "powershell -command -WindowStyle Hidden " + cmd
	app, appargs, err := execcmd_base(cmd)
	if err != nil {
		return "", err
	}
	r, err := exec.Command(app, appargs...).Output()
	if err != nil {
		return "", err
	}
	out, err := simplifiedchinese.GBK.NewDecoder().Bytes(r)
	if err != nil {
		return "", err
	}
	return string(out), nil

}
func PSCommandOutputNoSplit(cmd string) (string, error) {
	if cmd == "" {
		return "", fmt.Errorf("Lost Paramter")
	}
	r, err := exec.Command("powershell", "-command", cmd).Output()
	if err != nil {
		return "", err
	}
	return string(r), nil

}
func getHardwareCPUForPS() (string, string, string, string) {
	var c, j int
	var cpuCount, cpuName, cpuCores, cpuLogicalProcessors string
	cpuUN, _ := PSCommandOutputNoSplit("Get-WmiObject -Class Win32_Processor | select Name,NumberOfCores,NumberOfLogicalProcessors | format-list")
	for _, line := range strings.Split(cpuUN, "\n") {
		if strings.Index(line, "Name") != -1 {
			mi := strings.TrimSpace(strings.Split(line, ":")[1])
			cpuName = mi
			c++
		} else if strings.Index(line, "NumberOfCores") != -1 {
			mi := strings.TrimSpace(strings.Split(line, ":")[1])
			cpuCores = mi
			c++
		} else if strings.Index(line, "NumberOfLogicalProcessors") != -1 {
			mi := strings.TrimSpace(strings.Split(line, ":")[1])
			cpuLogicalProcessors = mi
			c++
		}
		if c == 3 {
			c = 0
			j++
		}

		cpuCount = strconv.Itoa(j)
	}
	return cpuCount, cpuName, cpuCores, cpuLogicalProcessors
}
func getHardwareMemoryForPS() ([]int, []string, []string, int) {
	Capacity := make([]int, 2)
	Speed := make([]string, 2)
	MemoryType := make([]string, 2)
	var c, j int

	menUN, _ := PSCommandOutputNoSplit("Get-WmiObject -Class Win32_PhysicalMemory -Property Capacity,Speed,MemoryType")

	for _, line := range strings.Split(menUN, "\n") {
		if strings.Contains(line, "Capacity") {
			mi := strings.TrimSpace(strings.Split(line, ":")[1])
			m, _ := strconv.ParseInt(mi, 10, 64)

			if j >= 2 {
				Capacity = append(Capacity, int(m/1024/1024/1024))
			} else {
				Capacity[j] = int(m / 1024 / 1024 / 1024)
			}

			c++
		} else if strings.Contains(line, "Speed") {
			m := strings.TrimSpace(strings.Split(line, ":")[1])
			if j >= 2 {
				Speed = append(Speed, m)
			} else {
				Speed[j] = m
			}
			c++
		} else if strings.Contains(line, "MemoryType") {
			m := strings.TrimSpace(strings.Split(line, ":")[1])

			switch m {
			// 其实吧，是有可能的
			case "0":
				m = "0"
			case "21":
				m = "DDR2"
			case "22":
				m = "DDR2 FB-DIMM"
			case "24":
				m = "DDR3"
			case "26":
				m = "DDR4"
			default:
				m = ""
			}
			if j >= 2 {
				MemoryType = append(MemoryType, m)
			} else {
				MemoryType[j] = m
			}
		}
		if c == 2 {
			j++
			c = 0
		}

	}
	for i, line := range MemoryType {
		if line == "" || line == "0" {
			if speed, err := strconv.Atoi(Speed[i]); err == nil && speed >= 2133 {
				MemoryType[i] = "DDR4"
			}
		}
	}
	return Capacity, Speed, MemoryType, j - 1
}
func getHardwareHardDiskForPS(mini bool) ([]string, []int64, []string, int) {
	MediaType := make([]string, 2)
	FriendlyName := make([]string, 2)
	Sizen := make([]int64, 2)
	var c, j int
	diskUN, _ := PSCommandOutputNoSplit("Get-PhysicalDisk |select mediaType,FriendlyName,Size | format-list")
	for _, line := range strings.Split(diskUN, "\n") {
		if strings.Index(line, "MediaType") != -1 {
			m := strings.TrimSpace(strings.Split(line, ":")[1])

			// 如果是小白
			if mini {
				switch m {
				case "SSD":
					m = "固态硬盘"
				case "HDD":
					m = "机械硬盘"
				// 跳过U盘
				case "Unspecified":
					// 2+1后开始重置
					c = 2
				}
			}

			if j >= 2 {
				MediaType = append(MediaType, m)
			} else {
				MediaType[j] = m
			}
			c++

		} else if strings.Index(line, "FriendlyName") != -1 {
			m := strings.TrimSpace(strings.Split(line, ":")[1])

			if j >= 2 {
				FriendlyName = append(FriendlyName, m)
			} else {
				FriendlyName[j] = m
			}

			// 跳过虚拟硬盘
			if FriendlyName[j] == "Msft Virtual Disk" {
				c = 2
			}
			c++
		} else if strings.Index(line, "Size") != -1 {
			Size := strings.TrimSpace(strings.Split(line, ":")[1])
			Sizei, _ := strconv.ParseInt(Size, 10, 64)
			m := Sizei / 1024 / 1024 / 1024

			if j >= 2 {
				Sizen = append(Sizen, m)
			} else {
				Sizen[j] = m
			}
			c++

		}
		if c == 3 {
			j++
			c = 0
		}

	}

	return FriendlyName, Sizen, MediaType, j - 1
}

func main() {
	var ch = make(chan int)
	var Memory, Disk string

	fmt.Println("查看硬件(CPU、内存、硬盘)小工具     -- made in 陶腾飞(2022年3月27日)")
	// cpu
	go func() {
		<-ch

		cpuCount, cpuName, cpuCores, cpuLogicalProcessors := getHardwareCPUForPS()
		cpu := fmt.Sprintf("%s * %s %s核 %s线程", cpuCount, cpuName, cpuCores, cpuLogicalProcessors)
		fmt.Printf("CPU:%s\n", cpu)

	}()
	ch <- 1

	// 内存
	go func() {

		<-ch
		Capacity, Speed, MemoryType, mCount := getHardwareMemoryForPS()
		for i := 0; i <= mCount; i++ {
			Memory += fmt.Sprintf("%dGB %sMhz %s,", Capacity[i], Speed[i], MemoryType[i])
		}
		fmt.Printf("Memory:%s\n", Memory[0:len(Memory)-1])

	}()
	ch <- 1

	// 硬盘
	go func() {
		<-ch

		_, Sizen, MediaType, hCount := getHardwareHardDiskForPS(false)
		for i := 0; i <= hCount; i++ {
			Disk += fmt.Sprintf("%dGB %s,", Sizen[i], MediaType[i])
		}

		Disk = Disk[0 : len(Disk)-1]
		fmt.Printf("Disk:%s\n", Disk)

	}()
	ch <- 1
	fmt.Scanln()
	fmt.Scanln()
}
