{-# LANGUAGE ForeignFunctionInterface #-}

{-|
Description:    Functions and types providing metadata about the disc contents.

Copyright:      (c) 2018-2021 Sam May
License:        GPL-3.0-or-later
Maintainer:     ag@eitilt.life

Stability:      stable
Portability:    non-portable (requires libcdio)

Metadata is stored in a binary format both library-internally and on the disc.
Most audio archivists will recognize it as "those information fields in a CUE
file" (though there are [other
formats](https://www.gnu.org/software/libcdio/cd-text-format.html#Sony-Text-File-Format-_0028Input-Sheet-Version-0_002e7T_0029)
as well), and casual listeners will recognize it as the scrolling text that
you're always happy to see, on the rare times your music player shows it.
Little-used and even-less-known, however, is that a single disc can
theoretically contain metadata in up to eight different languages; because of
the complexity that introduces, no unifying datatype is provided in favour of a
function-based interface.


= @cdtext.h@

== Defines
* @MIN_CDTEXT_FIELD@                (removed; identical to @'minBound' :: 'Field'@)
* @MAX_CDTEXT_FIELD@                (removed; identical to @'maxBound' :: 'Field'@)

== Types
* @cdtext_field_t@                  -> 'Field'

    - @CDTEXT_FIELD_INVALID@        (removed; handled via 'Nothing')

* @cdtext_genre_t@                  -> 'Genre'

    - @CDTEXT_GENRE_UNUSED@         (removed; handled via 'Nothing')

* @cdtext_lang_t@                   -> 'Language'

    - @CDTEXT_LANGUAGE_UNKNOWN@     -> 'UnknownLanguage'

        - /Before libcdio 2.1.0:  (often effectively removed; handled via 'Nothing')/

    - @CDTEXT_LANGUAGE_INVALID@     (removed; handled via 'Nothing')
    - @CDTEXT_LANGUAGE_BLOCK_UNUSED@ (removed; handled via 'Nothing')

* @cdtext_t@                        (removed; merged into 'Cdio' objects)

== Symbols
* @cdtext_data_init@                -> 'cdTextDataInit'
* @cdtext_destroy@                  (removed; handled via the garbage collector)
* @cdtext_field2str@                -> 'fieldString'
* @cdtext_genre2str@                -> 'genreString'
* @cdtext_get@                      (removed; type conversion makes it identical to @cdtext_get_const@)
* @cdtext_get_const@                -> 'cdTextGet'
* @cdtext_get_first_track@          -> 'firstTrack'
* @cdtext_get_genre@                -> 'genre'
* @cdtext_get_language@             -> 'language'
* @cdtext_get_last_track@           -> 'lastTrack'
* @cdtext_init@                     (removed; internal function without much external occasion)
* @cdtext_lang2str@                 -> 'languageString'
* @cdtext_list_languages@           -> 'listLanguages'
* @cdtext_list_languages_v2@        -> 'listAllLanguages'
* @cdtext_select_language@          -> 'selectLanguage'
* @cdtext_set@                      (removed; primarily intended for internal use, and is more limited than would be expected)
* @cdtext_set_language_index@       -> 'selectLanguageIndex'
* @cdtext_str2lang@                 -> 'parseLanguage'


= "Sound.Libcdio.Read.CdText"
While similar functionality is provided, the
@"Sound.Libcdio.Read".'Sound.Libcdio.Read.CdText'@ is written as a separate
monadic interface rather than in the 'Cdio'-bound style used here.

* 'cdTextDataInit'                  -> 'Sound.Libcdio.Read.CdText.parseCdText'
* 'cdTextGet'                       -> 'Sound.Libcdio.Read.CdText.discId', @'snd' 'Sound.Libcdio.Read.CdText.genre'@, and 'Sound.Libcdio.Read.CdText.info'
* 'genre'                           -> @'fst' 'Sound.Libcdio.Read.CdText.genre'@
* 'listAllLanguages'                -> 'Sound.Libcdio.Read.CdText.languages'
* 'listLanguages'                   (removed; obsolete and imprecise)
* 'selectLanguage'                  -> 'Sound.Libcdio.Read.CdText.withLanguage'
* 'selectLanguageIndex'             -> 'Sound.Libcdio.Read.CdText.withIndex'
-}
module Foreign.Libcdio.CdText
    ( -- * Types
      Cdio
    , Field ( .. )
    , Genre ( .. )
    , Language ( .. )
      -- * Description
    , fieldString
    , languageString
    , parseLanguage
    , genreString
      -- * Management
    , cdTextDataInit
    , listLanguages
    , listAllLanguages
    , selectLanguage
    , selectLanguageIndex
      -- * Access
    , cdTextGet
    , genre
    , language
    , firstTrack
    , lastTrack
    ) where


