// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fsdriver

import (
	"fmt"
	"math/rand"
	"path"
	"regexp"
	"strings"
	"syscall"

	"yunion.io/x/log"
	"yunion.io/x/pkg/errors"
	"yunion.io/x/pkg/util/version"
	"yunion.io/x/pkg/utils"

	"yunion.io/x/onecloud/pkg/apis"
	api "yunion.io/x/onecloud/pkg/apis/compute"
	"yunion.io/x/onecloud/pkg/cloudcommon/types"
	deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
	"yunion.io/x/onecloud/pkg/util/fileutils2"
	"yunion.io/x/onecloud/pkg/util/netutils2"
	"yunion.io/x/onecloud/pkg/util/procutils"
	"yunion.io/x/onecloud/pkg/util/seclib2"
	"yunion.io/x/onecloud/pkg/util/stringutils2"
	"yunion.io/x/onecloud/pkg/util/winutils"
)

const (
	ACTIVE_COMPUTER_NAME_KEY = `HKLM\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName`
	COMPUTER_NAME_KEY        = `HKLM\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName`

	TCPIP_PARAM_KEY      = `HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters`
	BOOT_SCRIPT_PATH     = "/Windows/System32/GroupPolicy/Machine/Scripts/Startup/cloudboot.bat"
	WIN_BOOT_SCRIPT_PATH = "cloudboot"

	WIN_TELEGRAF_BINARY_PATH = "/opt/yunion/bin/telegraf.exe"
	WIN_TELEGRAF_PATH        = "/Program Files/Telegraf"

	WIN_QGA_PATH = "/Program Files/Qemu-ga"
)

type SWindowsRootFs struct {
	*sGuestRootFsDriver

	guestDebugLogPath string

	bootScript  string
	bootScripts map[string]string
}

func NewWindowsRootFs(part IDiskPartition) IRootFsDriver {
	seq := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
		'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
	suffix := make([]byte, 16)
	lenSeq := len(seq)
	for i := 0; i < 16; i++ {
		suffix[i] = seq[rand.Intn(lenSeq)]
	}
	return &SWindowsRootFs{
		sGuestRootFsDriver: newGuestRootFsDriver(part),
		guestDebugLogPath:  `%SystemRoot%\mdbg_` + string(suffix),
		bootScripts:        make(map[string]string),
	}
}

func (w *SWindowsRootFs) IsFsCaseInsensitive() bool {
	return true
}

func (w *SWindowsRootFs) GetName() string {
	return "Windows"
}

func (w *SWindowsRootFs) String() string {
	return "WindowsRootFs"
}

func (w *SWindowsRootFs) DeployPublicKey(IDiskPartition, string, *deployapi.SSHKeys) error {
	return nil
}

func (w *SWindowsRootFs) RootSignatures() []string {
	return []string{
		"/program files", "/windows",
		"/windows/system32/drivers/etc", "/windows/system32/config",
		"/windows/system32/config/sam",
		"/windows/system32/config/software",
		"/windows/system32/config/system",
	}
}

func (w *SWindowsRootFs) GetReleaseInfo(IDiskPartition) *deployapi.ReleaseInfo {
	confPath := w.rootFs.GetLocalPath("/windows/system32/config", true)
	tool := winutils.NewWinRegTool(confPath)
	if tool.CheckPath() {
		distro := w.GetName()
		version := tool.GetProductName()
		curVersion := tool.GetVersion()
		arch := w.GetArch(hostCpuArch)
		lan := tool.GetInstallLanguage()
		return &deployapi.ReleaseInfo{
			Distro:         distro,
			Version:        version,
			Arch:           arch,
			Language:       lan,
			CurrentVersion: curVersion,
		}
	} else {
		return nil
	}
}

