-- | Module    : Hum.Utils
-- Copyright   : (c) Itai Y. Efrat 2020-2021
-- License     : GPLv2-or-later (see LICENSE)
-- Maintainer  : Itai Y. Efrat <itai3397@gmail.com>
--


module Hum.Utils where
import           Hum.Types
import           Hum.Rebuild
import           Brick.Types
import           Brick.Widgets.List
import qualified Data.Vector                   as V
import qualified Data.Text                     as T
import qualified Data.ByteString               as BS
import           Network.MPD                    ( withMPD )
import qualified Network.MPD                   as MPD
import qualified Data.Map.Strict               as Map
import           Text.Printf                    ( printf )
import           Control.Lens
import qualified Witherable         as W

-- | A backwards function composition operator that I love with my whole heart.
infixl 8  ?
{-# INLINE (?) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(?)    :: (a -> b) -> (b -> c) -> a -> c
? :: (a -> b) -> (b -> c) -> a -> c
(?) a -> b
f b -> c
g = \a
x -> b -> c
g (a -> b
f a
x)

-- | Get comma seperated metedata from tag.
meta :: Text -> MPD.Metadata -> MPD.Song -> Text
meta :: Text -> Metadata -> Song -> Text
meta Text
notFound Metadata
tag Song
song = Text -> ([Text] -> Text) -> Maybe [Text] -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
  Text
notFound
  (Text -> [Text] -> Text
T.intercalate Text
",")
  (Value -> Text
forall a. ToString a => a -> Text
MPD.toText (Value -> Text) -> Maybe [Value] -> Maybe [Text]
forall (f :: * -> *) (g :: * -> *) a b.
(Functor f, Functor g) =>
(a -> b) -> f (g a) -> f (g b)
<<$>> Metadata -> Map Metadata [Value] -> Maybe [Value]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Metadata
tag (Song -> Map Metadata [Value]
MPD.sgTags Song
song))

-- | Like 'meta', but returns a Maybe for future use.
mmeta :: MPD.Metadata -> MPD.Song -> Maybe Text
mmeta :: Metadata -> Song -> Maybe Text
mmeta Metadata
tag Song
song =
  Text -> [Text] -> Text
T.intercalate Text
"," ([Text] -> Text) -> Maybe [Text] -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Value -> Text
forall a. ToString a => a -> Text
MPD.toText (Value -> Text) -> Maybe [Value] -> Maybe [Text]
forall (f :: * -> *) (g :: * -> *) a b.
(Functor f, Functor g) =>
(a -> b) -> f (g a) -> f (g b)
<<$>> Metadata -> Map Metadata [Value] -> Maybe [Value]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Metadata
tag (Song -> Map Metadata [Value]
MPD.sgTags Song
song))

-- | Formats seconds to %M:%S.
secondsToTime :: Integer -> Text
secondsToTime :: Integer -> Text
secondsToTime Integer
sec =
  let (Integer
minutes, Integer
seconds) = Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
divMod Integer
sec Integer
60
  in  String -> Text
forall a. ToText a => a -> Text
toText (String -> Integer -> Integer -> String
forall r. PrintfType r => String -> r
printf String
"%d:%02d" Integer
minutes Integer
seconds :: String)

-- | Deletes highlighted songs in list from queue, does not rebuild queue.
deleteHighlightedfromQ :: MPD.MonadMPD m => SongList -> m ()
deleteHighlightedfromQ :: SongList -> m ()
deleteHighlightedfromQ SongList
ls =
  let (SongList
hls :: SongList) = ((Song, Bool) -> Bool) -> SongList -> SongList
forall (f :: * -> *) a. Filterable f => (a -> Bool) -> f a -> f a
W.filter (Song, Bool) -> Bool
forall a b. (a, b) -> b
snd SongList
ls
  in  SongList -> ((Song, Bool) -> m ()) -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ SongList
hls (\(Song, Bool)
s -> Maybe Id -> (Id -> m ()) -> m ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust (Song -> Maybe Id
MPD.sgId (Song -> Maybe Id)
-> ((Song, Bool) -> Song) -> (Song, Bool) -> Maybe Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Song
forall a b. (a, b) -> a
fst ((Song, Bool) -> Maybe Id) -> (Song, Bool) -> Maybe Id
forall a b. (a -> b) -> a -> b
$ (Song, Bool)
s) Id -> m ()
forall (m :: * -> *). MonadMPD m => Id -> m ()
MPD.deleteId)
        m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe Id -> (Id -> m ()) -> m ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust
             ((Song -> Maybe Id
MPD.sgId (Song -> Maybe Id)
-> ((Int, (Song, Bool)) -> Song) -> (Int, (Song, Bool)) -> Maybe Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Song
forall a b. (a, b) -> a
fst ((Song, Bool) -> Song)
-> ((Int, (Song, Bool)) -> (Song, Bool))
-> (Int, (Song, Bool))
-> Song
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, (Song, Bool)) -> (Song, Bool)
forall a b. (a, b) -> b
snd) ((Int, (Song, Bool)) -> Maybe Id)
-> Maybe (Int, (Song, Bool)) -> Maybe Id
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< SongList -> Maybe (Int, (Song, Bool))
forall (t :: * -> *) n e.
(Splittable t, Foldable t) =>
GenericList n t e -> Maybe (Int, e)
listSelectedElement SongList
ls)
             Id -> m ()
