// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only

package tui

import (
	"time"

	"github.com/clearlinux/clr-installer/network"

	"github.com/VladimirMarkelov/clui"
	term "github.com/nsf/termbox-go"
)

// NetworkInterfacePage is the Page implementation for the network configuration page
type NetworkInterfacePage struct {
	BasePage
	IPEdit           *clui.EditField
	IPWarning        *clui.Label
	NetMaskEdit      *clui.EditField
	NetMaskWarning   *clui.Label
	GatewayEdit      *clui.EditField
	GatewayWarning   *clui.Label
	DNSServerEdit    *clui.EditField
	DNSServerWarning *clui.Label
	DNSDomainEdit    *clui.EditField
	DNSDomainWarning *clui.Label
	ifaceLbl         *clui.Label
	DHCPCheck        *clui.CheckBox
	confirmBtn       *SimpleButton

	defaultValues struct {
		IP        string
		NetMask   string
		Gateway   string
		DNSServer string
		DNSDomain string
		DHCP      bool
	}
}

func (page *NetworkInterfacePage) getSelectedInterface() *network.Interface {
	var iface *network.Interface
	var ok bool

	prevPage := page.tui.getPage(TuiPageNetwork)
	if iface, ok = prevPage.GetData().(*network.Interface); !ok {
		return nil
	}

	return iface
}

func (page *NetworkInterfacePage) clearAllWarnings() {
	page.IPWarning.SetTitle("")
	page.NetMaskWarning.SetTitle("")
	page.GatewayWarning.SetTitle("")
	page.DNSServerWarning.SetTitle("")
	page.DNSDomainWarning.SetTitle("")

	page.setConfirmButton()
}

// Activate will set the fields with the selected interface info
func (page *NetworkInterfacePage) Activate() {
	sel := page.getSelectedInterface()

	page.ifaceLbl.SetTitle(sel.Name)
	page.IPEdit.SetTitle("")
	page.NetMaskEdit.SetTitle("")
	page.GatewayEdit.SetTitle(sel.Gateway)
	page.DNSServerEdit.SetTitle(sel.DNSServer)
	page.DNSDomainEdit.SetTitle(sel.DNSDomain)
	page.clearAllWarnings()

	page.defaultValues.Gateway = sel.Gateway
	page.defaultValues.DNSServer = sel.DNSServer
	page.defaultValues.DNSDomain = sel.DNSDomain
	page.defaultValues.DHCP = sel.DHCP

	showIPv4 := sel.HasIPv4Addr()
	for _, addr := range sel.Addrs {
		if showIPv4 && addr.Version != network.IPv4 {
			continue
		}

		page.IPEdit.SetTitle(addr.IP)
		page.NetMaskEdit.SetTitle(addr.NetMask)

		page.defaultValues.IP = addr.IP
		page.defaultValues.NetMask = addr.NetMask
		break
	}

	page.setDHCP(sel.DHCP)
}

func (page *NetworkInterfacePage) setConfirmButton() {
	if page.IPWarning.Title() == "" && page.NetMaskWarning.Title() == "" &&
		page.GatewayWarning.Title() == "" &&
		page.DNSServerWarning.Title() == "" && page.DNSDomainWarning.Title() == "" {
		page.confirmBtn.SetEnabled(true)
	} else {
		page.confirmBtn.SetEnabled(false)
	}
}

func (page *NetworkInterfacePage) validateIPField(editField *clui.EditField, warnLabel *clui.Label) {
	warnLabel.SetTitle(network.IsValidIP(editField.Title()))

	page.setConfirmButton()
}

func (page *NetworkInterfacePage) validateIPOrHostField(editField *clui.EditField, warnLabel *clui.Label) {
	warning := network.IsValidIP(editField.Title())

	if warning != "" {
		hostWarning := network.IsValidDomainName(editField.Title())
		if hostWarning != "" {
			warning = hostWarning // + " OR " + warning
		} else {
			warning = hostWarning // empty string
		}
	}

	warnLabel.SetTitle(warning)
	page.setConfirmButton()
}

func (page *NetworkInterfacePage) validateDomainField(editField *clui.EditField, warnLabel *clui.Label) {
	warnLabel.SetTitle(network.IsValidDomainName(editField.Title()))

	page.setConfirmButton()
}