import qualified Data.List as L
import qualified Data.Maybe as Y

import qualified Foreign.C.String as C
import qualified Foreign.C.Types as C
import qualified Foreign.Ptr as C

import qualified Foreign.Marshal.Array as M
import qualified Foreign.Marshal.Utils as M

import qualified System.IO.Unsafe as IO.Unsafe

import Foreign.Libcdio.Device
import Foreign.Libcdio.Marshal
import Foreign.Libcdio.Track
import Foreign.Libcdio.Types.Enums
import Foreign.Libcdio.Types.Internal
import Foreign.Libcdio.Version

import Sound.Libcdio.Common


-- | Return a canonical English name of the given genre, as opposed to the
-- machine representation returned by the 'Show' instance.
genreString :: Genre -> String
genreString :: Genre -> String
genreString = IO String -> String
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO String -> String) -> (Genre -> IO String) -> Genre -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    CString -> IO String
C.peekCString (CString -> IO String) -> (Genre -> CString) -> Genre -> IO String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> CString
genreString' (CGenre -> CString) -> (Genre -> CGenre) -> Genre -> CString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CGenre
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CGenre) -> (Genre -> Int) -> Genre -> CGenre
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Genre -> Int
forall a. Enum a => a -> Int
fromEnum

foreign import ccall safe "cdio/compat/cdtext.h cdtext_genre2str"
  genreString' :: CGenre -> C.CString

-- | Return a canonical English name of the given language, as opposed to the
-- machine representation returned by the 'Show' instance.
languageString :: Language -> String
languageString :: Language -> String
languageString = IO String -> String
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO String -> String)
-> (Language -> IO String) -> Language -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    CString -> IO String
C.peekCString (CString -> IO String)
-> (Language -> CString) -> Language -> IO String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> CString
languageString' (CGenre -> CString) -> (Language -> CGenre) -> Language -> CString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CGenre
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CGenre) -> (Language -> Int) -> Language -> CGenre
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Language -> Int
forall a. Enum a => a -> Int
fromEnum

foreign import ccall safe "cdio/compat/cdtext.h cdtext_lang2str"
  languageString' :: CLanguage -> C.CString

-- | Transform a string returned by 'languageString' back into its
-- 'Language' representation.
-- 
-- /Before libcdio 2.1.0:  Always returns 'Nothing'/
parseLanguage :: String -> Maybe Language
parseLanguage :: String -> Maybe Language
parseLanguage String
s
    | Version
libcdioVersionNum Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
>= [Int] -> Version
makeVersion [Int
2,Int
1] = IO (Maybe Language) -> Maybe Language
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO (Maybe Language) -> Maybe Language)
-> IO (Maybe Language) -> Maybe Language
forall a b. (a -> b) -> a -> b
$ do
        CGenre
i <- String -> (CString -> IO CGenre) -> IO CGenre
forall a. String -> (CString -> IO a) -> IO a
C.withCString String
s CString -> IO CGenre
parseLanguage'
        Maybe Language -> IO (Maybe Language)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Language -> IO (Maybe Language))
-> (Maybe CGenre -> Maybe Language)
-> Maybe CGenre
-> IO (Maybe Language)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CGenre -> Language) -> Maybe CGenre -> Maybe Language
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Language
forall a. Enum a => Int -> a
toEnum (Int -> Language) -> (CGenre -> Int) -> CGenre -> Language
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) (Maybe CGenre -> IO (Maybe Language))
-> Maybe CGenre -> IO (Maybe Language)
forall a b. (a -> b) -> a -> b
$ [CGenre] -> CGenre -> Maybe CGenre
forall a. Eq a => [a] -> a -> Maybe a
maybeError [CGenre
languageInvalid, CGenre
languageUnused] CGenre
i
    | Bool
otherwise = Maybe Language
forall a. Maybe a
Nothing

foreign import ccall safe "cdio/compat/cdtext.h cdtext_str2lang_safe"
  parseLanguage' :: C.CString -> IO CLanguage

foreign import ccall safe "cdio/compat/cdtext.h language_unknown"
  languageUnknown :: CLanguage

foreign import ccall safe "cdio/compat/cdtext.h language_invalid"
  languageInvalid :: CLanguage