forall (m :: * -> *). MonadMPD m => Id -> m ()
MPD.deleteId

-- | Deletes list of songs from queue in MPD, does not rebuild queue.
deleteBulkfromQ :: MPD.MonadMPD m => SongList -> m ()
deleteBulkfromQ :: SongList -> m ()
deleteBulkfromQ SongList
ls = SongList -> ((Song, Bool) -> m ()) -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ SongList
ls (\(Song, Bool)
s -> Maybe Id -> (Id -> m ()) -> m ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust (Song -> Maybe Id
MPD.sgId (Song -> Maybe Id)
-> ((Song, Bool) -> Song) -> (Song, Bool) -> Maybe Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Song
forall a b. (a, b) -> a
fst ((Song, Bool) -> Maybe Id) -> (Song, Bool) -> Maybe Id
forall a b. (a -> b) -> a -> b
$ (Song, Bool)
s) Id -> m ()
forall (m :: * -> *). MonadMPD m => Id -> m ()
MPD.deleteId)

-- | Adds songs to queue under the selected item in it in MPD, does not rebuild queue.
pasteSongstoQ :: MPD.MonadMPD m => SongList -> SongList -> m () -- TODO refactor to act on HumState
pasteSongstoQ :: SongList -> SongList -> m ()
pasteSongstoQ SongList
clip SongList
ls =
  let pos :: Maybe Int
pos         = SongList -> Maybe Int
forall n (t :: * -> *) e. GenericList n t e -> Maybe Int
listSelected SongList
ls
      indexedClip :: Vector (Int, Path)
indexedClip = Vector Path -> Vector (Int, Path)
forall a. Vector a -> Vector (Int, a)
V.indexed (Vector Path -> Vector (Int, Path))
-> Vector Path -> Vector (Int, Path)
forall a b. (a -> b) -> a -> b
$ Song -> Path
MPD.sgFilePath (Song -> Path) -> ((Song, Bool) -> Song) -> (Song, Bool) -> Path
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Song
forall a b. (a, b) -> a
fst ((Song, Bool) -> Path) -> Vector (Song, Bool) -> Vector Path
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SongList -> Vector (Song, Bool)
forall n (t :: * -> *) e. GenericList n t e -> t e
listElements SongList
clip
  in  Vector (Int, Path) -> ((Int, Path) -> m Id) -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Vector (Int, Path)
indexedClip (\(Int
n, Path
song) -> Path -> Maybe Int -> m Id
forall (m :: * -> *). MonadMPD m => Path -> Maybe Int -> m Id
MPD.addId Path
song (Maybe Int -> m Id) -> Maybe Int -> m Id
forall a b. (a -> b) -> a -> b
$ (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)) (Int -> Int) -> Maybe Int -> Maybe Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Int
pos)

-- | Produce list of highligted elements (and selected element) in input list.
getHighlighted
  :: (W.Filterable t, Traversable t)
  => GenericList n t (e, Highlight)
  -> GenericList n t (e, Highlight)
getHighlighted :: GenericList n t (e, Bool) -> GenericList n t (e, Bool)
getHighlighted GenericList n t (e, Bool)
ls = GenericList n t (e, Bool)
ls GenericList n t (e, Bool)
-> (GenericList n t (e, Bool) -> GenericList n t (e, Bool))
-> GenericList n t (e, Bool)
forall a b. a -> (a -> b) -> b
& GenericList n t (e, Bool) -> GenericList n t (e, Bool)
forall (t :: * -> *) n e.
Traversable t =>
GenericList n t (e, Bool) -> GenericList n t (e, Bool)
listHighlightSelected (GenericList n t (e, Bool) -> GenericList n t (e, Bool))
-> (GenericList n t (e, Bool) -> GenericList n t (e, Bool))
-> GenericList n t (e, Bool)
-> GenericList n t (e, Bool)
forall a b c. (a -> b) -> (b -> c) -> a -> c
? ((e, Bool) -> Bool)
-> GenericList n t (e, Bool) -> GenericList n t (e, Bool)
forall (f :: * -> *) a. Filterable f => (a -> Bool) -> f a -> f a
W.filter (e, Bool) -> Bool
forall a b. (a, b) -> b
snd (GenericList n t (e, Bool) -> GenericList n t (e, Bool))
-> (GenericList n t (e, Bool) -> GenericList n t (e, Bool))
-> GenericList n t (e, Bool)
-> GenericList n t (e, Bool)
forall a b c. (a -> b) -> (b -> c) -> a -> c
? GenericList n t (e, Bool) -> GenericList n t (e, Bool)
forall (t :: * -> *) n e.
Traversable t =>
GenericList n t (e, Bool) -> GenericList n t (e, Bool)
listUnhighlightAll

