{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module Main where

import           Brick hiding ( Location )
import           Brick.Focus
import           Brick.Panes
import           Brick.Widgets.Border
import           Brick.Widgets.Border.Style
import           Brick.Widgets.Edit
import           Brick.Widgets.List
import           Control.Monad ( guard, when )
import           Control.Monad.IO.Class ( liftIO )
import qualified Data.List as DL
import           Data.Maybe ( catMaybes )
import           Data.Version ( showVersion )
import           Graphics.Vty ( defAttr, withStyle, defaultStyleMask
                              , bold, reverseVideo, dim
                              , black, white, yellow, red )
import qualified Graphics.Vty as Vty
import           Lens.Micro

import           Defs
import           Panes.Location ()
import           Panes.Operations
import           Panes.Projects ()
import           Panes.Summary
import           Panes.FileMgr
import           Paths_brick_panes ( version )

-- import           InitialData
-- import Data.ByteString.Lazy as BS
-- import Data.Aeson ( encode )

type MyWorkState = Panel WName MyWorkEvent MyWorkCore
                   '[ SummaryPane
                    , OperationsPane
                    , Location
                    , Projects
                    , FileMgrPane
                    ]

main :: IO ()
main = defaultMain myworkApp initialState >> return ()
-- main = BS.writeFile "projects.json" (encode $ initial_projects)

myworkApp :: App MyWorkState MyWorkEvent WName
myworkApp = App { appDraw = drawMyWork
                , appChooseCursor = showFirstCursor
                , appHandleEvent = handleMyWorkEvent
                , appStartEvent = return ()
                , appAttrMap = const myattrs
                }

myattrs :: AttrMap
myattrs = attrMap defAttr
          [
            (editAttr, white `on` black)
          , (editFocusedAttr, yellow `on` black)

          , (listAttr, defAttr `withStyle` defaultStyleMask)
          , (listSelectedAttr, defAttr `withStyle` bold)
          , (listSelectedFocusedAttr, defAttr `withStyle` reverseVideo)

          , (attrName "disabled", defAttr `withStyle` dim)
          , (attrName "Selected", black `on` yellow)
          , (attrName "Error", fg red)
          ]

initialState :: MyWorkState
initialState = focusRingUpdate myWorkFocusL
               $ addToPanel Never
               $ addToPanel Never
               $ addToPanel WhenFocused
               $ addToPanel WhenFocused
               $ addToPanel WhenFocusedModal
               $ basePanel initMyWorkCore

drawMyWork :: MyWorkState -> [Widget WName]
drawMyWork mws =
  let mainPanes =
        [
          borderWithLabel  (str $ " mywork " <> showVersion version <> " ")
          $ vBox $ catMaybes
          [
            panelDraw @SummaryPane mws
          , Just hBorder
          , Just $ hBox $ catMaybes
            [ hLimitPercent 25
              <$> panelDraw @Projects mws
            , Just vBorder
            , panelDraw @Location mws
            ]
          , Just hBorder
          , panelDraw @OperationsPane mws
          ]
        ]
      allPanes = catMaybes [ panelDraw @FileMgrPane mws
                           ]
                 <> mainPanes
      disableLower = \case
        (m:ls) -> m : (withDefAttr (attrName "disabled") <$> ls)
        o -> o
  in joinBorders . withBorderStyle unicode <$> disableLower allPanes


handleMyWorkEvent :: BrickEvent WName MyWorkEvent -> EventM WName MyWorkState ()
handleMyWorkEvent = \case
    AppEvent _ -> return () -- this app does not use these
    -- Application global actions
    --   * CTRL-q quits
    --   * CTRL-l refreshes vty
    --   * ESC dismisses any modal window
    VtyEvent (Vty.EvKey (Vty.KChar 'q') [Vty.MCtrl])  -> halt
    VtyEvent (Vty.EvKey (Vty.KChar 'l') [Vty.MCtrl])  -> do
      vty <- getVtyHandle
      liftIO $ Vty.refresh vty
    VtyEvent (Vty.EvKey (Vty.KFun 1) []) -> do
      fmgr <- liftIO initFileMgr
      modify ((focusRingUpdate myWorkFocusL) . (onPane @FileMgrPane .~ fmgr))
    -- Otherwise, allow the Panes in the Panel to handle the event
    ev -> do proj0 <- gets selectedProject
             s <- get
             (_,s') <- handleFocusAndPanelEvents myWorkFocusL s ev
             put s'
             (new,prjs) <- gets getProjects
             let mprj st = do pnm <- selectedProject st
                              guard (Just pnm /= proj0)
                              DL.find ((== pnm) . name) (projects prjs)
             when new $
               modify $ \st -> st
                               & focusRingUpdate myWorkFocusL
                               & onPane @Projects %~ updatePane prjs
                               & onPane @FileMgrPane %~ updatePane False
             modify $ \st ->
                        case mprj st of
                          Just p -> st & onPane @Location %~ updatePane p
                          _ -> st
             modify $ focusRingUpdate myWorkFocusL

myWorkFocusL :: Lens' MyWorkState (FocusRing WName)
myWorkFocusL = onBaseState . coreWorkFocusL