foreign import ccall safe "cdio/compat/cdtext.h language_unused"
  languageUnused :: CLanguage

-- | Describe the type of data in a human-readable manner, as opposed to the
-- machine representation returned by the 'Show' instance.
fieldString :: Field -> String
fieldString :: Field -> String
fieldString = IO String -> String
forall a. IO a -> a
IO.Unsafe.unsafePerformIO (IO String -> String) -> (Field -> IO String) -> Field -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    CString -> IO String
C.peekCString (CString -> IO String) -> (Field -> CString) -> Field -> IO String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> CString
fieldString' (CGenre -> CString) -> (Field -> CGenre) -> Field -> CString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CGenre
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CGenre) -> (Field -> Int) -> Field -> CGenre
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Field -> Int
forall a. Enum a => a -> Int
fromEnum

foreign import ccall safe "cdio/compat/cdtext.h cdtext_field2str"
  fieldString' :: CField -> C.CString


-- | Attempt to retrieve the value of a given field from the disc (if
-- 'Nothing') or track (if 'Just') metadata, in the currently-active language.
-- Note that 'GenreName' and 'DiscId' will /always/ refer to the disc-level
-- data.
cdTextGet :: Cdio -> Field -> Maybe Track -> IO (Maybe String)
cdTextGet :: Cdio -> Field -> Maybe Track -> IO (Maybe String)
cdTextGet Cdio
_ Field
_ (Just Track
DiscPregap) = Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
cdTextGet Cdio
_ Field
_ (Just Track
DiscLeadout) = Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
cdTextGet Cdio
c Field
GenreName Maybe Track
_ = Cdio -> Field -> Track -> IO (Maybe String)
cdTextGet_ Cdio
c Field
GenreName Track
0
cdTextGet Cdio
c Field
DiscId Maybe Track
_ = Cdio -> Field -> Track -> IO (Maybe String)
cdTextGet_ Cdio
c Field
DiscId Track
0
cdTextGet Cdio
c Field
f Maybe Track
Nothing = Cdio -> Field -> Track -> IO (Maybe String)
cdTextGet_ Cdio
c Field
f Track
0
cdTextGet Cdio
c Field
f (Just Track
t) = Cdio -> Field -> Track -> IO (Maybe String)
cdTextGet_ Cdio
c Field
f Track
t

cdTextGet_ :: Cdio -> Field -> Track -> IO (Maybe String)
cdTextGet_ :: Cdio -> Field -> Track -> IO (Maybe String)
cdTextGet_ Cdio
c Field
f Track
t = Maybe String
-> Cdio -> (Ptr CdText -> IO (Maybe String)) -> IO (Maybe String)
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' Maybe String
forall a. Maybe a
Nothing Cdio
c ((Ptr CdText -> IO (Maybe String)) -> IO (Maybe String))
-> (Ptr CdText -> IO (Maybe String)) -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ \Ptr CdText
x ->
    Ptr CdText -> CGenre -> CTrack -> IO CString
cdTextGet' Ptr CdText
x (Int -> CGenre
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CGenre) -> Int -> CGenre
forall a b. (a -> b) -> a -> b
$ Field -> Int
forall a. Enum a => a -> Int
fromEnum Field
f) (Int -> CTrack
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CTrack) -> Int -> CTrack
forall a b. (a -> b) -> a -> b
$ Track -> Int
forall a. Enum a => a -> Int
fromEnum Track
t) IO CString -> (CString -> IO (Maybe String)) -> IO (Maybe String)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (CString -> IO String) -> CString -> IO (Maybe String)
forall a b. (Ptr a -> IO b) -> Ptr a -> IO (Maybe b)
M.maybePeek CString -> IO String
C.peekCString

foreign import ccall safe "cdio/compat/cdtext.h cdtext_get_const"
  cdTextGet' :: C.Ptr CdText -> CField -> CTrack -> IO C.CString


-- | The machine-readable genre describing the music on this disc.  For any
-- associated human-readable name or subgenre, use @'cdTextGet' c 'GenreName'
-- 'Nothing'@.
genre :: Cdio -> IO (Maybe Genre)
genre :: Cdio -> IO (Maybe Genre)
genre Cdio
c = do
    Maybe CGenre
g <- Cdio -> (Ptr CdText -> IO CGenre) -> IO (Maybe CGenre)
forall b. Cdio -> (Ptr CdText -> IO b) -> IO (Maybe b)
withCdText Cdio
c Ptr CdText -> IO CGenre
genre'
    Maybe Genre -> IO (Maybe Genre)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Genre -> IO (Maybe Genre))