-- | Paste one list into another under the selected item.
listPaste
  :: (Splittable t, Semigroup (t e))
  => GenericList n t e -- ^ List pasted into
  -> GenericList n t e -- ^ Pasted list
  -> GenericList n t e
listPaste :: GenericList n t e -> GenericList n t e -> GenericList n t e
listPaste GenericList n t e
paste GenericList n t e
ls =
  let es :: t e
es         = GenericList n t e -> t e
forall n (t :: * -> *) e. GenericList n t e -> t e
listElements GenericList n t e
ls
      pos :: Int
pos        = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
0 (GenericList n t e -> Maybe Int
forall n (t :: * -> *) e. GenericList n t e -> Maybe Int
listSelected GenericList n t e
ls)
      (t e
es1, t e
es2) = Int -> t e -> (t e, t e)
forall (t :: * -> *) a. Splittable t => Int -> t a -> (t a, t a)
Brick.Widgets.List.splitAt (Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) t e
es
  in  GenericList n t e
ls { listElements :: t e
listElements = t e
es1 t e -> t e -> t e
forall a. Semigroup a => a -> a -> a
<> GenericList n t e -> t e
forall n (t :: * -> *) e. GenericList n t e -> t e
listElements GenericList n t e
paste t e -> t e -> t e
forall a. Semigroup a => a -> a -> a
<> t e
es2 }

-- | Delete highlighted element (and selected element) from list.
deleteHighlighted
  :: HumState
  -> Lens' HumState SongList -- ^ Lens that leads to list
  -> HumState
deleteHighlighted :: HumState -> Lens' HumState SongList -> HumState
deleteHighlighted HumState
st Lens' HumState SongList
lns =
  HumState
st HumState -> (HumState -> HumState) -> HumState
forall a b. a -> (a -> b) -> b
& (Clipboard -> Identity Clipboard) -> HumState -> Identity HumState
Lens' HumState Clipboard
clipboardL ((Clipboard -> Identity Clipboard)
 -> HumState -> Identity HumState)
-> ((SongList -> Identity SongList)
    -> Clipboard -> Identity Clipboard)
-> (SongList -> Identity SongList)
-> HumState
-> Identity HumState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SongList -> Identity SongList) -> Clipboard -> Identity Clipboard
Lens' Clipboard SongList
clSongsL ((SongList -> Identity SongList) -> HumState -> Identity HumState)
-> SongList -> HumState -> HumState
forall s t a b. ASetter s t a b -> b -> s -> t
.~ (HumState
st HumState -> Getting SongList HumState SongList -> SongList
forall s a. s -> Getting a s a -> a
^. Getting SongList HumState SongList
Lens' HumState SongList
lns SongList -> (SongList -> SongList) -> SongList
forall a b. a -> (a -> b) -> b
& SongList -> SongList
forall (t :: * -> *) n e.
(Filterable t, Traversable t) =>
GenericList n t (e, Bool) -> GenericList n t (e, Bool)
getHighlighted)
     HumState -> (HumState -> HumState) -> HumState
forall a b. a -> (a -> b) -> b
& (SongList -> Identity SongList) -> HumState -> Identity HumState
Lens' HumState SongList
lns ((SongList -> Identity SongList) -> HumState -> Identity HumState)
-> (SongList -> SongList) -> HumState -> HumState
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ SongList -> SongList
forall (t :: * -> *) n e.
Traversable t =>
GenericList n t (e, Bool) -> GenericList n t (e, Bool)
listHighlightSelected (SongList -> SongList)
-> (SongList -> SongList) -> SongList -> SongList
forall a b c. (a -> b) -> (b -> c) -> a -> c
?  ((Song, Bool) -> Bool) -> SongList -> SongList
forall (f :: * -> *) a. Filterable f => (a -> Bool) -> f a -> f a
W.filter (Bool -> Bool
not (Bool -> Bool) -> ((Song, Bool) -> Bool) -> (Song, Bool) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Bool
forall a b. (a, b) -> b
snd)

-- | Copy highlighted element (and selected element) from list to 'Clipboard'.
yankHighlighted
  :: HumState
  -> Lens' HumState SongList -- ^ Lens that leads to list
  -> HumState
yankHighlighted :: HumState -> Lens' HumState SongList -> HumState
yankHighlighted HumState
st Lens' HumState SongList
lns =
  HumState
st HumState -> (HumState -> HumState) -> HumState
forall a b. a -> (a -> b) -> b
& (Clipboard -> Identity Clipboard) -> HumState -> Identity HumState
Lens' HumState Clipboard
clipboardL ((Clipboard -> Identity Clipboard)
 -> HumState -> Identity HumState)
-> ((SongList -> Identity SongList)
    -> Clipboard -> Identity Clipboard)