func (w *SWindowsRootFs) GetLoginAccount(rootFs IDiskPartition, sUser string, defaultRootUser bool, windowsDefaultAdminUser bool) (string, error) {
	confPath := w.rootFs.GetLocalPath("/windows/system32/config", true)
	tool := winutils.NewWinRegTool(confPath)
	tool.CheckPath()
	users := tool.GetUsers()
	admin := "Administrator"
	selUsr := ""
	isWin10NonPro := w.IsWindows10NonPro()
	// Win10 try not to use Administrator users // Win 10 professional can use Adminsitrator
	if _, ok := users[admin]; ok && windowsDefaultAdminUser && !isWin10NonPro && w.GetIRootFsDriver().AllowAdminLogin() {
		selUsr = admin
	} else {
		// Looking for an unlocked user who is not an Administrator
		for user, ok := range users {
			if !ok {
				continue
			}
			if user != admin {
				selUsr = user
			}
		}
		if _, ok := users[admin]; ok && len(selUsr) == 0 {
			selUsr = admin
		}
	}
	if selUsr == "" {
		return "", fmt.Errorf("no unlocked user")
	}
	if !users[selUsr] {
		// user is locked
		tool.UnlockUser(selUsr)
	}
	return selUsr, nil
}

func (w *SWindowsRootFs) GetArch(hostCpuArch string) string {
	if w.rootFs.Exists("/program files (x86)", true) {
		return apis.OS_ARCH_X86_64
	} else if w.rootFs.Exists("/program files (arm)", true) {
		return apis.OS_ARCH_AARCH64
	}
	if hostCpuArch == apis.OS_ARCH_AARCH32 {
		return apis.OS_ARCH_AARCH32
	} else {
		return apis.OS_ARCH_X86_32
	}
}

func (w *SWindowsRootFs) IsWindows10NonPro() bool {
	info := w.GetReleaseInfo(nil)
	if info != nil && strings.HasPrefix(info.Version, "Windows 10 ") && !strings.HasPrefix(info.Version, "Windows 10 Pro") {
		return true
	}
	return false
}

func (w *SWindowsRootFs) IsOldWindows() bool {
	info := w.GetReleaseInfo(nil)
	if info != nil && strings.HasPrefix(info.CurrentVersion, "5.") {
		return true
	}
	return false
}

func (w *SWindowsRootFs) GetOs() string {
	return "Windows"
}

func (w *SWindowsRootFs) appendGuestBootScript(name, content string) {
	w.bootScript += "\r\n" + fmt.Sprintf("start %s", name)
	w.bootScripts[name] = content
}

func (w *SWindowsRootFs) regAdd(path, key, val, regType string) string {
	return fmt.Sprintf(`REG ADD %s /V "%s" /D "%s" /T %s /F`, path, key, val, regType)
}

func (w *SWindowsRootFs) putGuestScriptContents(spath, content string) error {
	contentArr := []string{}
	contentLen := len(content)

	var j = 0
	if content[0] == '\n' {
		contentArr = append(contentArr, "")
		j += 1
	}

	for i := 1; i < contentLen; i++ {
		if content[i] == '\n' && content[i-1] != '\r' {
			contentArr = append(contentArr, content[j:i])
			j = i + 1
		}
	}
	if j < contentLen {
		contentArr = append(contentArr, content[j:])
	} else {
		contentArr = append(contentArr, "")
	}

	content = strings.Join(contentArr, "\r\n")
	return w.rootFs.FilePutContents(spath, content, false, true)
}

func (w *SWindowsRootFs) DeployHostname(part IDiskPartition, hostname, domain string) error {
	bootScript := strings.Join([]string{
		`set HOSTNAME_SCRIPT=%SystemRoot%\hostnamecfg.bat`,
		`if exist %HOSTNAME_SCRIPT% (`,
		`    call %HOSTNAME_SCRIPT%`,
		`    del %HOSTNAME_SCRIPT%`,
		`)`,
	}, "\r\n")
	w.appendGuestBootScript("hostnamecfg", bootScript)

	lines := []string{}
	for k, v := range map[string]string{
		"Hostname":    hostname,
		"Domain":      domain,
		"NV Hostname": hostname,
		"NV Domain":   domain,
	} {
		lines = append(lines, w.regAdd(TCPIP_PARAM_KEY, k, v, "REG_SZ"))
	}
	// windows allow a maximal length of 15
	// http://support.microsoft.com/kb/909264
	if len(hostname) > api.MAX_WINDOWS_COMPUTER_NAME_LENGTH {
		hostname = hostname[:api.MAX_WINDOWS_COMPUTER_NAME_LENGTH]
	}
	lines = append(lines, w.regAdd(ACTIVE_COMPUTER_NAME_KEY, "ComputerName", hostname, "REG_SZ"))
	lines = append(lines, w.regAdd(COMPUTER_NAME_KEY, "ComputerName", hostname, "REG_SZ"))
	hostScripts := strings.Join(lines, "\r\n")
	return w.putGuestScriptContents("/windows/hostnamecfg.bat", hostScripts)
}

