package ui

import (
	"fmt"
	"time"

	"github.com/aditya-K2/gspt/config"
	"github.com/aditya-K2/gspt/spt"
	"github.com/aditya-K2/tview"
	"github.com/aditya-K2/utils"
	"github.com/gdamore/tcell/v2"
)

var (
	ImgX  int
	ImgY  int
	ImgW  int
	ImgH  int
	start = true
)

var (
	TrackStyle         tcell.Style
	AlbumStyle         tcell.Style
	ArtistStyle        tcell.Style
	TimeStyle          tcell.Style
	GenreStyle         tcell.Style
	PlaylistNavStyle   tcell.Style
	NavStyle           tcell.Style
	ContextMenuStyle   tcell.Style
	NotSelectableStyle tcell.Style
)

var (
	App      *tview.Application
	coverArt *CoverArt
	Main     *interactiveView
	root     *Root
	Flex     *tview.Flex
	cfg      = config.Config
)

func onConfigChange() {
	setStyles()
	setIcons()
	setBorderRunes()
	if coverArt != nil && cfg.Image == config.ImageShow {
		coverArt.RefreshState()
	}
}

func rectWatcher() {
	// Wait Until the ImagePreviewer is drawn
	// Ensures that cover art is not drawn before the UI is rendered.
	// Ref Issue: #39
	drawCh := make(chan bool)
	go func() {
		for ImgX == 0 && ImgY == 0 {
			ImgX, ImgY, ImgW, ImgH = coverArt.GetRect()
		}
		drawCh <- true
	}()

	go func() {
		// Waiting for the draw channel
		draw := <-drawCh
		if draw {
			go func() {
				for {
					_ImgX, _ImgY, _ImgW, _ImgH := coverArt.GetRect()
					if start {
						progressRoutine()
						start = false
					}
					if _ImgX != ImgX || _ImgY != ImgY ||
						_ImgW != ImgW || _ImgH != ImgH {
						ImgX = _ImgX
						ImgY = _ImgY
						ImgW = _ImgW
						ImgH = _ImgH
						coverArt.RefreshState()
					}
					time.Sleep(time.Millisecond * time.Duration(cfg.RedrawInterval))
				}
			}()
		}
	}()
}