-> (SongList -> Identity SongList)
-> HumState
-> Identity HumState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SongList -> Identity SongList) -> Clipboard -> Identity Clipboard
Lens' Clipboard SongList
clSongsL ((SongList -> Identity SongList) -> HumState -> Identity HumState)
-> SongList -> HumState -> HumState
forall s t a b. ASetter s t a b -> b -> s -> t
.~ (HumState
st HumState -> Getting SongList HumState SongList -> SongList
forall s a. s -> Getting a s a -> a
^. Getting SongList HumState SongList
Lens' HumState SongList
lns SongList -> (SongList -> SongList) -> SongList
forall a b. a -> (a -> b) -> b
& SongList -> SongList
forall (t :: * -> *) n e.
(Filterable t, Traversable t) =>
GenericList n t (e, Bool) -> GenericList n t (e, Bool)
getHighlighted)

-- | Toggle selected items highlight status.
listToggleHighlight :: Traversable t => GenericList n t (e,Highlight) -> GenericList n t (e,Highlight)
listToggleHighlight :: GenericList n t (e, Bool) -> GenericList n t (e, Bool)
listToggleHighlight = ((e, Bool) -> (e, Bool))
-> GenericList n t (e, Bool) -> GenericList n t (e, Bool)
forall (t :: * -> *) e n.
Traversable t =>
(e -> e) -> GenericList n t e -> GenericList n t e
listModify ((Bool -> Bool) -> (e, Bool) -> (e, Bool)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second Bool -> Bool
not)


-- | Highlight selcted item.
listHighlightSelected :: Traversable t => GenericList n t (e,Highlight) -> GenericList n t (e,Highlight)
listHighlightSelected :: GenericList n t (e, Bool) -> GenericList n t (e, Bool)
listHighlightSelected = ((e, Bool) -> (e, Bool))
-> GenericList n t (e, Bool) -> GenericList n t (e, Bool)
forall (t :: * -> *) e n.
Traversable t =>
(e -> e) -> GenericList n t e -> GenericList n t e
listModify ((Bool -> Bool) -> (e, Bool) -> (e, Bool)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second (Bool -> Bool -> Bool
forall a b. a -> b -> a
const Bool
True))

-- | Unhighlight selcted item.
listUnhighlightAll :: Traversable t => GenericList n t (e,Highlight) -> GenericList n t (e,Highlight)
listUnhighlightAll :: GenericList n t (e, Bool) -> GenericList n t (e, Bool)
listUnhighlightAll = ((e, Bool) -> (e, Bool))
-> GenericList n t (e, Bool) -> GenericList n t (e, Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Bool -> Bool) -> (e, Bool) -> (e, Bool)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second ((Bool -> Bool) -> (e, Bool) -> (e, Bool))
-> (Bool -> Bool) -> (e, Bool) -> (e, Bool)
forall a b. (a -> b) -> a -> b
$ Bool -> Bool -> Bool
forall a b. a -> b -> a
const Bool
False)

-- | Save list of songs to a stored playlist. If exists does nothing.
saveListToPl :: MPD.MonadMPD m =>
     SongList
  -> Text -- ^ Name of playlist to save to
  -> m () -- TODO use unusedPlName
saveListToPl :: SongList -> Text -> m ()
saveListToPl SongList
ls Text
name =
  let songpaths :: Vector Path
songpaths = Song -> Path
MPD.sgFilePath (Song -> Path) -> ((Song, Bool) -> Song) -> (Song, Bool) -> Path
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Song
forall a b. (a, b) -> a
fst ((Song, Bool) -> Path) -> Vector (Song, Bool) -> Vector Path
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SongList -> Vector (Song, Bool)
forall n (t :: * -> *) e. GenericList n t e -> t e
listElements SongList
ls
      name' :: PlaylistName
name'     = String -> PlaylistName
forall a. IsString a => String -> a
fromString (String -> PlaylistName)
-> (Text -> String) -> Text -> PlaylistName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> PlaylistName) -> Text -> PlaylistName
forall a b. (a -> b) -> a -> b
$ Text
name
  in  Vector Path -> (Path -> m ()) -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Vector Path
songpaths (PlaylistName -> Path -> m ()
forall (m :: * -> *). MonadMPD m => PlaylistName -> Path -> m ()
MPD.playlistAdd PlaylistName
name')

-- | Overwrite stored playlist with new song list.
overwriteListToPl :: MPD.MonadMPD m => SongList -> Text -> m ()
overwriteListToPl :: SongList -> Text -> m ()
overwriteListToPl SongList
ls Text
name =
  let songpaths :: Vector Path
songpaths = Song -> Path
MPD.sgFilePath (Song -> Path) -> ((Song, Bool) -> Song) -> (Song, Bool) -> Path
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Song, Bool) -> Song
forall a b. (a, b) -> a
fst ((Song, Bool) -> Path) -> Vector (Song, Bool) -> Vector Path
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SongList -> Vector (Song, Bool)
forall n (t :: * -> *) e. GenericList n t e -> t e
listElements SongList
ls
      name' :: PlaylistName