func (w *SWindowsRootFs) DeployHosts(part IDiskPartition, hn, domain string, ips []string) error {
	var (
		ETC_HOSTS = "/windows/system32/drivers/etc/hosts"
		oldHf     = ""
	)

	if w.rootFs.Exists(ETC_HOSTS, true) {
		oldHfBytes, err := w.rootFs.FileGetContents(ETC_HOSTS, true)
		if err != nil {
			log.Errorln(err)
			return err
		}
		oldHf = string(oldHfBytes)
	}

	return w.rootFs.FilePutContents(ETC_HOSTS, fileutils2.FormatHostsFile(oldHf, ips, hn, getHostname(hn, domain)), false, true)
}

func (w *SWindowsRootFs) DeployNetworkingScripts(rootfs IDiskPartition, nics []*types.SServerNic) error {
	mainNic, err := netutils2.GetMainNicFromDeployApi(nics)
	if err != nil {
		return err
	}
	mainIp := ""
	if mainNic != nil {
		mainIp = mainNic.Ip
	}
	mainIp6 := ""
	if mainNic != nil {
		mainIp6 = mainNic.Ip6
	}
	bootScript := strings.Join([]string{
		`set NETCFG_SCRIPT=%SystemRoot%\netcfg.bat`,
		`if exist %NETCFG_SCRIPT% (`,
		`    call %NETCFG_SCRIPT%`,
		`    del %NETCFG_SCRIPT%`,
		`)`,
	}, "\r\n")
	w.appendGuestBootScript("netcfg", bootScript)
	lines := []string{
		"@echo off",
		w.MakeGuestDebugCmd("netcfg step 1"),
		"setlocal enableDelayedExpansion",
		`for /f "delims=" %%a in ('getmac /fo csv /nh /v') do (`,
		`  set line=%%a&set line=!line:"=,!`,
		`  for /f "delims=,,, tokens=1,3" %%b in ("!line!") do (`,
	}

	hasV6 := false
	for _, snic := range nics {
		if len(snic.Ip6) > 0 {
			hasV6 = true
			break
		}
	}
	if hasV6 {
		lines = append(lines, `    netsh interface teredo set state disable`)
		lines = append(lines, `    netsh interface 6to4 set state state=disabled`)
		lines = append(lines, `    netsh interface isatap set state state=disabled`)
		lines = append(lines, `    netsh interface ipv6 set privacy disabled store=persistent`)
		lines = append(lines, `    netsh interface ipv6 set global randomizeidentifiers=disabled store=persistent`)
	}

	for _, snic := range nics {
		mac := snic.Mac
		mac = strings.Replace(strings.ToUpper(mac), ":", "-", -1)
		lines = append(lines, fmt.Sprintf(`    if "%%%%c" == "%s" (`, mac))
		if snic.Mtu > 0 {
			lines = append(lines, fmt.Sprintf(`      netsh interface ipv4 set subinterface "%%%%b" mtu=%d`, snic.Mtu))
		}
		if snic.Manual {
			if len(snic.Ip) > 0 {
				netmask := netutils2.Netlen2Mask(int(snic.Masklen))
				cfg := fmt.Sprintf(`      netsh interface ip set address "%%%%b" static %s %s`, snic.Ip, netmask)
				if len(snic.Gateway) > 0 && snic.Ip == mainIp {
					cfg += fmt.Sprintf(" %s", snic.Gateway)
				}
				lines = append(lines, cfg)
			}
			if len(snic.Ip6) > 0 {
				cfg := fmt.Sprintf(`      netsh interface ipv6 add address "%%%%b" %s/%d store=persistent`, snic.Ip6, snic.Masklen6)
				lines = append(lines, cfg)
				if len(snic.Gateway6) > 0 && snic.Ip6 == mainIp6 {
					cfg := fmt.Sprintf(`      netsh interface ipv6 add route ::/0 "%%%%b" %s`, snic.Gateway6)
					lines = append(lines, cfg)
				}
			}
			routes4 := []netutils2.SRouteInfo{}
			routes6 := []netutils2.SRouteInfo{}
			routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, snic, mainIp, mainIp6, len(nics))
			for _, r := range routes4 {
				lines = append(lines, fmt.Sprintf(`      netsh interface ip add route %s/%d %s "%%%%b"`, r.Prefix, r.PrefixLen, r.Gateway.String()))
			}
			for _, r := range routes6 {
				lines = append(lines, fmt.Sprintf(`      netsh interface ipv6 add route %s/%d %s "%%%%b"`, r.Prefix, r.PrefixLen, r.Gateway.String()))
			}
			dns4list, dns6list := netutils2.GetNicDns(snic)
			if len(dns4list) > 0 {
				lines = append(lines, fmt.Sprintf(
					`      netsh interface ip set dns name="%%%%b" source=static addr=%s`, dns4list[0]))
				if len(dns4list) > 1 {
					for i := 1; i < len(dns4list); i++ {
						lines = append(lines, fmt.Sprintf(`      netsh interface ip add dns "%%%%b" %s index=%d`, dns4list[i], i+1))
					}
				}
			}
			if len(dns6list) > 0 {
				lines = append(lines, fmt.Sprintf(
					`      netsh interface ipv6 set dns name="%%%%b" source=static addr=%s`, dns6list[0]))
				if len(dns6list) > 1 {
					for i := 1; i < len(dns6list); i++ {
						lines = append(lines, fmt.Sprintf(`      netsh interface ip add dns "%%%%b" %s index=%d`, dns6list[i], i+1))
					}
				}
			}

			if len(snic.Domain) > 0 && snic.Ip == mainIp {
				lines = append(lines, w.regAdd(TCPIP_PARAM_KEY, "SearchList", snic.Domain, "REG_SZ"))
			}
		} else {
			if len(snic.Ip) > 0 {
				lines = append(lines, `      netsh interface ip set address "%%b" dhcp`)
				lines = append(lines, `      netsh interface ip set dns "%%b" dhcp`)
			}
			if len(snic.Ip6) > 0 {
				cfg := `      netsh interface ipv6 set interface "%%b" routerdiscovery=enabled store=persistent`
				lines = append(lines, cfg)
				cfg = `      netsh interface ipv6 set interface "%%b" managedaddress=enabled otherstateful=enabled store=persistent`
				lines = append(lines, cfg)
			}
		}
		lines = append(lines, `    )`)
	}
	lines = append(lines, `  )`)
	lines = append(lines, `)`)
	lines = append(lines, w.MakeGuestDebugCmd("netcfg step 2"))
	// lines = append(lines, `netsh advfirewall firewall set rule group=\"remote desktop\" new enable=yes`)
	netScript := strings.Join(lines, "\r\n")
	return w.putGuestScriptContents("/windows/netcfg.bat", netScript)
}