-> Maybe Genre -> IO (Maybe Genre)
forall a b. (a -> b) -> a -> b
$ if Maybe CGenre
g Maybe CGenre -> Maybe CGenre -> Bool
forall a. Eq a => a -> a -> Bool
== CGenre -> Maybe CGenre
forall a. a -> Maybe a
Just CGenre
0 then Maybe Genre
forall a. Maybe a
Nothing else Int -> Genre
forall a. Enum a => Int -> a
toEnum (Int -> Genre) -> (CGenre -> Int) -> CGenre -> Genre
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CGenre -> Genre) -> Maybe CGenre -> Maybe Genre
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe CGenre
g

foreign import ccall safe "cdio/compat/cdtext.h cdtext_get_genre"
  genre' :: C.Ptr CdText -> IO CGenre

-- | Indicate which language results will (currently) be returned in.  See
-- 'selectLanguage' to change this.
language :: Cdio -> IO (Maybe Language)
language :: Cdio -> IO (Maybe Language)
language Cdio
c = (CGenre -> Language) -> Maybe CGenre -> Maybe Language
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Language
forall a. Enum a => Int -> a
toEnum (Int -> Language) -> (CGenre -> Int) -> CGenre -> Language
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) (Maybe CGenre -> Maybe Language)
-> IO (Maybe CGenre) -> IO (Maybe Language)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Cdio -> (Ptr CdText -> IO CGenre) -> IO (Maybe CGenre)
forall b. Cdio -> (Ptr CdText -> IO b) -> IO (Maybe b)
withCdText Cdio
c Ptr CdText -> IO CGenre
language'

foreign import ccall safe "cdio/compat/cdtext.h cdtext_get_language"
  language' :: C.Ptr CdText -> IO CLanguage


-- | The earliest track with any associated metadata in the current language.
-- Note that this may differ from 'minTrack'.
firstTrack :: Cdio -> IO (Maybe Track)
firstTrack :: Cdio -> IO (Maybe Track)
firstTrack Cdio
c = (CTrack -> Track) -> Maybe CTrack -> Maybe Track
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Track
forall a. Enum a => Int -> a
toEnum (Int -> Track) -> (CTrack -> Int) -> CTrack -> Track
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CTrack -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) (Maybe CTrack -> Maybe Track)
-> (CTrack -> Maybe CTrack) -> CTrack -> Maybe Track
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CTrack] -> CTrack -> Maybe CTrack
forall a. Eq a => [a] -> a -> Maybe a
maybeError [CTrack
0] (CTrack -> Maybe Track) -> IO CTrack -> IO (Maybe Track)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CTrack -> Cdio -> (Ptr CdText -> IO CTrack) -> IO CTrack
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' CTrack
0 Cdio
c Ptr CdText -> IO CTrack
firstTrack'

foreign import ccall safe "cdio/compat/cdtext.h cdtext_get_first_track"
  firstTrack' :: C.Ptr CdText -> IO CTrack

-- | The final track with any associated metadata in the current language.
-- Note that this may differ from 'maxTrack'.
lastTrack :: Cdio -> IO (Maybe Track)
lastTrack :: Cdio -> IO (Maybe Track)
lastTrack Cdio
c = (CTrack -> Track) -> Maybe CTrack -> Maybe Track
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Track
forall a. Enum a => Int -> a
toEnum (Int -> Track) -> (CTrack -> Int) -> CTrack -> Track
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CTrack -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) (Maybe CTrack -> Maybe Track)
-> (CTrack -> Maybe CTrack) -> CTrack -> Maybe Track
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CTrack] -> CTrack -> Maybe CTrack
forall a. Eq a => [a] -> a -> Maybe a
maybeError [CTrack
0] (CTrack -> Maybe Track) -> IO CTrack -> IO (Maybe Track)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CTrack -> Cdio -> (Ptr CdText -> IO CTrack) -> IO CTrack
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' CTrack
0 Cdio
c Ptr CdText -> IO CTrack
lastTrack'

foreign import ccall safe "cdio/compat/cdtext.h cdtext_get_last_track"
  lastTrack' :: C.Ptr CdText -> IO CTrack