name'     = String -> PlaylistName
forall a. IsString a => String -> a
fromString (String -> PlaylistName)
-> (Text -> String) -> Text -> PlaylistName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> PlaylistName) -> Text -> PlaylistName
forall a b. (a -> b) -> a -> b
$ Text
name
  in PlaylistName -> m ()
forall (m :: * -> *). MonadMPD m => PlaylistName -> m ()
MPD.playlistClear PlaylistName
name' m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
     Vector Path -> (Path -> m ()) -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Vector Path
songpaths (PlaylistName -> Path -> m ()
forall (m :: * -> *). MonadMPD m => PlaylistName -> Path -> m ()
MPD.playlistAdd PlaylistName
name')

-- | Save edited playlist in Playlist view to disk.
saveEditedPl :: Bool -> HumState -> EventM n HumState
saveEditedPl :: Bool -> HumState -> EventM n HumState
saveEditedPl Bool
bl HumState
st = if Bool
bl
  then do
    let plSongs :: SongList
plSongs = HumState
st HumState -> Getting SongList HumState SongList -> SongList
forall s a. s -> Getting a s a -> a
^. (PlaylistsState -> Const SongList PlaylistsState)
-> HumState -> Const SongList HumState
Lens' HumState PlaylistsState
playlistsL ((PlaylistsState -> Const SongList PlaylistsState)
 -> HumState -> Const SongList HumState)
-> ((SongList -> Const SongList SongList)
    -> PlaylistsState -> Const SongList PlaylistsState)
-> Getting SongList HumState SongList
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SongList -> Const SongList SongList)
-> PlaylistsState -> Const SongList PlaylistsState
Lens' PlaylistsState SongList
plSongsL
    let plName :: Text
plName =  HumState
st HumState
-> Getting
     (List Name PlaylistName) HumState (List Name PlaylistName)
-> List Name PlaylistName
forall s a. s -> Getting a s a -> a
^. (PlaylistsState -> Const (List Name PlaylistName) PlaylistsState)
-> HumState -> Const (List Name PlaylistName) HumState
Lens' HumState PlaylistsState
playlistsL ((PlaylistsState -> Const (List Name PlaylistName) PlaylistsState)
 -> HumState -> Const (List Name PlaylistName) HumState)
-> ((List Name PlaylistName
     -> Const (List Name PlaylistName) (List Name PlaylistName))
    -> PlaylistsState -> Const (List Name PlaylistName) PlaylistsState)
-> Getting
     (List Name PlaylistName) HumState (List Name PlaylistName)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (List Name PlaylistName
 -> Const (List Name PlaylistName) (List Name PlaylistName))
-> PlaylistsState -> Const (List Name PlaylistName) PlaylistsState
Lens' PlaylistsState (List Name PlaylistName)
plListL List Name PlaylistName -> (List Name PlaylistName -> Text) -> Text
forall a b. a -> (a -> b) -> b
& List Name PlaylistName -> Maybe (Int, PlaylistName)
forall (t :: * -> *) n e.
(Splittable t, Foldable t) =>
GenericList n t e -> Maybe (Int, e)
listSelectedElement (List Name PlaylistName -> Maybe (Int, PlaylistName))
-> (Maybe (Int, PlaylistName) -> PlaylistName)
-> List Name PlaylistName
-> PlaylistName
forall a b c. (a -> b) -> (b -> c) -> a -> c
? PlaylistName
-> ((Int, PlaylistName) -> PlaylistName)
-> Maybe (Int, PlaylistName)
-> PlaylistName
forall b a. b -> (a -> b) -> Maybe a -> b
maybe PlaylistName
"unnamed" (Int, PlaylistName) -> PlaylistName
forall a b. (a, b) -> b
snd (List Name PlaylistName -> PlaylistName)
-> (PlaylistName -> Text) -> List Name PlaylistName -> Text
forall a b c. (a -> b) -> (b -> c) -> a -> c
? PlaylistName -> Text
forall a. ToString a => a -> Text
MPD.toText
    Response ()
