module Sound.MED.Generic where

import qualified Sound.MED.Raw.MMD0 as MMD0
import qualified Sound.MED.Raw.MMD1 as MMD1
import qualified Sound.MED.Raw.MMD2 as MMD2
import qualified Sound.MED.Raw.MMD3 as MMD3

import qualified Sound.MED.Raw.MMD0exp as MMD0exp
import qualified Sound.MED.Raw.MMD0Song as MMD0Song
import qualified Sound.MED.Raw.MMD2Song as MMD2Song

import qualified Sound.MED.Generic.Block as MEDBlock
import qualified Sound.MED.Generic.Instrument as MEDInstrument
import qualified Sound.MED.Generic.PlaySeq as MEDPlaySeq
import qualified Sound.MED.Generic.Tempo as MEDTempo
import Sound.MED.Generic.Instrument(medinstruments)

import Sound.MED.Basic.Human(Human(human))
import Sound.MED.Basic.Amiga

import Control.Exception (bracket)
import Data.Foldable (foldMap)
import Text.Printf (printf)


data MED = MED
  { instrs :: [MEDInstrument.MEDInstrument]
  , blocks :: [MEDBlock.MEDBlock]
  , playseqs :: [MEDPlaySeq.MEDPlaySeq]
  , tempo :: MEDTempo.MEDTempo
  }

peek :: (Reader m) => m MED
peek = do
  ident <- peekULONG 0
  let samples0 song =
        take (fromIntegral (MMD0Song.numsamples song)) $ MMD0Song.sample song
  let samples2 song =
        take (fromIntegral (MMD2Song.numsamples song)) $ MMD2Song.sample song
  let instrs_ smplarr samples expdata med =
        medinstruments (smplarr med) (samples med)
          (foldMap MMD0exp.iinfo $ expdata med)
          (foldMap MMD0exp.exp_smp $ expdata med)
  let mmd0PlaySeq song = [MEDPlaySeq.playSeq0 song]
  let mmd2PlaySeqs = map MEDPlaySeq.playSeq2 . MMD2Song.playseqtable
  case ident of
    0x4D4D4430 -> do
      med <- MMD0.peek 0
      return $ MED
        { instrs = instrs_ MMD0.smplarr (samples0 . MMD0.song) MMD0.expdata med
        , blocks = map MEDBlock.medblock0 $ MMD0.blockarr med
        , playseqs = mmd0PlaySeq $ MMD0.song med
        , tempo = MEDTempo.song0Tempo $ MMD0.song med
        }
    0x4D4D4431 -> do
      med <- MMD1.peek 0
      return $ MED
        { instrs = instrs_ MMD1.smplarr (samples0 . MMD1.song) MMD1.expdata med
        , blocks = map MEDBlock.medblock1 $ MMD1.blockarr med
        , playseqs = mmd0PlaySeq $ MMD1.song med
        , tempo = MEDTempo.song0Tempo $ MMD1.song med
        }
    0x4D4D4432 -> do
      med <- MMD2.peek 0
      return $ MED
        { instrs = instrs_ MMD2.smplarr (samples2 . MMD2.song) MMD2.expdata med
        , blocks = map MEDBlock.medblock1 $ MMD2.blockarr med
        , playseqs = mmd2PlaySeqs $ MMD2.song med
        , tempo = MEDTempo.song2Tempo $ MMD2.song med
        }
    0x4D4D4433 -> do
      med <- MMD3.peek 0
      return $ MED
        { instrs = instrs_ MMD3.smplarr (samples2 . MMD3.song) MMD3.expdata med
        , blocks = map MEDBlock.medblock1 $ MMD3.blockarr med
        , playseqs = mmd2PlaySeqs $ MMD3.song med
        , tempo = MEDTempo.song2Tempo $ MMD3.song med
        }
    _ -> fail $ printf "unknown format: %08x" $ toInteger ident

instance Human MED where
  human m =
    concatMap human (instrs m) ++
    concatMap human (blocks m) ++
    unlines (map human (playseqs m))


load :: FilePath -> IO MED
load path = bracket (loadMEM path) freeMEM (runStorable peek)