module ID3.Type.Tag
where
import Data.List ((\\))
import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Accessor
import Data.Accessor.Basic (compose)
import ID3.Type.Header
import ID3.Type.ExtHeader
import ID3.Type.Frame
import ID3.Type.Unparse
data ID3Tag = ID3Tag
{ tagHeader :: ID3Header
, tagExtHeader :: Maybe ID3ExtHeader
, tagFrames :: Map FrameID ID3Frame
, tagFramesOrder :: [FrameID]
, tagPadding :: Integer
} deriving Eq
emptyID3Tag :: ID3Tag
emptyID3Tag = ID3Tag emptyID3Header Nothing Map.empty [] 0
initID3Tag :: [ID3Tag -> ID3Tag] -> ID3Tag
initID3Tag = flip compose emptyID3Tag
header :: Accessor ID3Tag ID3Header
header = accessor tagHeader (\x t -> t {tagHeader = x})
version :: Accessor ID3Tag TagVersion
version = header .> tagVersion
instance HasSize ID3Tag where
size = accessor getFullSize setSize
setSize :: TagSize -> ID3Tag -> ID3Tag
setSize = setVal $ header .> tagSize
getFullSize :: ID3Tag -> FrameSize
getFullSize t = getActualSize t + (t^.padding)
getActualSize :: ID3Tag -> FrameSize
getActualSize t = (footerSize t) + (framesSize (t^.frames)) + (extHSize t)
framesSize :: Map FrameID ID3Frame -> FrameSize
framesSize fs = Map.fold (\fr x -> fr^.frHeader^.frSize + 10 + x) 0 fs
footerSize :: ID3Tag -> Integer
footerSize t = if (footerFlag $ t^.flags) then 10 else 0
extHSize :: ID3Tag -> Integer
extHSize t = case t^.extHeader of
Just eH -> eH^.extSize
Nothing -> 0
padding :: Accessor ID3Tag Integer
padding = accessor tagPadding (\x t -> t {tagPadding = x})
flags :: Accessor ID3Tag TagFlags
flags = header .> tagFlags
extHeader :: Accessor ID3Tag (Maybe ID3ExtHeader)
extHeader = accessor tagExtHeader (\x t -> t {tagExtHeader = x})
frames :: Accessor ID3Tag (Map FrameID ID3Frame)
frames = accessor tagFrames (\x t -> t {tagFrames = x})
framesOrder :: Accessor ID3Tag [FrameID]
framesOrder = accessor tagFramesOrder (\x t -> t {tagFramesOrder = x})
frame :: FrameID -> Accessor ID3Tag (Maybe ID3Frame)
frame frid = accessor (\t -> getFrame t frid) (\x t -> setFrame t frid x)
getFrame :: ID3Tag -> FrameID -> Maybe ID3Frame
getFrame t f = Map.lookup f (t^.frames)
setFrame :: ID3Tag -> FrameID -> Maybe ID3Frame -> ID3Tag
setFrame t f x = updateSize $ frames ^= Map.alter (\_ -> x) f (t^.frames) $ t
sortFrames :: Map FrameID ID3Frame -> [FrameID] -> [ID3Frame]
sortFrames fs ids = mapMaybe (\frid -> Map.lookup frid fs) $ ids ++ (Map.keys fs \\ ids)
instance Show ID3Tag where
show t = ( show $ t^.header) ++"\n"++
( "Full tag size: "++(show $ getFullSize t)) ++"\n"++
( "Actual tag size: "++(show $ getActualSize t)) ++"\n"++
( "Padding size: "++(show $ t^.padding)) ++"\n"++
( unwords $ t^.framesOrder ) ++"\n"++
( maybe "" show $ t^.extHeader ) ++"\n"++
( concatMap show $ sortFrames (t^.frames) (t^.framesOrder) )
instance Parsed ID3Tag where
unparse t = ( unparse $ t^.header ) ++
( maybe [] unparse $ t^.extHeader ) ++
( concatMap unparse $ sortFrames (t^.frames) (t^.framesOrder) ) ++
( if (footerFlag $ t^.flags) then unparseFooter ++ (drop 10 unparsePadding) else unparsePadding )
where
unparseFooter = (unparse $ Str "3DI") ++ (drop 3 $ unparse $ t^.header)
unparsePadding = replicate (fromInteger $ t^.padding) 0x00