_ <- IO (Response ()) -> EventM n (Response ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Response ()) -> EventM n (Response ()))
-> (MPD () -> IO (Response ())) -> MPD () -> EventM n (Response ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MPD () -> IO (Response ())
forall a. MPD a -> IO (Response a)
withMPD (MPD () -> EventM n (Response ()))
-> MPD () -> EventM n (Response ())
forall a b. (a -> b) -> a -> b
$ SongList -> Text -> MPD ()
forall (m :: * -> *). MonadMPD m => SongList -> Text -> m ()
overwriteListToPl SongList
plSongs Text
plName
    HumState -> EventM n HumState
forall (m :: * -> *). MonadIO m => HumState -> m HumState
reloadPlList HumState
st
  else HumState -> EventM n HumState
forall (m :: * -> *). MonadIO m => HumState -> m HumState
reloadPlList HumState
st

-- | Deletes selected playlist in Playlist view from disk.
deleteSelectedPl :: Bool -> HumState -> EventM n HumState
deleteSelectedPl :: Bool -> HumState -> EventM n HumState
deleteSelectedPl Bool
bl HumState
st = if Bool
bl
  then do
    let plName :: Maybe PlaylistName
plName = HumState
st HumState
-> Getting
     (List Name PlaylistName) HumState (List Name PlaylistName)
-> List Name PlaylistName
forall s a. s -> Getting a s a -> a
^. (PlaylistsState -> Const (List Name PlaylistName) PlaylistsState)
-> HumState -> Const (List Name PlaylistName) HumState
Lens' HumState PlaylistsState
playlistsL ((PlaylistsState -> Const (List Name PlaylistName) PlaylistsState)
 -> HumState -> Const (List Name PlaylistName) HumState)
-> ((List Name PlaylistName
     -> Const (List Name PlaylistName) (List Name PlaylistName))
    -> PlaylistsState -> Const (List Name PlaylistName) PlaylistsState)
-> Getting
     (List Name PlaylistName) HumState (List Name PlaylistName)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (List Name PlaylistName
 -> Const (List Name PlaylistName) (List Name PlaylistName))
-> PlaylistsState -> Const (List Name PlaylistName) PlaylistsState
Lens' PlaylistsState (List Name PlaylistName)
plListL List Name PlaylistName
-> (List Name PlaylistName -> Maybe (Int, PlaylistName))
-> Maybe (Int, PlaylistName)
forall a b. a -> (a -> b) -> b
& List Name PlaylistName -> Maybe (Int, PlaylistName)
forall (t :: * -> *) n e.
(Splittable t, Foldable t) =>
GenericList n t e -> Maybe (Int, e)
listSelectedElement Maybe (Int, PlaylistName)
-> ((Int, PlaylistName) -> PlaylistName) -> Maybe PlaylistName
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (Int, PlaylistName) -> PlaylistName
forall a b. (a, b) -> b
snd
    Response (Maybe ())
_ <- IO (Response (Maybe ())) -> EventM n (Response (Maybe ()))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Response (Maybe ())) -> EventM n (Response (Maybe ())))
-> (MPD (Maybe ()) -> IO (Response (Maybe ())))
-> MPD (Maybe ())
-> EventM n (Response (Maybe ()))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MPD (Maybe ()) -> IO (Response (Maybe ()))
forall a. MPD a -> IO (Response a)
withMPD (MPD (Maybe ()) -> EventM n (Response (Maybe ())))
-> MPD (Maybe ()) -> EventM n (Response (Maybe ()))
forall a b. (a -> b) -> a -> b
$ (PlaylistName -> MPD ()) -> Maybe PlaylistName -> MPD (Maybe ())
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse PlaylistName -> MPD ()
forall (m :: * -> *). MonadMPD m => PlaylistName -> m ()
MPD.rm Maybe PlaylistName
plName
    HumState -> EventM n HumState
forall (m :: * -> *). MonadIO m => HumState -> m HumState
rebuildPl HumState
st
 else HumState -> EventM n HumState
forall (f :: * -> *) a. Applicative f => a -> f a
pure HumState
st

-- | Appends smallest number possible to playlist name for it to not be taken.
-- Does nothing if name is untaken.
unusedPlName :: MPD.PlaylistName -> IO MPD.PlaylistName
unusedPlName :: PlaylistName -> IO PlaylistName
unusedPlName PlaylistName
prefix = do
  [PlaylistName]
plNames <- [PlaylistName] -> Either MPDError [PlaylistName] -> [PlaylistName]
forall b a. b -> Either a b -> b
fromRight [] (Either MPDError [PlaylistName] -> [PlaylistName])
-> IO (Either MPDError [PlaylistName]) -> IO [PlaylistName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (IO (Either MPDError [PlaylistName])
-> IO (Either MPDError [PlaylistName])
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either MPDError [PlaylistName])
 -> IO (Either MPDError [PlaylistName]))
-> (MPD [PlaylistName] -> IO (Either MPDError [PlaylistName]))
-> MPD [PlaylistName]
-> IO (Either MPDError [PlaylistName])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MPD [PlaylistName] -> IO (Either MPDError [PlaylistName])
forall a. MPD a -> IO (Response a)
withMPD (MPD [PlaylistName] -> IO (Either MPDError [PlaylistName]))
-> MPD [PlaylistName] -> IO (Either MPDError [PlaylistName])
forall a b. (a -> b) -> a -> b
$ MPD [PlaylistName]
forall (m :: * -> *). MonadMPD m => m [PlaylistName]
MPD.listPlaylists)
  let newPlName :: Maybe PlaylistName