-- | Try to set the data associated with the given language as active for
-- future calls to 'cdTextGet' and similar.  If passed 'UnknownLanguage' or the
-- CDTEXT does not provide the one requested, selects the first (default) data
-- set instead, and returns 'False'.
selectLanguage :: Cdio -> Language -> IO Bool
selectLanguage :: Cdio -> Language -> IO Bool
selectLanguage Cdio
c Language
l = (CBool -> Bool) -> IO CBool -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CBool -> Bool
forall a. (Eq a, Num a) => a -> Bool
M.toBool (IO CBool -> IO Bool)
-> ((Ptr CdText -> IO CBool) -> IO CBool)
-> (Ptr CdText -> IO CBool)
-> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CBool -> Cdio -> (Ptr CdText -> IO CBool) -> IO CBool
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' CBool
0 Cdio
c ((Ptr CdText -> IO CBool) -> IO Bool)
-> (Ptr CdText -> IO CBool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Ptr CdText
x ->
    Ptr CdText -> CGenre -> IO CBool
selectLanguage' Ptr CdText
x (CGenre -> IO CBool) -> (Int -> CGenre) -> Int -> IO CBool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CGenre
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> IO CBool) -> Int -> IO CBool
forall a b. (a -> b) -> a -> b
$ Language -> Int
forall a. Enum a => a -> Int
fromEnum Language
l

foreign import ccall safe "cdio/compat/cdtext.h cdtext_select_language"
  selectLanguage' :: C.Ptr CdText -> CLanguage -> IO CBool

-- | Retrieve the languages included in the disc metadata.  Note that this does
-- not save the index position or any duplicate language blocks (if that is
-- desired, see 'listAllLanguages' instead).
listLanguages :: Cdio -> IO [Language]
listLanguages :: Cdio -> IO [Language]
listLanguages Cdio
c = [Language]
-> Cdio -> (Ptr CdText -> IO [Language]) -> IO [Language]
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' [] Cdio
c ((Ptr CdText -> IO [Language]) -> IO [Language])
-> (Ptr CdText -> IO [Language]) -> IO [Language]
forall a b. (a -> b) -> a -> b
$ \Ptr CdText
x -> do
    Ptr CGenre
l' <- Ptr CdText -> IO (Ptr CGenre)
listLanguages' Ptr CdText
x
    [Maybe Language]
ls <- Ptr CGenre -> [CGenre] -> IO [Maybe Language]
peekLanguages Ptr CGenre
l' [CGenre
languageUnknown, CGenre
languageInvalid, CGenre
languageUnused]
    [Language] -> IO [Language]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Language] -> IO [Language])
-> ([Language] -> [Language]) -> [Language] -> IO [Language]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Language] -> [Language]
forall a. Eq a => [a] -> [a]
L.nub ([Language] -> IO [Language]) -> [Language] -> IO [Language]
forall a b. (a -> b) -> a -> b
$ [Maybe Language] -> [Language]
forall a. [Maybe a] -> [a]
Y.catMaybes [Maybe Language]
ls

foreign import ccall safe "cdio/compat/cdtext.h cdtext_list_languages"
  listLanguages' :: C.Ptr CdText -> IO (C.Ptr CLanguage)

-- | Retrieve the languages included in the disc metadata, in the order they
-- occur, and respecting any empty language blocks if there's a valid language
-- after them (empty blocks at the end are cleaned away).
-- 
-- /Before libcdio 2.1.0:  Acts as 'listLanguages'/
listAllLanguages :: Cdio -> IO [Maybe Language]
listAllLanguages :: Cdio -> IO [Maybe Language]
listAllLanguages Cdio
c = [Maybe Language]
-> Cdio
-> (Ptr CdText -> IO [Maybe Language])
-> IO [Maybe Language]
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' [] Cdio
c ((Ptr CdText -> IO [Maybe Language]) -> IO [Maybe Language])
-> (Ptr CdText -> IO [Maybe Language]) -> IO [Maybe Language]
forall a b. (a -> b) -> a -> b
$ \Ptr CdText
x -> do
    Ptr CGenre
l' <- Ptr CdText -> IO (Ptr CGenre)
listAllLanguages' Ptr CdText
x
    [Maybe Language]
ls <- Ptr CGenre -> [CGenre] -> IO [Maybe Language]
peekLanguages Ptr CGenre
l' [CGenre
languageInvalid, CGenre
languageUnused]
    [Maybe Language] -> IO [Maybe Language]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Maybe Language] -> IO [Maybe Language])