func (page *NetworkInterfacePage) getDHCP() bool {
	state := page.DHCPCheck.State()
	if state == 1 {
		return true
	}

	return false
}

func (page *NetworkInterfacePage) setDHCP(DHCP bool) {
	state := 0

	if DHCP {
		state = 1
	}

	page.DHCPCheck.SetState(state)
}

func newFieldLabel(frame *clui.Frame, text string) *clui.Label {
	lbl := clui.CreateLabel(frame, AutoSize, 2, text, Fixed)
	lbl.SetAlign(AlignRight)
	return lbl
}

func validateIPEdit(k term.Key, ch rune) bool {
	validKeys := []rune{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'}

	if k == term.KeyBackspace || k == term.KeyBackspace2 {
		return false
	}

	if k == term.KeyArrowUp || k == term.KeyArrowDown ||
		k == term.KeyArrowLeft || k == term.KeyArrowRight {
		return false
	}

	for _, curr := range validKeys {
		if curr == ch {
			return false
		}
	}

	return true
}

func newNetworkInterfacePage(tui *Tui) (Page, error) {
	page := &NetworkInterfacePage{}
	page.setup(tui, TuiPageInterface, NoButtons, TuiPageMenu)

	frm := clui.CreateFrame(page.content, AutoSize, AutoSize, BorderNone, Fixed)
	frm.SetPack(clui.Horizontal)

	lblFrm := clui.CreateFrame(frm, 10, AutoSize, BorderNone, Fixed)
	lblFrm.SetPack(clui.Vertical)
	lblFrm.SetPaddings(1, 0)

	newFieldLabel(lblFrm, "Interface:")
	newFieldLabel(lblFrm, "Ip address:")
	newFieldLabel(lblFrm, "Subnet mask:")
	newFieldLabel(lblFrm, "Gateway:")
	newFieldLabel(lblFrm, "DNS Server:")
	newFieldLabel(lblFrm, "DNS Domain:")

	fldFrm := clui.CreateFrame(frm, 50, AutoSize, BorderNone, Fixed)
	fldFrm.SetPack(clui.Vertical)

	ifaceFrm := clui.CreateFrame(fldFrm, 5, 2, BorderNone, Fixed)
	ifaceFrm.SetPack(clui.Vertical)

	page.ifaceLbl = clui.CreateLabel(ifaceFrm, AutoSize, 2, "", Fixed)
	page.ifaceLbl.SetAlign(AlignLeft)

	page.IPEdit, page.IPWarning = newEditField(fldFrm, true, validateIPEdit, 0)
	page.NetMaskEdit, page.NetMaskWarning = newEditField(fldFrm, true, validateIPEdit, 0)
	page.GatewayEdit, page.GatewayWarning = newEditField(fldFrm, true, nil, 0)
	page.DNSServerEdit, page.DNSServerWarning = newEditField(fldFrm, true, nil, 0)
	page.DNSDomainEdit, page.DNSDomainWarning = newEditField(fldFrm, true, nil, 0)

	page.IPEdit.OnChange(func(ev clui.Event) {
		page.validateIPField(page.IPEdit, page.IPWarning)
	})
	page.IPEdit.OnActive(func(active bool) {
		if page.IPEdit.Active() {
			page.validateIPField(page.IPEdit, page.IPWarning)
		}
	})
	page.IPWarning.SetVisible(true)
	page.NetMaskEdit.OnChange(func(ev clui.Event) {
		page.validateIPField(page.NetMaskEdit, page.NetMaskWarning)
	})
	page.NetMaskEdit.OnActive(func(active bool) {
		if page.NetMaskEdit.Active() {
			page.validateIPField(page.NetMaskEdit, page.NetMaskWarning)
		}
	})
	page.NetMaskWarning.SetVisible(true)
	page.GatewayEdit.OnChange(func(ev clui.Event) {
		page.validateIPOrHostField(page.GatewayEdit, page.GatewayWarning)
	})
	page.GatewayEdit.OnActive(func(active bool) {
		if page.GatewayEdit.Active() {
			page.validateIPOrHostField(page.GatewayEdit, page.GatewayWarning)
		}
	})
	page.GatewayWarning.SetVisible(true)
	page.DNSServerEdit.OnChange(func(ev clui.Event) {
		page.validateIPOrHostField(page.DNSServerEdit, page.DNSServerWarning)
	})
	page.DNSServerEdit.OnActive(func(active bool) {
		if page.DNSServerEdit.Active() {
			page.validateIPOrHostField(page.DNSServerEdit, page.DNSServerWarning)
		}
	})
	page.DNSServerWarning.SetVisible(true)
	page.DNSDomainEdit.OnChange(func(ev clui.Event) {
		page.validateDomainField(page.DNSDomainEdit, page.DNSDomainWarning)
	})
	page.DNSDomainEdit.OnActive(func(active bool) {
		if page.DNSDomainEdit.Active() {
			page.validateDomainField(page.DNSDomainEdit, page.DNSDomainWarning)
		}
	})
	page.DNSDomainWarning.SetVisible(true)

	dhcpFrm := clui.CreateFrame(fldFrm, 5, 2, BorderNone, Fixed)
	dhcpFrm.SetPack(clui.Vertical)

	page.DHCPCheck = clui.CreateCheckBox(dhcpFrm, 1, "Automatic/dhcp", Fixed)

	page.DHCPCheck.OnChange(func(ev int) {
		enable := true

		if ev == 1 {
			enable = false
			page.clearAllWarnings()
		} else {
			page.validateIPField(page.IPEdit, page.IPWarning)
			page.validateIPField(page.NetMaskEdit, page.NetMaskWarning)
			page.validateIPOrHostField(page.GatewayEdit, page.GatewayWarning)
			page.validateIPOrHostField(page.DNSServerEdit, page.DNSServerWarning)
			page.validateDomainField(page.DNSDomainEdit, page.DNSDomainWarning)
		}

		page.IPEdit.SetEnabled(enable)
		page.NetMaskEdit.SetEnabled(enable)
		page.GatewayEdit.SetEnabled(enable)
		page.DNSServerEdit.SetEnabled(enable)
		page.DNSDomainEdit.SetEnabled(enable)
	})

	btnFrm := clui.CreateFrame(fldFrm, 30, 1, BorderNone, Fixed)
	btnFrm.SetPack(clui.Horizontal)
	btnFrm.SetGaps(1, 1)
	btnFrm.SetPaddings(2, 0)

	cancelBtn := CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Cancel", Fixed)
	cancelBtn.OnClick(func(ev clui.Event) {
		page.GotoPage(TuiPageNetwork)
	})

	page.confirmBtn = CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Confirm", Fixed)
	page.confirmBtn.OnClick(func(ev clui.Event) {
		IP := page.IPEdit.Title()
		NetMask := page.NetMaskEdit.Title()
		DHCP := page.getDHCP()
		Gateway := page.GatewayEdit.Title()
		DNSServer := page.DNSServerEdit.Title()
		DNSDomain := page.DNSDomainEdit.Title()
		changed := false

		if IP != page.defaultValues.IP {
			changed = true
		}

		if NetMask != page.defaultValues.NetMask {
			changed = true
		}

		if DHCP != page.defaultValues.DHCP {
			changed = true
		}

		if Gateway != page.defaultValues.Gateway {
			changed = true
		}

		if DNSServer != page.defaultValues.DNSServer {
			changed = true
		}

		if DNSDomain != page.defaultValues.DNSDomain {
			changed = true
		}

		if changed {
			sel := page.getSelectedInterface()
			if !sel.HasIPv4Addr() {
				sel.AddAddr(IP, NetMask, network.IPv4)
			} else {
				for _, addr := range sel.Addrs {
					if addr.Version != network.IPv4 {
						continue
					}

					addr.IP = IP
					addr.NetMask = NetMask
					break
				}
			}

			sel.DHCP = DHCP
			sel.Gateway = Gateway
			sel.DNSServer = DNSServer
			sel.DNSDomain = DNSDomain
			page.getModel().AddNetworkInterface(sel)
		}

		if dialog, err := CreateNetworkTestDialogBox(page.tui.model); err == nil {
			dialog.OnClose(func() {
				page.GotoPage(TuiPageNetwork)
			})
			if dialog.RunNetworkTest() {
				page.getModel().CopyNetwork = true
				page.tui.getPage(TuiPageNetwork).SetDone(true)

				// Automatically close if it worked
				clui.RefreshScreen()
				time.Sleep(time.Second)
				dialog.Close()
			} else {
				page.tui.getPage(TuiPageNetwork).SetDone(false)
			}
		}
	})

	return page, nil
}