newPlName = (NonEmpty PlaylistName -> PlaylistName)
-> [PlaylistName] -> Maybe PlaylistName
forall a b. (NonEmpty a -> b) -> [a] -> Maybe b
viaNonEmpty NonEmpty PlaylistName -> PlaylistName
forall (f :: * -> *) a. IsNonEmpty f a a "head" => f a -> a
head ([PlaylistName] -> Maybe PlaylistName)
-> [PlaylistName] -> Maybe PlaylistName
forall a b. (a -> b) -> a -> b
$ (PlaylistName -> Bool) -> [PlaylistName] -> [PlaylistName]
forall a. (a -> Bool) -> [a] -> [a]
filter (PlaylistName -> [PlaylistName] -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`notElem` [PlaylistName]
plNames) (PlaylistName
prefixPlaylistName -> [PlaylistName] -> [PlaylistName]
forall a. a -> [a] -> [a]
:(PlaylistName -> PlaylistName -> PlaylistName
append' PlaylistName
prefix (PlaylistName -> PlaylistName)
-> (Int -> PlaylistName) -> Int -> PlaylistName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> PlaylistName
forall b a. (Show a, IsString b) => a -> b
show (Int -> PlaylistName) -> [Int] -> [PlaylistName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
2::Int ..]))
  PlaylistName -> IO PlaylistName
forall (f :: * -> *) a. Applicative f => a -> f a
pure (PlaylistName -> Maybe PlaylistName -> PlaylistName
forall a. a -> Maybe a -> a
fromMaybe PlaylistName
"unnamed" Maybe PlaylistName
newPlName) -- HACK
  where
    append' :: PlaylistName -> PlaylistName -> PlaylistName
append' (MPD.PlaylistName ByteString
x) (MPD.PlaylistName ByteString
y) = ByteString -> PlaylistName
MPD.PlaylistName (ByteString -> ByteString -> ByteString
BS.append ByteString
x ByteString
y)

-- | Duplicates stored playlist on disk (with nonconflicting name).
duplicatePlaylist :: MPD.PlaylistName -> HumState -> EventM n HumState -- HACK
duplicatePlaylist :: PlaylistName -> HumState -> EventM n HumState
duplicatePlaylist PlaylistName
pl HumState
st = do
  Vector Song
songs <- [Song] -> Vector Song
forall a. [a] -> Vector a
V.fromList ([Song] -> Vector Song)
-> (Either MPDError [Song] -> [Song])
-> Either MPDError [Song]
-> Vector Song
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Song] -> Either MPDError [Song] -> [Song]
forall b a. b -> Either a b -> b
fromRight [] (Either MPDError [Song] -> Vector Song)
-> EventM n (Either MPDError [Song]) -> EventM n (Vector Song)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (IO (Either MPDError [Song]) -> EventM n (Either MPDError [Song])
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either MPDError [Song]) -> EventM n (Either MPDError [Song]))
-> (MPD [Song] -> IO (Either MPDError [Song]))
-> MPD [Song]
-> EventM n (Either MPDError [Song])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MPD [Song] -> IO (Either MPDError [Song])
forall a. MPD a -> IO (Response a)
withMPD (MPD [Song] -> EventM n (Either MPDError [Song]))
-> MPD [Song] -> EventM n (Either MPDError [Song])
forall a b. (a -> b) -> a -> b
$ PlaylistName -> MPD [Song]
forall (m :: * -> *). MonadMPD m => PlaylistName -> m [Song]
MPD.listPlaylistInfo PlaylistName
pl)
  PlaylistName
newPlName <- IO PlaylistName -> EventM n PlaylistName
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO PlaylistName -> EventM n PlaylistName)
-> IO PlaylistName -> EventM n PlaylistName
forall a b. (a -> b) -> a -> b
$ PlaylistName -> IO PlaylistName
unusedPlName PlaylistName
pl
  HumState
_ <- String -> Vector Song -> HumState -> EventM n HumState
forall n. String -> Vector Song -> HumState -> EventM n HumState
songBulkAddtoPl (PlaylistName -> String
forall a. ToString a => a -> String
MPD.toString PlaylistName
newPlName) Vector Song
songs HumState
st
  HumState -> EventM n HumState
forall (m :: * -> *). MonadIO m => HumState -> m HumState
rebuildPl HumState
st

-- | Pastes playlist in clipboard to disk (with nonconflicting name).
pastePlaylist :: HumState -> EventM n HumState
pastePlaylist :: HumState -> EventM n HumState
pastePlaylist HumState
st = do
  let plName :: PlaylistName