func (w *SWindowsRootFs) MakeGuestDebugCmd(content string) string {
	mark := "============="
	content = regexp.MustCompile(`(["^&<>|])`).ReplaceAllString(content, "^$1")
	return fmt.Sprintf("echo %s %s %s >> %s", mark, content, mark, w.guestDebugLogPath)
}

func (w *SWindowsRootFs) prependGuestBootScript(content string) {
	w.bootScript = content + "\r\n" + w.bootScript
}

func (w *SWindowsRootFs) PrepareFsForTemplate(IDiskPartition) error {
	for _, f := range []string{"/Pagefile.sys", "/Hiberfil.sys", "/Swapfile.sys"} {
		if w.rootFs.Exists(f, true) {
			w.rootFs.Remove(f, true)
		}
	}
	return nil
}

func (w *SWindowsRootFs) CommitChanges(part IDiskPartition) error {
	confPath := part.GetLocalPath("/windows/system32/config", true)
	tool := winutils.NewWinRegTool(confPath)
	tool.CheckPath()
	tool.EnableRdp()
	tool.ResetUSBProfile()

	if w.IsOldWindows() {
		// windows prior to windows 2003 should not try to commit changes
		return nil
	}

	tool.InstallGpeditStartScript(WIN_BOOT_SCRIPT_PATH)

	bootDir := path.Dir(BOOT_SCRIPT_PATH)
	if err := w.rootFs.Mkdir(bootDir, syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR, true); err != nil {
		return err
	}
	if err := w.rootFs.FilePutContents(BOOT_SCRIPT_PATH, w.bootScript, false, false); err != nil {
		return errors.Wrap(err, "write boot script")
	}
	for k, v := range w.bootScripts {
		if err := w.rootFs.FilePutContents(path.Join(bootDir, fmt.Sprintf("%s.bat", k)), v, false, false); err != nil {
			return errors.Wrap(err, "write boot scripts")
		}
	}
	return nil
}

