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 emptyID3Header Nothing Map.empty [] 0
initID3Tag = flip compose emptyID3Tag
header = accessor tagHeader (\x t -> t {tagHeader = x})
version = header .> tagVersion
instance HasSize ID3Tag where
size = accessor getFullSize setSize
setSize = setVal $ header .> tagSize
getFullSize t = getActualSize t + (t^.padding)
getActualSize t = (footerSize t) + (framesSize (t^.frames)) + (extHSize t)
framesSize fs = Map.fold (\fr x -> fr^.frHeader^.frSize + 10 + x) 0 fs
footerSize t = if t^.flags^.footed then 10 else 0
extHSize t = case t^.extHeader of
Just eH -> eH^.extSize
Nothing -> 0
padding = accessor tagPadding (\x t -> t {tagPadding = x})
flags = header .> tagFlags
extHeader = accessor tagExtHeader (\x t -> t {tagExtHeader = x})
frames = accessor tagFrames (\x t -> t {tagFrames = x})
framesOrder = accessor tagFramesOrder (\x t -> t {tagFramesOrder = x})
frame :: FrameID -> Accessor ID3Tag (Maybe ID3Frame)
frame id = accessor (\t -> getFrame t id) (\x t -> setFrame t id x)
getFrame t f = Map.lookup f (t^.frames)
setFrame t f x = updateSize $ frames ^= Map.alter (\_ -> x) f (t^.frames) $ t
sortFrames fs ids = mapMaybe (\id -> Map.lookup id 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 t^.flags^.footed then unparseFooter ++ (drop 10 unparsePadding) else unparsePadding )
where
unparseFooter = (unparse $ Str "3DI") ++ (drop 3 $ unparse $ t^.header)
unparsePadding = replicate (fromInteger $ t^.padding) 0x00