plName = PlaylistName -> Maybe PlaylistName -> PlaylistName
forall a. a -> Maybe a -> a
fromMaybe PlaylistName
"<error>" (HumState
st HumState
-> Getting (Maybe PlaylistName) HumState (Maybe PlaylistName)
-> Maybe PlaylistName
forall s a. s -> Getting a s a -> a
^. (Clipboard -> Const (Maybe PlaylistName) Clipboard)
-> HumState -> Const (Maybe PlaylistName) HumState
Lens' HumState Clipboard
clipboardL ((Clipboard -> Const (Maybe PlaylistName) Clipboard)
 -> HumState -> Const (Maybe PlaylistName) HumState)
-> ((Maybe PlaylistName
     -> Const (Maybe PlaylistName) (Maybe PlaylistName))
    -> Clipboard -> Const (Maybe PlaylistName) Clipboard)
-> Getting (Maybe PlaylistName) HumState (Maybe PlaylistName)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe PlaylistName
 -> Const (Maybe PlaylistName) (Maybe PlaylistName))
-> Clipboard -> Const (Maybe PlaylistName) Clipboard
Lens' Clipboard (Maybe PlaylistName)
clPlNameL)
  PlaylistName -> HumState -> EventM n HumState
forall n. PlaylistName -> HumState -> EventM n HumState
duplicatePlaylist PlaylistName
plName HumState
st

-- | Adds list of songs to queue in MPD. Does not rebuild state.
songBulkAddtoQ
  :: Bool -- ^ If true plays first song added
  -> V.Vector MPD.Song
  -> HumState
  -> EventM n HumState
songBulkAddtoQ :: Bool -> Vector Song -> HumState -> EventM n HumState
songBulkAddtoQ Bool
play Vector Song
songs HumState
s = do -- TODO don't need s? maybe should rebuild? overlap with system in Hum.UI
  let songPaths :: Vector Path
songPaths = Song -> Path
MPD.sgFilePath (Song -> Path) -> Vector Song -> Vector Path
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Vector Song
songs
  (Path -> EventM n (Response ())) -> Vector Path -> EventM n ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_
    (\Path
sel -> IO (Response ()) -> EventM n (Response ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
      (MPD () -> IO (Response ())
forall a. MPD a -> IO (Response a)
withMPD (MPD () -> IO (Response ())) -> MPD () -> IO (Response ())
forall a b. (a -> b) -> a -> b
$ Path -> Maybe Int -> MPD Id
forall (m :: * -> *). MonadMPD m => Path -> Maybe Int -> m Id
MPD.addId Path
sel Maybe Int
forall a. Maybe a
Nothing MPD Id -> (Id -> MPD ()) -> MPD ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= if Bool
play
        then Id -> MPD ()
forall (m :: * -> *). MonadMPD m => Id -> m ()
MPD.playId
        else MPD () -> Id -> MPD ()
forall a b. a -> b -> a
const MPD ()
forall (f :: * -> *). Applicative f => f ()
pass
      )
    )
    (Int -> Vector Path -> Vector Path
forall a. Int -> Vector a -> Vector a
V.take Int
1 Vector Path
songPaths)
  (Path -> EventM n (Response Id)) -> Vector Path -> EventM n ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (\Path
sel -> IO (Response Id) -> EventM n (Response Id)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (MPD Id -> IO (Response Id)
forall a. MPD a -> IO (Response a)
withMPD (MPD Id -> IO (Response Id)) -> MPD Id -> IO (Response Id)
forall a b. (a -> b) -> a -> b
$ Path -> Maybe Int -> MPD Id
forall (m :: * -> *). MonadMPD m => Path -> Maybe Int -> m Id
MPD.addId Path
sel Maybe Int
forall a. Maybe a
Nothing))
            (Int -> Vector Path -> Vector Path
forall a. Int -> Vector a -> Vector a
V.drop Int
1 Vector Path
songPaths)
  HumState -> EventM n HumState
forall (f :: * -> *) a. Applicative f => a -> f a
pure HumState
s

-- | Adds list of songs to stored playlist in MPD. Does not rebuild state.
songBulkAddtoPl
  :: String -- ^ Playlist Name
  -> V.Vector MPD.Song -- ^ Songs to add
  -> HumState
  -> EventM n HumState
songBulkAddtoPl :: String -> Vector Song -> HumState -> EventM n HumState
songBulkAddtoPl String
pl Vector Song
songs HumState
s = do
  let songPaths :: Vector Path
songPaths = Song -> Path
MPD.sgFilePath (Song -> Path) -> Vector Song -> Vector Path
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Vector Song
songs
  (Path -> EventM n (Response ())) -> Vector Path -> EventM n ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_
    (\Path
sel -> IO (Response ()) -> EventM n (Response ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
      (MPD () -> IO (Response ())
forall a. MPD a -> IO (Response a)
withMPD (MPD () -> IO (Response ())) -> MPD () -> IO (Response ())
forall a b. (a -> b) -> a -> b
$ PlaylistName -> Path -> MPD ()
forall (m :: * -> *). MonadMPD m => PlaylistName -> Path -> m ()
MPD.playlistAdd (String -> PlaylistName
forall a. IsString a => String -> a
fromString String
pl) Path
sel
      )
    )
    Vector Path
songPaths
  HumState -> EventM n HumState
forall (m :: * -> *). MonadIO m => HumState -> m HumState
rebuildPl HumState
s