package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"
	"updater/download"
	"updater/utils"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
	"github.com/jessevdk/go-flags"
	"infish.cn/comm"
)

// 启动程序
// 下载文件->成功->失败
// 下载成功->通知nats关闭原程序
// 安装->成功->失败
// 成功启动程序
// 失败还原程序，关闭更新应用

// Define the progress variables, a channel and a variable

var progress float32
var progressIncrementer chan float32

var GAppOption = &AppOption{}

type AppOption struct {
	NatsPort int    `short:"p" long:"np" description:"nats port"`
	Url      string `short:"u" long:"url" description:"url"`
	Upload   string `short:"d" long:"upload" description:"upload"`
}

func (o *AppOption) Parse() {
	flags.NewParser(o, flags.Default|flags.IgnoreUnknown).Parse()
}

type ProcesCallback = func(int, string)

func upgradeLancherExe(url string, cb ProcesCallback) {
	exepath, _ := os.Executable()
	appExeDir, _ := filepath.Split(exepath)

	//1 下载lancher.exe
	downLancherExe := filepath.Join(appExeDir, "lancher")
	err := download.DownloadExe(downLancherExe, GAppOption.Url)
	if err != nil {
		fmt.Println(err)
		cb(-1, "下载安装包失败!")
		return
	}

	//2 通知lancher.exe退出
	remoteCnn, err := comm.NewNatsBus(fmt.Sprintf("nats://localhost:%d", GAppOption.NatsPort), 1, 1, []*comm.NatsStreamWather{})
	if err != nil {
		fmt.Println(err)
		cb(-1, "安装失败!")
		return
	}

	err = remoteCnn.Publish("lancher.quit", []byte{})
	if err != nil {
		fmt.Println(err)
		cb(-1, "安装失败!")
		return
	}
	remoteCnn.GetNatsConn().Flush()

	time.Sleep(time.Second * 3)

	//3 安装新的lancher.exe
	currLancherExe := filepath.Join(appExeDir, "lancher.exe")
	currLancherExeback := filepath.Join(appExeDir, "lancher.exe.back")
	err = os.Rename(currLancherExe, currLancherExeback)
	if err != nil {
		fmt.Println(err)
		cb(-1, "安装失败!")
		return
	}

	err = os.Rename(downLancherExe, currLancherExe)

	if err != nil {
		fmt.Println(err)
		cb(-1, "安装失败!")
		os.Rename(currLancherExeback, currLancherExe)
		return
	}

	//4 启动lancher.exe
	// 重启
	cmd := exec.Command(currLancherExe)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Start()
	if err != nil {
		fmt.Printf("failed to call cmd.Start(): %v", err)
		os.Rename(currLancherExeback, currLancherExe)
		cb(-1, "新包启动失败!")
		return
	}
	cb(1, "")

	time.Sleep(time.Second)
	os.Remove(currLancherExeback)
	os.Remove(downLancherExe)

	os.Exit(0)
}

var installError = ""
var installSucc = false

func main() {
	GAppOption.Parse()
	fmt.Println(GAppOption)
	// 上传编译好的执行文件
	if len(GAppOption.Upload) > 0 {
		zipPath, err := utils.UploadExe()
		if err != nil {
			fmt.Println(err)
		}
		// 成功后删除压缩文件
		os.Remove(zipPath)
		return
	}

	// // Setup a separate channel to provide ticks to increment progress
	progressIncrementer = make(chan float32)
	go func() {
		for {
			time.Sleep(time.Second / 25)
			progressIncrementer <- 0.004
		}
	}()

	w := app.NewWindow(
		app.Title("uploader"),
		app.Size(unit.Dp(600), unit.Dp(400)),
	)

	go func() {
		// create new window

		if err := draw(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	go func() {
		if len(GAppOption.Url) < 1 {
			return
		}

		upgradeLancherExe(GAppOption.Url, func(i int, s string) {
			fmt.Println("xxx=>", i, s)

			if i == -1 {
				installError = s
				w.Invalidate()
				return
			}
			installSucc = true
			w.Invalidate()
		})
	}()

	app.Main()
}

type C = layout.Context
type D = layout.Dimensions

func draw(w *app.Window) error {
	// ops are the operations from the UI
	var ops op.Ops

	// startButton is a clickable widget
	var startButton widget.Clickable

	// is the egg boiling?
	var boiling bool

	// th defines the material design style
	th := material.NewTheme([]text.FontFace{})

	for {
		select {
		// listen for events in the window.
		case e := <-w.Events():

			// detect what type of event
			switch e := e.(type) {

			// this is sent when the application should re-render.
			case system.FrameEvent:
				gtx := layout.NewContext(&ops, e)
				// Let's try out the flexbox layout concept
				if startButton.Clicked() {
					boiling = !boiling
				}

				layout.Flex{
					// Vertical alignment, from top to bottom
					Axis: layout.Vertical,
					// Empty space is left at the start, i.e. at the top
					Spacing: layout.SpaceStart,
				}.Layout(gtx,
					layout.Rigid(
						func(gtx C) D {
							bar := material.ProgressBar(th, progress)
							return bar.Layout(gtx)
						},
					),
					layout.Rigid(
						func(gtx C) D {
							// We start by defining a set of margins
							margins := layout.Inset{
								Top:    unit.Dp(25),
								Bottom: unit.Dp(25),
								Right:  unit.Dp(25),
								Left:   unit.Dp(25),
							}
							// Then we lay out within those margins ...
							return margins.Layout(gtx,
								// ...the same function we earlier used to create a button
								func(gtx C) D {
									var text string
									if !boiling {
										text = "Start"
									} else {
										text = "Stop"
									}
									btn := material.Button(th, &startButton, text)
									return btn.Layout(gtx)
								},
							)
						},
					),
				)
				e.Frame(gtx.Ops)

			// this is sent when the application is closed.
			case system.DestroyEvent:
				return e.Err
			}

		//listen for events in the incrementor channel
		case p := <-progressIncrementer:
			if boiling && progress < 1 {
				progress += p
				w.Invalidate()
			}
		}
	}
}