-> [Maybe Language] -> IO [Maybe Language]
forall a b. (a -> b) -> a -> b
$ case (Maybe Language -> Bool) -> [Maybe Language] -> [Maybe Language]
forall a. (a -> Bool) -> [a] -> [a]
L.dropWhileEnd Maybe Language -> Bool
forall a. Maybe a -> Bool
Y.isNothing [Maybe Language]
ls of
        [] -> [Language -> Maybe Language
forall a. a -> Maybe a
Just Language
UnknownLanguage]
        [Maybe Language]
ls' -> [Maybe Language]
ls'

foreign import ccall safe "cdio/compat/cdtext.h cdtext_list_languages_v2_safe"
  listAllLanguages' :: C.Ptr CdText -> IO (C.Ptr CLanguage)

peekLanguages :: C.Ptr CLanguage -> [CLanguage] -> IO [Maybe Language]
peekLanguages :: Ptr CGenre -> [CGenre] -> IO [Maybe Language]
peekLanguages Ptr CGenre
p [CGenre]
es
    | Ptr CGenre
p Ptr CGenre -> Ptr CGenre -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr CGenre
forall a. Ptr a
C.nullPtr = [Maybe Language] -> IO [Maybe Language]
forall (m :: * -> *) a. Monad m => a -> m a
return []
    | Bool
otherwise = (CGenre -> Maybe Language) -> [CGenre] -> [Maybe Language]
forall a b. (a -> b) -> [a] -> [b]
map ((CGenre -> Language) -> Maybe CGenre -> Maybe Language
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Language
forall a. Enum a => Int -> a
toEnum (Int -> Language) -> (CGenre -> Int) -> CGenre -> Language
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CGenre -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) (Maybe CGenre -> Maybe Language)
-> (CGenre -> Maybe CGenre) -> CGenre -> Maybe Language
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CGenre] -> CGenre -> Maybe CGenre
forall a. Eq a => [a] -> a -> Maybe a
maybeError [CGenre]
es) ([CGenre] -> [Maybe Language])
-> IO [CGenre] -> IO [Maybe Language]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Ptr CGenre -> IO [CGenre]
forall a. Storable a => Int -> Ptr a -> IO [a]
M.peekArray Int
8 Ptr CGenre
p

-- | Select the language at the given index of 'listAllLanguages' for future
-- data retrieval.  If the index is out of bounds or corresponds to a 'Nothing'
-- in 'listAllLanguages', the first (default) data set is selected instead and
-- 'False is returned.
selectLanguageIndex :: Cdio -> Word -> IO Bool
selectLanguageIndex :: Cdio -> Word -> IO Bool
selectLanguageIndex Cdio
c Word
i = CBool -> Bool
forall a. (Eq a, Num a) => a -> Bool
M.toBool (CBool -> Bool) -> IO CBool -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CBool -> Cdio -> (Ptr CdText -> IO CBool) -> IO CBool
forall b. b -> Cdio -> (Ptr CdText -> IO b) -> IO b
withCdText' CBool
0 Cdio
c ((Ptr CdText -> CGenre -> IO CBool)
-> CGenre -> Ptr CdText -> IO CBool
forall a b c. (a -> b -> c) -> b -> a -> c
flip Ptr CdText -> CGenre -> IO CBool
selectLanguageIndex' (CGenre -> Ptr CdText -> IO CBool)
-> CGenre -> Ptr CdText -> IO CBool
forall a b. (a -> b) -> a -> b
$ Word -> CGenre
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
i)

foreign import ccall safe "cdio/compat/cdtext.h cdtext_set_language_index_safe"
  selectLanguageIndex' :: C.Ptr CdText -> C.CInt -> IO CBool


{- Primarily intended as an internal function
-- | Set the given field at the given track to the given value.
cdTextSet :: Cdio -> Field -> Maybe String -> Maybe Track -> IO ()
cdTextSet _ _ _ (Just DiscPregap) = mempty
cdTextSet _ _ _ (Just DiscLeadout) = mempty
cdTextSet c f v Nothing = cdTextSet_ c f v 0
cdTextSet c f v (Just t) = cdTextSet_ c f v t

cdTextSet_ :: Cdio -> Field -> Maybe String -> Track -> IO ()
cdTextSet_ c f v t = withCdText_ c $ \x -> M.maybeWith C.withCString v $ \s ->
    cdTextSet' x (toCEnum f) s (withTrack t) C.nullPtr

foreign import ccall safe "cdio/compat/cdtext.h cdtext_set"
  cdTextSet' :: C.Ptr CdText -> CField -> C.CString -> CTrack -> C.CString -> IO ()
-}