func (w *SWindowsRootFs) ChangeUserPasswd(part IDiskPartition, account, gid, publicKey, password string, isRandomPassword bool) (string, error) {
	rinfo := w.GetReleaseInfo(part)
	confPath := part.GetLocalPath("/windows/system32/config", true)
	tool := winutils.NewWinRegTool(confPath)
	tool.CheckPath()
	success := false

	// symbol ^ is escape character is batch file.
	password = strings.ReplaceAll(password, "^", "")
	if rinfo != nil && version.GE(rinfo.CurrentVersion, "6.1") {
		success = w.deployPublicKeyByGuest(account, password)
	} else {
		success = tool.ChangePassword(account, password) == nil
	}

	var (
		secret string
		err    error
	)
	if success {
		if len(publicKey) > 0 {
			secret, err = seclib2.EncryptBase64(publicKey, password)
			if err != nil {
				return "", err
			}
		} else {
			secret, err = utils.EncryptAESBase64(gid, password)
			if err != nil {
				return "", err
			}
		}
		if rinfo != nil && strings.Contains(rinfo.Version, "Windows XP") {
			if len(tool.GetLogontype()) > 0 {
				tool.SetLogontype("0x0")
			}
		}
	} else {
		log.Errorf("Failed Password %s", account)
	}
	defUanme := tool.GetDefaultAccount()
	if len(defUanme) > 0 && defUanme != account {
		tool.SetDefaultAccount(account)
	}
	return secret, nil
}

func (w *SWindowsRootFs) deployPublicKeyByGuest(uname, passwd string) bool {
	if !w.deploySetupCompleteScripts(uname, passwd) {
		return false
	}
	bootScript := strings.Join([]string{
		`set CHANGE_PASSWD_SCRIPT=%SystemRoot%\chgpwd.bat`,
		`if exist %CHANGE_PASSWD_SCRIPT% (`,
		`    call %CHANGE_PASSWD_SCRIPT%`,
		`    del %CHANGE_PASSWD_SCRIPT%`,
		`)`,
	}, "\r\n")
	w.appendGuestBootScript("chgpwd", bootScript)
	logPath := w.guestDebugLogPath
	chksum := stringutils2.GetMD5Hash(passwd + logPath[(len(logPath)-10):])

	chgpwdScript := strings.Join([]string{
		w.MakeGuestDebugCmd("change password step 1"),
		strings.Join([]string{
			`%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`,
			` -executionpolicy bypass %SystemRoot%\chgpwd.ps1`,
			fmt.Sprintf(" %s %s %s %s", uname, passwd, chksum, logPath),
		}, ""),
		`del %SystemRoot%\chgpwd.ps1`,
		w.MakeGuestDebugCmd("change password step 2"),
	}, "\r\n")
	if w.putGuestScriptContents("/windows/chgpwd.bat", chgpwdScript) != nil {
		return false
	}
	if w.putGuestScriptContents("/windows/chgpwd.ps1", WinScriptChangePassword) != nil {
		return false
	}
	return true
}

