module Class where

import qualified Label

import Control.DeepSeq (NFData, rnf)
import Control.Monad (mplus, mfilter)

import qualified Data.Foldable as Fold
import qualified Data.List.HT as ListHT
import qualified Data.Map as Map; import Data.Map (Map)
import Data.String.HT (trim)
import Data.Maybe (fromMaybe)
import Data.Maybe.HT (toMaybe)
import Data.Char (isDigit)


data Sound rasping chirping ticking growling =
     Other String
   | Rasping rasping
   | Chirping chirping
   | Ticking ticking
   | Growling growling

instance
   (NFData rasping, NFData chirping, NFData ticking, NFData growling) =>
      NFData (Sound rasping chirping ticking growling) where
   rnf (Other str) = rnf str
   rnf (Rasping dat) = rnf dat
   rnf (Chirping dat) = rnf dat
   rnf (Ticking dat) = rnf dat
   rnf (Growling dat) = rnf dat


isPause :: Sound rasping chirping ticking growling -> Bool
isPause (Other str) = str == Label.pause
isPause _ = False


toName :: Sound rasping chirping ticking growling -> String
toName cl =
   case cl of
      Other str -> str
      Rasping _ -> Label.rasping
      Chirping _ -> Label.chirping
      Ticking _ -> Label.ticking
      Growling _ -> Label.growling

toLabel :: Sound Int chirping Int Int -> String
toLabel cl =
   case cl of
      Other str -> str
      Rasping numClicks -> Label.rasping ++ ' ' : show numClicks
      Chirping _ -> Label.chirping
      Ticking numClicks -> Label.ticking ++ ' ' : show numClicks
      Growling numClicks -> Label.growling ++ ' ' : show numClicks


type SoundParsed = Sound String String String String

fromLabel :: String -> SoundParsed
fromLabel lab =
   fromMaybe (Other lab) $
   fmap (Rasping . trim) (ListHT.maybePrefixOf Label.rasping lab)
   `mplus`
   fmap (Chirping . trim) (ListHT.maybePrefixOf Label.chirping lab)
   `mplus`
   fmap (Ticking . trim) (ListHT.maybePrefixOf Label.ticking lab)
   `mplus`
   fmap (Growling . trim) (ListHT.maybePrefixOf Label.growling lab)

strToLabel :: SoundParsed -> String
strToLabel cl =
   let add ext str = if null ext then str else str ++ ' ' : ext
   in case cl of
         Other str -> str
         Rasping str -> add Label.rasping str
         Chirping str -> add Label.chirping str
         Ticking str -> add Label.ticking str
         Growling str -> add Label.growling str


data Purity = Pure | Rumble
   deriving (Eq)

type SoundPurity = Sound Purity Purity Purity Purity

purityToName :: SoundPurity -> String
purityToName cl =
   let ext p =
         case p of
            Pure -> ""
            Rumble -> " rumble"
   in case cl of
         Other str -> str
         Rasping p -> Label.rasping ++ ext p
         Chirping p -> Label.chirping ++ ext p
         Ticking p -> Label.ticking ++ ext p
         Growling p -> Label.growling ++ ext p

checkPurity :: SoundParsed -> SoundPurity
checkPurity cl =
   fromMaybe (Other $ strToLabel cl) $
   let check cons p str =
         toMaybe (str==Label.rumble) (cons Rumble)
         `mplus`
         toMaybe (p str) (cons Pure)
   in case cl of
         Other str -> Just $ Other str
         Rasping str -> check Rasping (all isDigit) str
         Chirping str -> check Chirping null str
         Ticking str -> check Ticking (all isDigit) str
         Growling str -> check Growling (all isDigit) str

setRumble :: Bool -> SoundPurity -> SoundPurity
setRumble b cl =
   let add r = if b then Rumble else r
   in  case cl of
         Other str ->
            Other $
            if b
              then
                if str == Label.pause
                  then Label.rumble
                  else str ++ ' ' : Label.rumble
              else str
         Rasping r -> Rasping $ add r
         Chirping r -> Chirping $ add r
         Ticking r -> Ticking $ add r
         Growling r -> Growling $ add r


mapChirping ::
   (chirping0 -> chirping1) ->
   Sound rasping chirping0 ticking growling ->
   Sound rasping chirping1 ticking growling
mapChirping f cl =
   case cl of
      Other str -> Other str
      Rasping r -> Rasping r
      Chirping ch -> Chirping $ f ch
      Ticking t -> Ticking t
      Growling g -> Growling g

mapRasping ::
   (rasping0 -> rasping1) ->
   Sound rasping0 chirping ticking growling ->
   Sound rasping1 chirping ticking growling
mapRasping f cl =
   case cl of
      Other str -> Other str
      Rasping r -> Rasping $ f r
      Chirping ch -> Chirping ch
      Ticking t -> Ticking t
      Growling g -> Growling g

mapTicking ::
   (ticking0 -> ticking1) ->
   Sound rasping chirping ticking0 growling ->
   Sound rasping chirping ticking1 growling
mapTicking f cl =
   case cl of
      Other str -> Other str
      Rasping r -> Rasping r
      Chirping ch -> Chirping ch
      Ticking t -> Ticking $ f t
      Growling g -> Growling g

mapGrowling ::
   (growling0 -> growling1) ->
   Sound rasping chirping ticking growling0 ->
   Sound rasping chirping ticking growling1
mapGrowling f cl =
   case cl of
      Other str -> Other str
      Rasping r -> Rasping r
      Chirping ch -> Chirping ch
      Ticking t -> Ticking t
      Growling g -> Growling $ f g


maybeRasping :: Sound rasping chirping ticking growling -> Maybe rasping
maybeRasping cl =
   case cl of
      Rasping r -> Just r
      _ -> Nothing

maybeChirping :: Sound rasping chirping ticking growling -> Maybe chirping
maybeChirping cl =
   case cl of
      Chirping c -> Just c
      _ -> Nothing

maybeTicking :: Sound rasping chirping ticking growling -> Maybe ticking
maybeTicking cl =
   case cl of
      Ticking r -> Just r
      _ -> Nothing

maybeGrowling :: Sound rasping chirping ticking growling -> Maybe growling
maybeGrowling cl =
   case cl of
      Growling r -> Just r
      _ -> Nothing

maybeOther :: Sound rasping chirping ticking growling -> Maybe String
maybeOther cl =
   case cl of
      Other s -> Just s
      _ -> Nothing


countOthers ::
   (Fold.Foldable f) =>
   f (Sound rasping chirping ticking growling) -> Map String Int
countOthers =
   Map.unionsWith (+) .
   map (maybe Map.empty (flip Map.singleton 1) .
        mfilter (flip notElem [Label.pause, Label.rumble]) . maybeOther) .
   Fold.toList


data Abstract advert rasping chirping ticking growling =
     NoAdvertisement (Sound rasping chirping ticking growling)
   | Advertisement advert rasping (Maybe chirping)


abstractToLabel :: Abstract time Int chirping Int Int -> String
abstractToLabel cl =
   case cl of
      NoAdvertisement x -> toLabel x
      Advertisement _ numClicks chirp ->
         Label.advertisement ++ ' ' : show numClicks ++
         maybe " end" (const "") chirp