package ui

import (
	"fmt"
	"go_dreamfactory/cmd/v2/lib/common"
	os_storage "go_dreamfactory/cmd/v2/lib/storage"
	"go_dreamfactory/cmd/v2/service"
	"go_dreamfactory/cmd/v2/service/observer"
	"image/color"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"

	"github.com/pkg/errors"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cast"
)

type appTerm struct {
	appAdapter

	obs        observer.Observer
	sshService *service.SSHService

	jsonList    *common.ItemList            //json列表
	cProgress   *widget.ProgressBarInfinite //连接进度条进度条
	upProgress  *widget.ProgressBar         //上传进度条
	endProgress sync.WaitGroup

	downloadList *common.ItemList //download列表
}

func (app *appTerm) LazyInit(ptService service.PttService, obs observer.Observer) error {
	app.obs = obs
	app.sshService = &service.SSHService{}
	app.jsonList = common.NewItemList()

	//progress
	app.cProgress = widget.NewProgressBarInfinite()
	app.cProgress.Hide()

	app.upProgress = widget.NewProgressBar()
	app.upProgress.Hide()

	app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_TERM, theme.MailSendIcon(), nil)
	content := container.NewMax()
	content.Objects = []fyne.CanvasObject{}

	//load term conf
	storage, _ := os_storage.NewOSStorage()
	conf, err := storage.LoadConfig()
	if err != nil {
		return err
	}

	//left-top
	localDir := widget.NewEntry()
	localDir.PlaceHolder = `本地Json目录`

	remoteDir := widget.NewEntry()
	remoteDir.PlaceHolder = `远程目录`
	remoteDir.Text = "/home/liwei/go_dreamfactory/bin/json"

	//config
	ip := widget.NewEntry()
	ip.PlaceHolder = "10.0.0.9"
	port := widget.NewEntry()
	port.Text = "22" //default port
	port.PlaceHolder = "端口"
	userName := widget.NewEntry()
	userName.PlaceHolder = "账号"
	password := widget.NewPasswordEntry()
	password.PlaceHolder = "密码"

	passwordItem := &widget.FormItem{Text: "密码:", Widget: password}

	configForm := widget.NewForm(
		&widget.FormItem{Text: "IP:", Widget: container.NewBorder(nil, nil, nil, port, ip)},
		&widget.FormItem{Text: "用户名:", Widget: userName},
		passwordItem,
		widget.NewFormItem("Json目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
			openFolder(localDir, toolWin.w)
		}), localDir)),
		widget.NewFormItem("远程目录", remoteDir),
	)

	configForm.Items[4].HintText = "谨慎修改远程目录"

	//svn form
	lubanAddr := widget.NewEntry()
	lubanAddr.PlaceHolder = "Luban服务IP"

	workDir := widget.NewEntry()
	workDir.PlaceHolder = "工作目录"

	lubanCli := widget.NewEntry()
	lubanCli.PlaceHolder = "luban客户端可执行文件"

	//define
	define := widget.NewEntry()
	define.PlaceHolder = "定义文件"
	define.Text = `Defines\\__root__.xml`

	dataDir := widget.NewEntry()
	dataDir.PlaceHolder = "Data目录"

	jsonDir := widget.NewEntry()
	jsonDir.PlaceHolder = "Json目录"

	svnForm := widget.NewForm(
		&widget.FormItem{Text: "服务地址", Widget: lubanAddr},
		&widget.FormItem{Text: "工作目录", Widget: container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
			openFolder(workDir, toolWin.w)
		}), workDir)},
		&widget.FormItem{Text: "LubanCli", Widget: lubanCli},
		&widget.FormItem{Text: "Data目录", Widget: dataDir},
		&widget.FormItem{Text: "JSON目录", Widget: jsonDir},
	)

	//set input entry
	if conf.SyncConf != nil {
		ip.Text = conf.SyncConf.Ip
		port.Text = conf.SyncConf.Port
		userName.Text = conf.SyncConf.UserName
		password.Text = conf.SyncConf.Password
		localDir.Text = conf.SyncConf.LocalDir
		remoteDir.Text = conf.SyncConf.RemoteDir
		// //
		lubanAddr.Text = conf.SyncConf.ServerIp
		workDir.Text = conf.SyncConf.WorkDir
		lubanCli.Text = conf.SyncConf.LubanCli
		dataDir.Text = conf.SyncConf.DataDir
		jsonDir.Text = conf.SyncConf.JsonDir
	} else {
		conf.SyncConf = &os_storage.SyncConfig{
			Ip:        "10.0.0.9",
			UserName:  "root",
			Password:  "Legu.cc()123",
			RemoteDir: "/home/liwei/go_dreamfactory/bin/json/",

			ServerIp: "10.0.1.11",
			LubanCli: `Luban.Client\Luban.Client.exe`,
			DataDir:  "Datas",
			JsonDir:  `output\json`,
		}
	}

	// 解决文本没显示的问题
	passwordItem.Widget.Refresh()
	ip.Refresh()
	localDir.Refresh()
	workDir.Refresh()

	// save func
	saveFunc := func() {
		syncConf := &os_storage.SyncConfig{
			Ip:        ip.Text,
			UserName:  userName.Text,
			Password:  password.Text,
			Port:      port.Text,
			LocalDir:  localDir.Text,
			RemoteDir: remoteDir.Text,
			ServerIp:  lubanAddr.Text,
			WorkDir:   workDir.Text,
			LubanCli:  lubanCli.Text,
			DataDir:   dataDir.Text,
			JsonDir:   jsonDir.Text,
		}
		conf.SyncConf = syncConf
		if err := storage.StoreConfig(conf); err != nil {
			logrus.Error(err)
		}
	}
	saveBtn1 := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), saveFunc)

	saveBtn2 := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), saveFunc)

	// conn func
	connBtn := &widget.Button{Text: "连接", Icon: theme.MailForwardIcon()}
	disConnBtn := &widget.Button{Text: "断开", Icon: theme.CancelIcon()}
	syncBtn := &widget.Button{Text: "同步JSON", Icon: theme.ConfirmIcon()}
	refreshBtn := &widget.Button{Text: "刷新", Icon: theme.ViewRefreshIcon()}
	svnBtn := &widget.Button{Text: "SVN更新", Icon: theme.ConfirmIcon()}
	excelBtn := &widget.Button{Text: "更新Excel", Icon: theme.FolderIcon()}
	explorBtn := &widget.Button{Text: "资源管理器", Icon: theme.FolderIcon()}
	dlBtn := widget.NewButtonWithIcon("", theme.DownloadIcon(), nil)

	searchEntry := widget.NewEntry()

	// 全选/全取消
	allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
	allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
	allSelBtn.Hide()

	// 连接
	connBtn.OnTapped = func() {
		app.cProgress.Show()
		app.cProgress.Start()
		ciphers := []string{}

		if ip.Text == "" {
			dialog.ShowError(errors.New("IP未填写"), toolWin.w)
			return
		}
		if userName.Text == "" {
			dialog.ShowError(errors.New("账号未填写"), toolWin.w)
			return
		}
		if password.Text == "" {
			dialog.ShowError(errors.New("密码未填写"), toolWin.w)
			return
		}
		if port.Text == "" {
			dialog.ShowError(errors.New("端口未填写"), toolWin.w)
			return
		}

		if localDir.Text == "" {
			dialog.ShowError(errors.New("JSON目录未填写"), toolWin.w)
			return
		}

		if remoteDir.Text == "" {
			dialog.ShowError(errors.New("远程目录未填写"), toolWin.w)
			return
		}
		connBtn.Disable()

		err := app.sshService.Connect(userName.Text, password.Text, ip.Text, "", cast.ToInt(port.Text), ciphers)
		if err != nil {
			dialog.ShowError(err, toolWin.w)
			app.cProgress.Stop()
			app.cProgress.Hide()
			connBtn.Enable()
			return
		} else {
			app.jsonList.LoadItem(localDir.Text)

			app.cProgress.Stop()
			app.cProgress.Hide()
			disConnBtn.Enable()
			syncBtn.Enable()
			app.upProgress.Hide()
			allCancelBtn.Show()
			allSelBtn.Hide()
			refreshBtn.Enable()
			dlBtn.Enable()
		}
	}

	// 刷新JSON列表
	refreshBtn.Disable()
	reloadItem := func() {
		defer func() {
			syncBtn.Enable()
			allSelBtn.Hide()
			allCancelBtn.Show()
		}()
		app.jsonList.LoadItem(localDir.Text)
	}
	refreshBtn.OnTapped = reloadItem

	// 断开
	disConnBtn.Disable()
	disConnBtn.OnTapped = func() {
		defer func() {
			app.jsonList.Reset()
			connBtn.Enable()
			disConnBtn.Disable()
			syncBtn.Disable()
			allSelBtn.Hide()
			allCancelBtn.Show()
			refreshBtn.Disable()
			dlBtn.Disable()
		}()
		app.sshService.Close()
	}

	//资源管理器
	openExplor := func(dir string) {
		if runtime.GOOS == "windows" {
			if err := exec.Command("explorer", filepath.Join(dir)).Start(); err != nil {
				dialog.ShowError(errors.WithMessage(err, "请确认Json目录是否填写正确"), toolWin.w)
				return
			}
		}
	}
	explorBtn.OnTapped = func() {
		// logrus.Debug(localDir.Text)
		if localDir.Text == "" {
			common.ShowTip("Json目录必须填写")
		} else {
			openExplor(localDir.Text)
		}
	}

	//使用说明
	helpBtn1 := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
		quesWin := fyne.CurrentApp().NewWindow("同步JSON使用说明")
		quesWin.SetContent(widget.NewRichTextFromMarkdown(
			`
		* 同步Json的操作仅限于数值热更,非结构热更
		* 将json文件热更到内网测试环境
		* 提示:
			* IP:10.0.0.9 22 (保持一致)
			* 用户名:root(保持一致)
			* 密码:(服务端同学提供)
			* Json目录:(json文件目录全路径)
			* 远程目录:/home/liwei/go_dreamfactory/bin/json/(保持一致)
		* 全部填写完点击【保存配置】
		* 点击【连接】,右侧面板选择要同步的文件
		`))
		quesWin.Resize(fyne.NewSize(350, 200))
		quesWin.SetFixedSize(true)
		quesWin.CenterOnScreen()
		quesWin.Show()
	})

	helpBtn2 := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
		quesWin := fyne.CurrentApp().NewWindow("SVN更新使用说明")
		c := widget.NewRichTextFromMarkdown(
			`
			* 基于Luban工具生成json
			* 建议每次同步JSON之前先点击SVN更新
			* 提示:
				* 服务地址:10.0.1.11 (保持一致)
				* 工作目录:svn下ExcelFile目录全路径
				* LubanCli:Luban.Client\Luban.Client.exe (保持一致)
				* Data目录:Datas(保持一致)
				* JSON目录:output\json(保持一致)
			* 全部填写完点击【保存配置】
			* 点击【SVN更新】生成json文件
			`,
		)
		quesWin.SetContent(c)
		quesWin.Resize(fyne.NewSize(350, 200))
		quesWin.SetFixedSize(true)
		quesWin.CenterOnScreen()
		quesWin.Show()
	})

	//同步JSON
	syncBtn.Disable()
	syncNext := func() {
		defer func() {
			syncBtn.Enable()
			app.upProgress.Hide()
			reloadItem()
			dialog.ShowInformation("提示", "所有文件均上传完毕,需等1-2分钟待文件热更", toolWin.w)
		}()
		syncBtn.Disable()
		app.upProgress.Show()
		app.upProgress.SetValue(0)

		len := len(app.jsonList.SelItemIds)
		num := 0.0

		increment := func(wg *sync.WaitGroup) {
			num += float64(1) / float64(len)
			app.upProgress.SetValue(num)
			wg.Done()
		}

		for _, fileName := range app.jsonList.SelItemIds {
			app.endProgress.Add(1)
			go func(fn string) {
				// time.Sleep(time.Second * time.Duration(rand.Intn(3)))
				if err := app.sshService.ScpCopy(filepath.Join(localDir.Text, fn), remoteDir.Text); err != nil {
					logrus.WithField("err", err).Error("同步json")
					common.ShowTip(err.Error())
				}
				increment(&app.endProgress)
				// 移除已上传的
				app.jsonList.DeleteItem(fn)
				common.ShowTip(fmt.Sprintf("%s 成功上传", fn))
			}(fileName)
		}
		app.endProgress.Wait()
		app.upProgress.SetValue(1)
	}
	syncBtn.OnTapped = func() {
		if app.sshService.Client == nil {
			dialog.ShowError(errors.New("请先连接终端"), toolWin.w)
			return
		}

		//SVN更新提示
		dc := dialog.NewConfirm("提示", "是否要进行SVN更新？", func(b bool) {
			if b {
				common.ShowTip("单击【SVN更新】按钮进行更新")
				svnBtn.FocusGained()
				return
			} else {
				if len(app.jsonList.SelItemIds) == 0 {
					common.ShowTip("没有选择任何文件,或尝试点击【刷新】")
					return
				}
				syncNext()
			}
		}, toolWin.w)
		dc.SetConfirmText("必须的")
		dc.SetDismissText("我拿生命担保无需更新")
		dc.Show()
	}

	// excel更新
	excelBtn.OnTapped = func() {
		if workDir.Text == "" {
			common.ShowTip("工作目录必须填写")
		} else {
			openExplor(workDir.Text)
		}
	}

	// SVN更新
	svnNext := func() {
		defer func() {
			app.cProgress.Hide()
			app.cProgress.Stop()
			svnBtn.Enable()
		}()
		svnBtn.Disable()
		app.cProgress.Show()
		app.cProgress.Start()
		commandStr := `%s -h %s -j cfg --  -d %s --input_data_dir %s  --output_data_dir %s --gen_types data_json -s server`
		arg := fmt.Sprintf(commandStr,
			filepath.Join(workDir.Text, lubanCli.Text),
			lubanAddr.Text,
			filepath.Join(workDir.Text, define.Text),
			filepath.Join(workDir.Text, dataDir.Text),
			filepath.Join(workDir.Text, jsonDir.Text),
		)

		logrus.Debug(arg)
		c := exec.Command("cmd.exe", "/c", arg)
		if err := c.Run(); err != nil {
			dialog.ShowError(err, toolWin.w)
			return
		}
	}
	svnBtn.OnTapped = func() {

		//提示更新excel
		dc := dialog.NewConfirm("提示", "是否要更新excel?", func(b bool) {
			if b {
				//打开资源管理器
				openExplor(workDir.Text)
				return
			} else {
				svnNext()
			}
		}, toolWin.w)
		dc.SetConfirmText("必须的")
		dc.SetDismissText("我拿生命担保无需更新")
		dc.Show()
	}

	// 全部取消
	allCancelBtn.OnTapped = func() {
		defer func() {
			allCancelBtn.Hide()
			allSelBtn.Show()
		}()
		for i, v := range app.jsonList.CachedList.Items {
			app.jsonList.CachedList.Items[i].Checked = true
			app.jsonList.SelItemIds = append(app.jsonList.SelItemIds, v.Text)
			app.jsonList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
		}
		app.jsonList.ItemList.Refresh()
	}

	// 全选
	allSelBtn.OnTapped = func() {
		defer func() {
			allCancelBtn.Show()
			allSelBtn.Hide()
		}()
		app.jsonList.SelItemIds = []string{}
		for i, v := range app.jsonList.CachedList.Items {
			app.jsonList.CachedList.Items[i].Checked = false
			app.jsonList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
		}
		app.jsonList.ItemList.Refresh()
	}

	// 搜索
	searchEntry.PlaceHolder = "搜索"
	searchEntry.OnChanged = func(s string) {
		if app.sshService.Client == nil {
			dialog.ShowError(errors.New("请先连接终端"), toolWin.w)
			return
		}
		if s == "" {
			reloadItem()
		} else {
			// go func() {
			newList := []common.Item{}
			for _, v := range app.jsonList.SearchItem {
				if strings.Contains(v.Text, s) {
					newList = append(newList, v)
				}
			}
			app.jsonList.CachedList.Items = newList
			app.jsonList.ItemList.Refresh()
			// }()
		}
	}

	// 下载日志
	dlBtn.Disable()
	dlBtn.OnTapped = func() {
		w := app.createDownloadWindow()
		w.Show()
		w.SetCloseIntercept(func() {
			dlBtn.Enable()
			w.Close()
		})
		dlBtn.Disable()
	}

	// 创建json列表
	app.jsonList.ItemList = app.jsonList.CreateDefaultCheckList()

	btns1 := container.NewHBox(helpBtn1, dlBtn, &layout.Spacer{}, saveBtn1, connBtn, disConnBtn)
	btns2 := container.NewHBox(helpBtn2, &layout.Spacer{}, saveBtn2, excelBtn, svnBtn)

	split := container.NewHSplit(container.NewVBox(canvas.NewText("---只能在非上产环境!!!同步Json的操作仅限于数值热更,非结构热更---", color.RGBA{255, 0, 0, 255}), configForm,
		btns1, svnForm, btns2, app.cProgress), container.NewBorder(
		container.NewBorder(nil, nil, container.NewHBox(allCancelBtn, allSelBtn, syncBtn, refreshBtn), container.NewHBox(explorBtn), searchEntry),
		nil, nil, nil, app.jsonList.ItemList))
	split.Offset = 0.45
	content.Objects = append(content.Objects, container.NewBorder(nil, app.upProgress, nil, nil, split))

	app.tabItem.Content = content

	return nil
}

