module Brick.Widgets.Calendar.Internal.Actions
  ( moveUp
  , moveDown
  , moveLeft
  , moveRight
  , setMonthBefore
  , setMonthAfter
  , setYearBefore
  , setYearAfter
  ) where

import Data.Time
import Data.Time.Calendar.Month

import Brick.Widgets.Calendar.Internal.Core

moveUp :: CalendarState -> CalendarState
moveUp :: CalendarState -> CalendarState
moveUp =
  (Day -> Day) -> CalendarState -> CalendarState
navigateSelection (Year -> Day -> Day
addDays (-Year
7))

moveDown :: CalendarState -> CalendarState
moveDown :: CalendarState -> CalendarState
moveDown =
  (Day -> Day) -> CalendarState -> CalendarState
navigateSelection (Year -> Day -> Day
addDays Year
7)

moveLeft :: CalendarState -> CalendarState
moveLeft :: CalendarState -> CalendarState
moveLeft =
  (Day -> Day) -> CalendarState -> CalendarState
navigateSelection (Year -> Day -> Day
addDays (-Year
1))

moveRight :: CalendarState -> CalendarState
moveRight :: CalendarState -> CalendarState
moveRight =
  (Day -> Day) -> CalendarState -> CalendarState
navigateSelection (Year -> Day -> Day
addDays Year
1)

setMonthBefore :: CalendarState -> CalendarState
setMonthBefore :: CalendarState -> CalendarState
setMonthBefore =
  (Month -> Month) -> CalendarState -> CalendarState
navigateMonth (Year -> Month -> Month
addMonths (-Year
1))

setMonthAfter :: CalendarState -> CalendarState
setMonthAfter :: CalendarState -> CalendarState
setMonthAfter =
  (Month -> Month) -> CalendarState -> CalendarState
navigateMonth (Year -> Month -> Month
addMonths Year
1)

setYearBefore :: CalendarState -> CalendarState
setYearBefore :: CalendarState -> CalendarState
setYearBefore =
  (Month -> Month) -> CalendarState -> CalendarState
navigateMonth (Year -> Month -> Month
addMonths (-Year
12))

setYearAfter :: CalendarState -> CalendarState
setYearAfter :: CalendarState -> CalendarState
setYearAfter =
  (Month -> Month) -> CalendarState -> CalendarState
navigateMonth (Year -> Month -> Month
addMonths Year
12)

navigateSelection :: (Day -> Day) -> CalendarState -> CalendarState
navigateSelection :: (Day -> Day) -> CalendarState -> CalendarState
navigateSelection Day -> Day
dayTransform CalendarState
s =
  case CalendarState -> Maybe Day
calSelectedDay CalendarState
s of
    Maybe Day
Nothing -> 
      -- When no day is selected, select the first day of the current month
      CalendarState
s { calSelectedDay = Just $ fromGregorian (calYear s) (calMonth s) 1 }
    Just Day
day ->
      let newDay :: Day
newDay = Day -> Day
dayTransform Day
day
          (Year
y, MonthOfYear
m, MonthOfYear
_) = Day -> (Year, MonthOfYear, MonthOfYear)
toGregorian Day
newDay
      in if MonthOfYear
m MonthOfYear -> MonthOfYear -> Bool
forall a. Eq a => a -> a -> Bool
/= CalendarState -> MonthOfYear
calMonth CalendarState
s Bool -> Bool -> Bool
|| Year
y Year -> Year -> Bool
forall a. Eq a => a -> a -> Bool
/= CalendarState -> Year
calYear CalendarState
s
         -- If we've moved to a different month, update the view
         then CalendarState
s { calYear = y, calMonth = m, calSelectedDay = Just newDay }
         -- Otherwise just update the selected day
         else CalendarState
s { calSelectedDay = Just newDay }

navigateMonth :: (Month -> Month) -> CalendarState -> CalendarState
navigateMonth :: (Month -> Month) -> CalendarState -> CalendarState
navigateMonth Month -> Month
monthTransform CalendarState
s = 
  let currentYM :: Month
currentYM = Year -> MonthOfYear -> Month
YearMonth (CalendarState -> Year
calYear CalendarState
s) (CalendarState -> MonthOfYear
calMonth CalendarState
s)
      newYM :: Month
newYM = Month -> Month
monthTransform Month
currentYM
      YearMonth Year
newYear MonthOfYear
newMonth = Month
newYM
      
      -- Keep the same day if possible in the new month
      newDay :: Maybe Day
newDay = case CalendarState -> Maybe Day
calSelectedDay CalendarState
s of
        Maybe Day
Nothing -> Maybe Day
forall a. Maybe a
Nothing
        Just Day
day -> 
          let (Year
_, MonthOfYear
_, MonthOfYear
d) = Day -> (Year, MonthOfYear, MonthOfYear)
toGregorian Day
day
              lastDayInNewMonth :: MonthOfYear
lastDayInNewMonth = Month -> MonthOfYear
forall p. DayPeriod p => p -> MonthOfYear
periodLength Month
newYM
              adjustedDay :: MonthOfYear
adjustedDay = MonthOfYear -> MonthOfYear -> MonthOfYear
forall a. Ord a => a -> a -> a
min MonthOfYear
d MonthOfYear
lastDayInNewMonth
          in Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ Year -> MonthOfYear -> MonthOfYear -> Day
fromGregorian Year
newYear MonthOfYear
newMonth MonthOfYear
adjustedDay
          
  in CalendarState
s { calYear = newYear, calMonth = newMonth, calSelectedDay = newDay }