package tui

import (
  "embed"
  "log"
  "strconv"
  "time"
  "unicode"

  "github.com/gdamore/tcell/v2"
  "github.com/mrusme/superhighway84/cache"
  "github.com/mrusme/superhighway84/config"
  "github.com/mrusme/superhighway84/models"
  "github.com/rivo/tview"
  "go.uber.org/zap"
)

type TUI struct {
  App                        *tview.Application
  Views                      map[string]View
  ActiveView                 string

  Modal                      *tview.Modal
  ModalVisible               bool
  ModalButtons               map[string]ModalButton

  ArticlesDatasource         *[]*models.Article
  ArticlesRoots              *[]*models.Article

  CallbackRefreshArticles    func() (error)
  CallbackSubmitArticle      func(article *models.Article) (error)

  Config                     *config.Config
  Cache                      *cache.Cache
  Logger                     *zap.Logger

  Stats                      map[string]int64

  Version                    string
  VersionLatest              string

  Meta                       map[string]interface{}
}

type View interface {
  GetCanvas() (tview.Primitive)
  GetDefaultFocus() (tview.Primitive)

  Refresh()

  HandleInput(event *tcell.EventKey) (*tcell.EventKey)
}

type ModalButton struct {
  Rune      rune
  Callback  func()
}

func Init(embedfs *embed.FS, cfg *config.Config, cch *cache.Cache, logger *zap.Logger) (*TUI) {
  t := new(TUI)

  tview.Styles = tview.Theme{
    PrimitiveBackgroundColor:    tcell.ColorDefault,
    ContrastBackgroundColor:     tcell.ColorPink,
    MoreContrastBackgroundColor: tcell.ColorTeal,
    BorderColor:                 tcell.ColorWhite,
    TitleColor:                  tcell.ColorWhite,
    GraphicsColor:               tcell.ColorWhite,
    PrimaryTextColor:            tcell.ColorDefault,
    SecondaryTextColor:          tcell.ColorBlue,
    TertiaryTextColor:           tcell.ColorGreen,
    InverseTextColor:            tcell.ColorBlack,
    ContrastSecondaryTextColor:  tcell.ColorDarkCyan,
  }

  t.App = tview.NewApplication()
  t.Config = cfg
  t.Cache = cch
  t.Logger = logger

  t.Stats = make(map[string]int64)

  t.Meta = make(map[string]interface{})

  logoBytes, err := embedfs.ReadFile("superhighway84.jpeg")
  if err != nil {
    log.Panicln(err)
  }

  t.Views = make(map[string]View)
  t.Views["splashscreen"] = t.NewSplashscreen(&logoBytes)
  t.Views["mainscreen"] = t.NewMainscreen()

  t.ModalVisible = false

  t.initInput()

  return t
}

func (t *TUI) getInputEvent(event *tcell.EventKey) (string) {
  var action string
  var exist bool

  if event.Key() == tcell.KeyRune {
    action, exist = t.Config.Shortcuts[strconv.FormatInt(int64(event.Rune()), 10)]
  } else {
    action, exist = t.Config.Shortcuts[strconv.FormatInt(int64(event.Key()), 10)]
  }
  if exist == false {
    return ""
  }

  return action
}

func (t *TUI) initInput() {
  t.App.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
    action := t.getInputEvent(event)

    switch action {
    case "refresh":
      t.RefreshMainscreen()
      t.SetInfo(true)
      t.App.Sync()
      return nil
    case "quit":
      t.App.Stop()
      return nil
    default:
      if t.ModalVisible == true {
        for _, modalButton := range t.ModalButtons {
          if modalButton.Rune == '*' ||
            (event.Key() == tcell.KeyRune &&
              unicode.ToLower(modalButton.Rune) == unicode.ToLower(event.Rune())) {
            modalButton.Callback()
            t.HideModal()
            return nil
          }
        }
        return nil
      } else {
        return t.Views[t.ActiveView].HandleInput(event)
      }
    }
  })
}