func (w *SWindowsRootFs) deploySetupCompleteScripts(uname, passwd string) bool {
	SETUP_SCRIPT_PATH := "/Windows/Setup/Scripts/SetupComplete.cmd"
	if !w.rootFs.Exists(path.Dir(SETUP_SCRIPT_PATH), true) {
		w.rootFs.Mkdir(path.Dir(SETUP_SCRIPT_PATH),
			syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR, true)
	}
	if w.putGuestScriptContents("/windows/chgpwd_setup.ps1", WinScriptChangePassword) != nil {
		return false
	}
	cmds := []string{
		`%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass %SystemRoot%\chgpwd_setup.ps1 ` +
			fmt.Sprintf("%s %s", uname, passwd),
		"Net stop wuauserv",
	}
	for _, v := range [][3]string{
		{"AUOptions", "REG_DWORD", "3"},
		{"NoAutoUpdate", "REG_DWORD", "0"},
		{"ScheduledInstallDay", "REG_DWORD", "0"},
		{"ScheduledInstallTime", "REG_DWORD", "4"},
		{"AutoInstallMinorUpdates", "REG_DWORD", "1"},
		{"NoAutoRebootWithLoggedOnUsers", "REG_DWORD", "1"},
		{"IncludeRecommendedUpdates", "REG_DWORD", "0"},
		{"EnableFeaturedSoftware", "REG_DWORD", "1"},
	} {
		cmds = append(cmds, fmt.Sprintf(`REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v %s /t %s /d %s /f`,
			v[0], v[1], v[2]))
	}
	cmds = append(cmds, `REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" /v RealTimeIsUniversal /t REG_DWORD /d 1 /f`)
	cmds = append(cmds, "Net start wuauserv")
	cmds = append(cmds, "wuauclt /detectnow")
	cmds = append(cmds, `del %SystemRoot%\chgpwd_setup.ps1`)
	cmds = append(cmds, `del %SystemRoot%\Setup\Scripts\SetupComplete.cmd`)
	if w.putGuestScriptContents(SETUP_SCRIPT_PATH, strings.Join(cmds, "\r\n")) != nil {
		return false
	}
	return true
}

func (w *SWindowsRootFs) DeployFstabScripts(rootFs IDiskPartition, disks []*deployapi.Disk) error {
	bootScript := strings.Join([]string{
		`set MOUNT_DISK_SCRIPT=%SystemRoot%\mountdisk.bat`,
		`if exist %MOUNT_DISK_SCRIPT% (`,
		`    call %MOUNT_DISK_SCRIPT%`,
		`    del %MOUNT_DISK_SCRIPT%`,
		`)`,
	}, "\r\n")
	w.appendGuestBootScript("mountdisk", bootScript)
	logPath := w.guestDebugLogPath
	mountScript := strings.Join([]string{
		w.MakeGuestDebugCmd("mount disk step 1"),
		`cscript %SystemRoot%\mountdisk.js --debug ` + logPath,
		`del %SystemRoot%\mountdisk.js`,
		w.MakeGuestDebugCmd("mount disk step 2"),
	}, "\r\n")

	if w.putGuestScriptContents("/windows/mountdisk.bat", mountScript) != nil {
		return nil
	}
	w.putGuestScriptContents("/windows/mountdisk.js", WinScriptMountDisk)
	return nil
}

func (w *SWindowsRootFs) DetectIsUEFISupport(part IDiskPartition) bool {
	content, err := w.rootFs.FileGetContents("/windows/panther/setupact.log", true)
	if err != nil {
		log.Errorln(err)
		return false
	}
	contentStr := string(content)
	sep := "Detected boot environment: "
	idx := strings.Index(contentStr, sep)
	if idx < 0 {
		return false
	}
	if strings.HasPrefix(contentStr[idx+len(sep):], "EFI") ||
		strings.HasPrefix(contentStr[idx+len(sep):], "UEFI") {
		return true
	}
	return false
}