func NewApplication() *tview.Application {
	onConfigChange()
	config.OnConfigChange = onConfigChange

	App = tview.NewApplication()
	root = NewRoot()
	coverArt = newCoverArt()
	Main = NewInteractiveView()
	Main.SetBorder(true)

	progressBar := NewProgressBar().SetProgressFunc(progressFunc)
	searchbar := NewSearchBar()
	navMenu := NewNavMenu([]navItem{
		{"Albums", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			SetCurrentView(albumsView)
			App.SetFocus(Main)
			return nil
		}, nil)},
		{"Artists", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			SetCurrentView(artistsView)
			App.SetFocus(Main)
			return nil
		}, nil)},
		{"Liked Songs", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			SetCurrentView(likedSongsView)
			App.SetFocus(Main)
			return nil
		}, nil)},
		{"Recently Played", NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			recentlyPlayedView.RefreshState()
			SetCurrentView(recentlyPlayedView)
			App.SetFocus(Main)
			return nil
		}, nil)},
	})
	playlistNav := NewPlaylistNav()

	root.AfterContextClose(func() { App.SetFocus(Main) })

	// Define Actions
	globalActions := map[string]*Action{
		"shuffle": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			if err := spt.Shuffle(); err != nil {
				SendNotification(err.Error())
			}
			return nil
		}, progressBar),
		"repeat": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			if err := spt.Repeat(); err != nil {
				SendNotification(err.Error())
			}
			return nil
		}, progressBar),
		"save_config": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			msg := "Config Saved Successfully!"
			if err := config.WriteConfig(); err != nil {
				msg = fmt.Sprintf("Error Writing Config: %s", err.Error())
			}
			SendNotification(msg)
			return nil
		}, nil),
		"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			CurrentView.OpenEntry()
			return nil
		}, progressBar),
		"focus_search": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			App.SetFocus(searchbar)
			return nil
		}, nil),
		"toggle_playback": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			if err := spt.TogglePlayback(); err != nil {
				SendNotification(err.Error())
			}
			return nil
		}, progressBar),
		"choose_device": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			openDeviceMenu()
			return nil
		}, nil),
		"focus_nav": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			App.SetFocus(navMenu)
			return nil
		}, nil),
		"focus_playlists": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			App.SetFocus(playlistNav)
			return nil
		}, nil),
		"focus_main_view": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			App.SetFocus(Main)
			return nil
		}, nil),
		"open_current_track_album": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			openCurrentAlbum()
			return nil
		}, nil),
		"open_current_track_artist": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			openCurrentArtist()
			return nil
		}, nil),
		"open_current_context": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			if state != nil && state.Item != nil {
				switch state.PlaybackContext.Type {
				case "artist":
					{
						openCurrentArtist()
					}
				case "album":
					{
						openCurrentAlbum()
					}
				case "playlist":
					{
						id, err := spt.UriToID(state.PlaybackContext.URI)
						if err != nil {
							SendNotification("Error switching contexts: " + err.Error())
							return e
						}
						p, err := spt.GetFullPlaylist(id)
						if err != nil {
							SendNotification("Error switching contexts: " + err.Error())
							return e
						}
						playlistView.SetPlaylist(&p.SimplePlaylist)
						SetCurrentView(playlistView)
						App.SetFocus(Main)
					}
				default:
					{
						SendNotification("No Context Found!")
					}
				}
			}
			return nil
		}, nil),
		"next": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			if err := spt.Next(); err != nil {
				SendNotification(err.Error())
				return e
			}
			return nil
		}, progressBar),
		"previous": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			if err := spt.Previous(); err != nil {
				SendNotification(err.Error())
				return e
			}
			return nil
		}, progressBar),
	}

	imageActions := map[string]*Action{
		"increase_image_height": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.ImageWidthExtraY += 1
			return nil
		}, coverArt),
		"increase_image_width": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.ImageWidthExtraX += 1
			return nil
		}, coverArt),
		"decrease_image_height": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.ImageWidthExtraY -= 1
			return nil
		}, coverArt),
		"decrease_image_width": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.ImageWidthExtraX -= 1
			return nil
		}, coverArt),
		"move_image_right": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.AdditionalPaddingX += 1
			return nil
		}, coverArt),
		"move_image_left": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.AdditionalPaddingX -= 1
			return nil
		}, coverArt),
		"move_image_up": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.AdditionalPaddingY -= 1
			return nil
		}, coverArt),
		"move_image_down": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			cfg.AdditionalPaddingY += 1
			return nil
		}, coverArt),
	}

	if cfg.Image == config.ImageShow {
		globalActions = utils.MergeMaps(globalActions, imageActions)
	}

	playlistNav.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"play_entry": NewAction(playlistNav.PlayEntry,
			progressBar),
		"open_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			r, _ := playlistNav.GetSelection()
			playlistView.SetPlaylist(&(*playlistNav.Playlists)[r])
			SetCurrentView(playlistView)
			App.SetFocus(Main)
			return nil
		}, nil),
		"queue_entry": NewAction(playlistNav.QueueEntry, nil),
	}))
	navMenu.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"open_entry": NewAction(navMenu.OpenEntry,
			nil),
	}))
	playlistView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"add_to_playlist": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			playlistView.AddToPlaylist()
			return nil
		}, nil),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			playlistView.QueueEntry()
			return nil
		}, nil),
	}))
	recentlyPlayedView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"add_to_playlist": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			recentlyPlayedView.AddToPlaylist()
			return nil
		}, nil),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			recentlyPlayedView.QueueEntry()
			return nil
		}, nil),
	}))
	topTracksView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			topTracksView.PlayEntry()
			return nil
		}, progressBar),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			topTracksView.QueueEntry()
			return nil
		}, nil),
	}))
	likedSongsView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"add_to_playlist": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			likedSongsView.AddToPlaylist()
			return nil
		}, nil),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			likedSongsView.QueueEntry()
			return nil
		}, nil),
	}))
	searchView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			searchView.PlayEntry()
			return nil
		}, progressBar),
	}))
	artistsView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			artistsView.PlayEntry()
			return nil
		}, nil),
	}))
	artistView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			artistView.PlayEntry()
			return nil
		}, progressBar),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			artistView.QueueEntry()
			return nil
		}, nil),
	}))
	albumsView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"play_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			albumsView.PlayEntry()
			return nil
		}, progressBar),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			albumsView.QueueEntry()
			return nil
		}, nil),
	}))
	albumView.SetActions(utils.MergeMaps(globalActions, map[string]*Action{
		"add_to_playlist": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			albumView.AddToPlaylist()
			return nil
		}, nil),
		"queue_entry": NewAction(func(e *tcell.EventKey) *tcell.EventKey {
			albumView.QueueEntry()
			return nil
		}, nil),
	}))

	// Visual Actions
	albumView.SetVisualActions(map[string]func(start, end int, e *tcell.EventKey) *tcell.EventKey{
		"add_to_playlist": albumView.AddToPlaylistVisual,
		"queue_entry":     albumView.QueueSongsVisual,
	})
	recentlyPlayedView.SetVisualActions(map[string]func(start, end int, e *tcell.EventKey) *tcell.EventKey{
		"add_to_playlist": recentlyPlayedView.AddToPlaylistVisual,
		"queue_entry":     recentlyPlayedView.QueueSongsVisual,
	})
	playlistView.SetVisualActions(map[string]func(start, end int, e *tcell.EventKey) *tcell.EventKey{
		"add_to_playlist": playlistView.AddToPlaylistVisual,
		"queue_entry":     playlistView.QueueSongsVisual,
	})
	likedSongsView.SetVisualActions(map[string]func(start, end int, e *tcell.EventKey) *tcell.EventKey{
		"add_to_playlist": likedSongsView.AddToPlaylistVisual,
		"queue_entry":     likedSongsView.QueueSongsVisual,
	})

	mappings := config.GenerateMappings()

	// Map Actions
	playlistNav.SetMappings(mappings["playlist_nav"])
	playlistNav.SetInputCapture(playlistNav.ExternalInputCapture())
	navMenu.SetMappings(mappings["nav_menu"])
	navMenu.SetInputCapture(navMenu.ExternalInputCapture())
	playlistView.SetMappings(mappings["playlist_view"])
	recentlyPlayedView.SetMappings(mappings["recently_played_view"])
	topTracksView.SetMappings(mappings["top_tracks_view"])
	likedSongsView.SetMappings(mappings["liked_songs_view"])
	albumsView.SetMappings(mappings["albums_view"])
	albumView.SetMappings(mappings["album_view"])
	artistsView.SetMappings(mappings["artists_view"])
	artistView.SetMappings(mappings["artist_view"])
	searchView.SetMappings(mappings["search_view"])

	// Set up UI
	navFlex := tview.NewFlex().SetDirection(tview.FlexRow).
		AddItem(navMenu, 6, 3, false).
		AddItem(playlistNav, 0, 6, false)

	if cfg.Image == config.ImageShow {
		navFlex.AddItem(coverArt, 9, 3, false)
	}

	// mid
	mFlex := tview.NewFlex().
		AddItem(navFlex, 17, 1, false).
		AddItem(Main, 0, 4, false)

	// mid + top
	tFlex := tview.NewFlex().SetDirection(tview.FlexRow).
		AddItem(searchbar, 3, 1, false).
		AddItem(mFlex, 0, 1, false)

	Flex = tview.NewFlex().SetDirection(tview.FlexRow).
		AddItem(tFlex, 0, 8, false).
		AddItem(progressBar, 5, 1, false)

	root.Primitive("Main", Flex)
	App.SetRoot(root, true).SetFocus(Main)

	// Start Routines
	InitNotifier()
	if cfg.Image == config.ImageShow {
		go rectWatcher()
	} else {
		// Start Progress Routine directly
		progressRoutine()
	}

	go func() {
		for {
			if App != nil {
				App.Draw()
				time.Sleep(time.Second)
			}
		}
	}()

	SetCurrentView(topTracksView)
	topTracksView.RefreshState()

	return App
}