func (t *TUI) Launch() {
  go func() {
    time.Sleep(time.Millisecond * 200)
    t.SetView("splashscreen", true)
    t.Refresh()
    t.App.Draw()
  }()

  go func() {
    for {
      t.RefreshMainscreen()
      t.SetInfo(true)
      time.Sleep(time.Second * 60)
    }
  }()

  if err := t.App.Run(); err != nil {
    panic(err)
  }
}

func(t *TUI) SetView(name string, redraw bool) {
  t.ActiveView = name

  t.App.SetRoot(t.Views[t.ActiveView].GetCanvas(), true).
    SetFocus(t.Views[t.ActiveView].GetDefaultFocus())

  if redraw {
    t.App.Draw()
  }
}

func (t *TUI) RefreshMainscreen() {
  if t.ActiveView != "mainscreen" {
    return
  }
  t.RefreshData()
  t.Refresh()
  return
}

func (t *TUI) Refresh() {
  t.Views[t.ActiveView].Refresh()
  return
}

func (t *TUI) RefreshData() {
  if t.CallbackRefreshArticles != nil {
    err := t.CallbackRefreshArticles()
    if err != nil {
      t.ShowErrorModal(err.Error())
    }
  }
}

func(t *TUI) ShowModal(text string, buttons map[string]ModalButton) {
  t.Modal = tview.NewModal().
    SetTextColor(tcell.ColorBlack).
    SetBackgroundColor(tcell.ColorTeal).
    SetButtonBackgroundColor(tcell.ColorHotPink).
    SetButtonTextColor(tcell.ColorWhite).
    SetText(text)
    // SetDoneFunc(func(buttonIndex int, buttonLabel string) {
    //   modalButton := buttons[buttonLabel]
    //   modalButton.Callback()
    //   t.HideModal()
    // })
  var buttonLabels []string
  for buttonLabel := range buttons {
    buttonLabels = append(buttonLabels, buttonLabel)
  }
  t.Modal.AddButtons(buttonLabels)

  t.ModalVisible = true
  t.ModalButtons = buttons
  t.App.SetRoot(t.Modal, false).SetFocus(t.Modal)
}

func(t *TUI) HideModal() {
  t.ModalVisible = false
  t.SetView(t.ActiveView, false)
}

func(t *TUI) ShowErrorModal(text string) {
  t.ShowModal(
    text,
    map[string]ModalButton{
      "(F)uck": {
        Rune: '*',
        Callback: func() {
          return
        },
      },
    })
}

func(t *TUI) ShowHelpModal(text string) {
  t.ShowModal(
    text,
    map[string]ModalButton{
      "Press any key to close": {
        Rune: '*',
        Callback: func() {
          return
        },
      },
    })
}

func (t *TUI) SetInfo(refresh bool) {
  if refresh == true {
    t.Views["mainscreen"].(*Mainscreen).SetInfo(map[string]string{
      "refresh": "[green]◙[-]",
    })
    time.AfterFunc(time.Second * 3, func() {
      t.SetInfo(false)
    })
  } else {
    t.Views["mainscreen"].(*Mainscreen).SetInfo(map[string]string{
      "refresh": "[grey]◙[-]",
    })
  }
}

func (t* TUI) SetStats(peers, rateIn, rateOut, totalIn, totalOut int64) () {
  t.Stats["peers"] = peers
  t.Stats["rate_in"] = rateIn
  t.Stats["rate_out"] = rateOut
  t.Stats["total_in"] = totalIn
  t.Stats["total_out"] = totalOut

  t.Views["mainscreen"].(*Mainscreen).SetStats(t.Stats)
  t.App.Draw()
}

func (t* TUI) SetVersion(version string, versionLatest string) {
  t.Version = version
  t.VersionLatest = versionLatest
  t.Views["mainscreen"].(*Mainscreen).SetVersion(t.Version, t.VersionLatest)
}