func (l *SWindowsRootFs) IsResizeFsPartitionSupport() bool {
	return true
}

func (w *SWindowsRootFs) DeployQgaService(part IDiskPartition) error {
	if err := w.rootFs.Mkdir(WIN_QGA_PATH, syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR, true); err != nil {
		return errors.Wrap(err, "mkdir qemu-ga path")
	}

	qgaInstallerPath := path.Join(w.rootFs.GetMountPath(), WIN_QGA_PATH, "qemu-ga-x86_64.msi")
	output, err := procutils.NewCommand("cp", "-f", QGA_WIN_MSI_INSTALLER_PATH, qgaInstallerPath).Output()
	if err != nil {
		return errors.Wrapf(err, "cp qga installer failed %s", output)
	}

	bootScript := strings.Join([]string{
		`start "" "%PROGRAMFILES%\Qemu-ga\qemu-ga-x86_64.msi"`,
	}, "\r\n")
	w.appendGuestBootScript("qemu-ga", bootScript)
	return nil
}

func (w *SWindowsRootFs) DeployQgaBlackList(part IDiskPartition) error {
	return nil
}

func (w *SWindowsRootFs) DeployTelegraf(config string) (bool, error) {
	if err := w.rootFs.Mkdir(WIN_TELEGRAF_PATH, syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR, true); err != nil {
		return false, errors.Wrap(err, "mkdir telegraf path")
	}

	telegrafConfPath := path.Join(w.rootFs.GetMountPath(), WIN_TELEGRAF_PATH, "telegraf.conf")
	if err := w.rootFs.FilePutContents(telegrafConfPath, config, false, true); err != nil {
		return false, errors.Wrap(err, "write boot script")
	}
	telegrafConfPath = strings.ReplaceAll(path.Join("%PROGRAMFILES%", "Telegraf", "telegraf.conf"), "/", "\\")

	telegrafBinaryPath := path.Join(w.rootFs.GetMountPath(), WIN_TELEGRAF_PATH, "telegraf.exe")
	output, err := procutils.NewCommand("cp", "-f", WIN_TELEGRAF_BINARY_PATH, telegrafBinaryPath).Output()
	if err != nil {
		return false, errors.Wrapf(err, "cp telegraf failed %s", output)
	}
	telegrafBinaryPath = strings.ReplaceAll(path.Join("%PROGRAMFILES%", "Telegraf", "telegraf.exe"), "/", "\\")
	bootScript := strings.Join([]string{
		`set SETUP_TELEGRAF_SCRIPT=%SystemRoot%\telegraf.bat`,
		`if exist %SETUP_TELEGRAF_SCRIPT% (`,
		`    call %SETUP_TELEGRAF_SCRIPT%`,
		`    del %SETUP_TELEGRAF_SCRIPT%`,
		`)`,
	}, "\r\n")
	w.appendGuestBootScript("telegraf", bootScript)

	setupTelegrafBatScript := strings.Join([]string{
		w.MakeGuestDebugCmd("setup telegraf step 1"),
		strings.Join([]string{
			`%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`,
			` -executionpolicy bypass %SystemRoot%\telegraf.ps1`,
			fmt.Sprintf(" '%s' '%s' >> %s_telegraf", telegrafBinaryPath, telegrafConfPath, w.guestDebugLogPath),
		}, ""),
		`del %SystemRoot%\telegraf.ps1`,
		w.MakeGuestDebugCmd("setup telegraf step 2"),
	}, "\r\n")
	if err := w.putGuestScriptContents("/windows/telegraf.bat", setupTelegrafBatScript); err != nil {
		return false, errors.Wrap(err, "put setup bat scirpt")
	}
	if err := w.putGuestScriptContents("/windows/telegraf.ps1", winTelegrafSetupPowerShellScript); err != nil {
		return false, errors.Wrap(err, "put setup ps1 script")
	}
	return true, nil
}

func (w *SWindowsRootFs) ConfigSshd(loginAccount, loginPassword string, sshPort int) error {
	return nil
}
