package main

import (
	"fmt"
	"path/filepath"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/storage"
	"fyne.io/fyne/v2/widget"
	"go.arsenm.dev/itd/api"
)

func upgradeTab(parent fyne.Window, client *api.Client) *fyne.Container {
	var (
		archivePath  string
		firmwarePath string
		initPktPath  string
	)

	var archiveBtn *widget.Button
	// Create archive selection dialog
	archiveDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
		if e != nil || uc == nil {
			return
		}
		uc.Close()
		archivePath = uc.URI().Path()
		archiveBtn.SetText(fmt.Sprintf("Select archive (.zip) [%s]", filepath.Base(archivePath)))
	}, parent)
	// Limit dialog to .zip files
	archiveDialog.SetFilter(storage.NewExtensionFileFilter([]string{".zip"}))
	// Create button to show dialog
	archiveBtn = widget.NewButton("Select archive (.zip)", archiveDialog.Show)

	var firmwareBtn *widget.Button
	// Create firmware selection dialog
	firmwareDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
		if e != nil || uc == nil {
			return
		}
		uc.Close()
		firmwarePath = uc.URI().Path()
		firmwareBtn.SetText(fmt.Sprintf("Select firmware (.bin) [%s]", filepath.Base(firmwarePath)))
	}, parent)
	// Limit dialog to .bin files
	firmwareDialog.SetFilter(storage.NewExtensionFileFilter([]string{".bin"}))
	// Create button to show dialog
	firmwareBtn = widget.NewButton("Select firmware (.bin)", firmwareDialog.Show)

	var initPktBtn *widget.Button
	// Create init packet selection dialog
	initPktDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, e error) {
		if e != nil || uc == nil {
			return
		}
		uc.Close()
		initPktPath = uc.URI().Path()
		initPktBtn.SetText(fmt.Sprintf("Select init packet (.dat) [%s]", filepath.Base(initPktPath)))
	}, parent)
	// Limit dialog to .dat files
	initPktDialog.SetFilter(storage.NewExtensionFileFilter([]string{".dat"}))
	// Create button to show dialog
	initPktBtn = widget.NewButton("Select init packet (.dat)", initPktDialog.Show)

	// Hide init packet and firmware buttons
	initPktBtn.Hide()
	firmwareBtn.Hide()

	// Create dropdown to select upgrade type
	upgradeTypeSelect := widget.NewSelect([]string{
		"Archive",
		"Files",
	}, func(s string) {
		// Hide all buttons
		archiveBtn.Hide()
		initPktBtn.Hide()
		firmwareBtn.Hide()
		// Unhide appropriate button(s)
		switch s {
		case "Archive":
			archiveBtn.Show()
		case "Files":
			initPktBtn.Show()
			firmwareBtn.Show()
		}
	})
	// Select first elemetn
	upgradeTypeSelect.SetSelectedIndex(0)

	// Create new button to start DFU
	startBtn := widget.NewButton("Start", func() {
		// If archive path does not exist and both init packet and firmware paths
		// also do not exist, return error
		if archivePath == "" && (initPktPath == "" && firmwarePath == "") {
			guiErr(nil, "Upgrade requires archive or files selected", false, parent)
			return
		}

		// Create new label for byte progress
		progressLbl := widget.NewLabelWithStyle("0 / 0 B", fyne.TextAlignCenter, fyne.TextStyle{})
		// Create new progress bar
		progressBar := widget.NewProgressBar()
		// Create modal dialog containing label and progress bar
		progressDlg := widget.NewModalPopUp(container.NewVBox(
			layout.NewSpacer(),
			progressLbl,
			progressBar,
			layout.NewSpacer(),
		), parent.Canvas())
		// Resize modal to 300x100
		progressDlg.Resize(fyne.NewSize(300, 100))

		var fwUpgType api.UpgradeType
		var files []string
		// Get appropriate upgrade type and file paths
		switch upgradeTypeSelect.Selected {
		case "Archive":
			fwUpgType = api.UpgradeTypeArchive
			files = append(files, archivePath)
		case "Files":
			fwUpgType = api.UpgradeTypeFiles
			files = append(files, initPktPath, firmwarePath)
		}

		progress, err := client.FirmwareUpgrade(fwUpgType, files...)
		if err != nil {
			guiErr(err, "Error initiating DFU", false, parent)
			return
		}

		// Show progress dialog
		progressDlg.Show()

		for event := range progress {
			// Set label text to received / total B
			progressLbl.SetText(fmt.Sprintf("%d / %d B", event.Received, event.Total))
			// Set progress bar values
			progressBar.Max = float64(event.Total)
			progressBar.Value = float64(event.Received)
			// Refresh progress bar
			progressBar.Refresh()
			// If transfer finished, break
			if int64(event.Sent) == event.Total {
				break
			}
		}

		// Hide progress dialog after completion
		progressDlg.Hide()

		// Reset screen to default
		upgradeTypeSelect.SetSelectedIndex(0)
		firmwareBtn.SetText("Select firmware (.bin)")
		initPktBtn.SetText("Select init packet (.dat)")
		archiveBtn.SetText("Select archive (.zip)")
		firmwarePath = ""
		initPktPath = ""
		archivePath = ""

		dialog.NewInformation(
			"Upgrade Complete",
			"The firmware was transferred successfully.\nRemember to validate the firmware in InfiniTime settings.",
			parent,
		).Show()
	})

	// Return container containing all elements
	return container.NewVBox(
		layout.NewSpacer(),
		upgradeTypeSelect,
		archiveBtn,
		firmwareBtn,
		initPktBtn,
		startBtn,
		layout.NewSpacer(),
	)
}