func (a *appTerm) GetAppName() string {
	return common.TOOLBAR_TERM
}
func (a *appTerm) Icon() fyne.Resource {
	return theme.MailSendIcon()
}

func OpenExplor(dir string) {
	if dir == "" {
		common.ShowTip("资源管理器路径错误")
		return
	}
	if runtime.GOOS == "windows" {
		if err := exec.Command("explorer", filepath.Join(dir)).Start(); err != nil {
			dialog.ShowError(errors.WithMessage(err, "请确认Json目录是否填写正确"), toolWin.w)
			return
		}
	}
}

func (a *appTerm) OnClose() bool {
	dialog.ShowConfirm("关闭终端", "你希望断开连接吗?", func(b bool) {
		if !b {
			return
		}
		if a.sshService.Client != nil {
			a.sshService.Client.Close()
		}
	}, toolWin.w)
	return true
}

func (a *appTerm) createDownloadWindow() fyne.Window {
	w := toolWin.app.NewWindow("日志")

	a.downloadList = common.NewItemList()

	a.downloadList.ItemList = a.downloadList.CreateDownloadList()

	remoteLogDirEntry := widget.NewEntry()
	remoteLogDirEntry.PlaceHolder = "下载到"
	remoteLogDirEntry.Text = "/home/liwei/go_dreamfactory/bin/log/"

	saveDirEntry := widget.NewEntry()
	saveDirEntry.PlaceHolder = "保存目录"

	editorEntry := widget.NewEntry()
	editorEntry.PlaceHolder = "编辑器可执行文件路径"

	// config
	confForm := widget.NewForm(
		widget.NewFormItem("远程目录", remoteLogDirEntry),
		widget.NewFormItem("下载目录", saveDirEntry),
		widget.NewFormItem("编辑器", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
			openFile(editorEntry, w)
		}), editorEntry)),
	)

	// 进度条
	downloadProgress := widget.NewProgressBarInfinite()

	var (
		storage os_storage.Storage
		conf    *os_storage.Config
		err     error
	)
	defer func() {
		downloadProgress.Hide()
		//加载配置
		storage, _ = os_storage.NewOSStorage()
		conf, err = storage.LoadConfig()
		if err != nil {
			return
		}

		// sshConf = service.GetDbService().GetSSHConf(common.BUCKET_SSHCONF)
		if conf.SyncConf != nil {
			if conf.SyncConf.LogDir == "" {
				remoteLogDirEntry.Text = "/home/liwei/go_dreamfactory/bin/log/"
			} else {
				remoteLogDirEntry.Text = conf.SyncConf.LogDir
			}

			saveDirEntry.Text = conf.SyncConf.SaveDir
			editorEntry.Text = conf.SyncConf.Editor
		} else {
			conf.SyncConf = &os_storage.SyncConfig{}
		}

		remoteLogDirEntry.Refresh()
		saveDirEntry.Refresh()
		editorEntry.Refresh()

		//load list
		if remoteLogDirEntry.Text == "" {
			return
		}
		files, err := a.sshService.GetRemoteDir(remoteLogDirEntry.Text)
		if err != nil {
			dialog.ShowError(err, toolWin.w)
			return
		}

		for _, f := range files {
			a.downloadList.AddItem(common.Item{Id: f.FileName, Text: f.FileName, Size: f.Size})
		}
	}()

	//下载
	allDownloadBtn := widget.NewButtonWithIcon("下载", theme.DownloadIcon(), nil)
	DownloadDirBtn := widget.NewButtonWithIcon("打开下载目录", theme.FolderIcon(), nil)
	EditorBtn := widget.NewButtonWithIcon("打开编辑器", theme.FolderIcon(), nil)
	saveConfBtn := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), func() {
		// 保存配置
		conf.SyncConf.LogDir = remoteLogDirEntry.Text
		conf.SyncConf.SaveDir = saveDirEntry.Text
		conf.SyncConf.Editor = editorEntry.Text
		if err := storage.StoreConfig(conf); err != nil {
			logrus.Error(err)
		}
	})

	allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
	allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
	allSelBtn.Hide()

	// 全部取消
	allCancelBtn.OnTapped = func() {
		defer func() {
			allCancelBtn.Hide()
			allSelBtn.Show()
		}()
		dlist := a.downloadList
		for i, v := range dlist.CachedList.Items {
			dlist.CachedList.Items[i].Checked = true
			dlist.SelItemIds = append(dlist.SelItemIds, v.Text)
			dlist.ItemList.UpdateItem(i, container.NewHBox(
				widget.NewCheck(v.Text, nil),
				&layout.Spacer{},
				widget.NewLabel(common.ConvertFileSize(v.Size))))
		}
		dlist.ItemList.Refresh()
	}

	// 全选
	allSelBtn.OnTapped = func() {
		defer func() {
			allCancelBtn.Show()
			allSelBtn.Hide()
		}()
		dlist := a.downloadList
		dlist.SelItemIds = []string{}
		for i, v := range dlist.CachedList.Items {
			dlist.CachedList.Items[i].Checked = false
			dlist.ItemList.UpdateItem(i, container.NewHBox(
				widget.NewCheck(v.Text, nil),
				&layout.Spacer{},
				widget.NewLabel(common.ConvertFileSize(v.Size))))
		}
		dlist.ItemList.Refresh()
	}

	// 打开下载目录
	DownloadDirBtn.OnTapped = func() {
		OpenExplor(saveDirEntry.Text)
	}

	EditorBtn.OnTapped = func() {
		if editorEntry.Text == "" {
			common.ShowTip("请配置编辑器")
			return
		}
		if runtime.GOOS == "windows" {
			for _, v := range a.downloadList.SelItemIds {
				if err := exec.Command(editorEntry.Text, filepath.Join(saveDirEntry.Text, v)).Start(); err != nil {
					dialog.ShowError(errors.WithMessage(err, "请确认编辑器目录是否填写正确"), toolWin.w)
					return
				}
			}

		}
	}

	//使用说明
	helpBtn := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
		quesWin := fyne.CurrentApp().NewWindow("日志使用说明")
		quesWin.SetContent(widget.NewRichTextFromMarkdown(
			`
		* 下载远程服务日志到本地分析
		* 提示:
			* 远程目录 (保持一致)
			* 下载目录:填写本地任意目录
			* 编辑器:选择已安装的编辑器(例如:notepad++、Sublime)
		* 操作:
			* 选择文件,点击【打开编辑器】
		`))
		quesWin.Resize(fyne.NewSize(350, 200))
		quesWin.SetFixedSize(true)
		quesWin.CenterOnScreen()
		quesWin.Show()
	})

	var wg sync.WaitGroup
	//download
	allDownloadBtn.OnTapped = func() {
		downloadProgress.Show()
		downloadProgress.Start()
		selItems := a.downloadList.SelItemIds
		if len(selItems) == 0 {
			common.ShowTip("请选择下载的文件")
			return
		} else {
			for _, item := range selItems {
				wg.Add(1)
				go func(name string) {
					defer func() {
						downloadProgress.Hide()
						wg.Done()
					}()
					logrus.WithField("filepath", saveDirEntry.Text+name).Debug("下载")
					if err := a.sshService.ScpDownload(saveDirEntry.Text, remoteLogDirEntry.Text+name); err != nil {
						common.ShowTip(name + " 下载失败")
					}
				}(item)
			}
			wg.Wait()
		}
	}

	btns := container.NewHBox(allSelBtn, allCancelBtn, allDownloadBtn, DownloadDirBtn, EditorBtn, helpBtn)
	//toolbar
	toolbar := container.NewBorder(nil, btns, nil, saveConfBtn, confForm)

	//layout
	w.SetContent(container.NewBorder(toolbar, downloadProgress, nil, nil, a.downloadList.ItemList))

	w.Resize(fyne.NewSize(800, 450))
	w.CenterOnScreen()
	return w
}
