{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE GADTs #-}

module Hypsibius.Data where

import           Data.Adnot
import           Data.Sequence (Seq)
import           Data.Text (Text)
import           Data.Word (Word8)
import           Lens.Family2.TH

-- | XXX: This is a temporary definition of 'Oscillator' for early
--   prototyping purposes.
data Oscillator
  = OscSine
  | OscSquare
    deriving (Eq, Show)

-- | XXX: This is a temporary definition of 'Instrument' for early
--   prototyping purposes.
data Instrument = Instrument
  { _instrSource :: Oscillator
  } deriving (Eq, Show)

$(makeLenses ''Instrument)


-- | We'll maintain a list of instruments and refer to them using
--   indices. For type safety, here is a wrapper around those
--   indices.
newtype InstrRef = InstrRef { _fromInstrRef :: Int }
  deriving (Eq, Show)

$(makeLenses ''InstrRef)

-- | A 'Note' here is an individual element of a scale, which we'll
--   maintain a unique list of on a per-song basis, and most of the time
--   we'll use indices into that list. A 'Note' has a frequency represented
--   in cents and an appearance that the user will see when running the
--   program, which should be no more than a few characters long.
data Note = Note
  { _noteCents      :: Double
  , _noteAppearance :: Text
  , _noteColor      :: Maybe Text
  } deriving (Eq, Show)

instance FromAdnot Note where
  parseAdnot = withSum "Note" go
    where go "note" [name, cents] =
            Note <$> parseAdnot cents <*> parseAdnot name <*> pure Nothing
          go "note" [name, cents, color] =
            Note <$> parseAdnot cents
                 <*> parseAdnot name
                 <*> (Just <$> parseAdnot color)
          go "note" _ = fail "Unknown argument structure"
          go c _ = fail ("Expected note, got " ++ show c)

$(makeLenses ''Note)

-- | We'll maintain a list of notes and refer to them using indices. For type
--   safety, here is a wrapper around those indices.
newtype NoteRef = NoteRef { _fromNoteRef :: Int }
  deriving (Eq, Show)

$(makeLenses ''NoteRef)

-- | A 'Scale' has a name, a total number of cents (which will almost always be
--   1200 for traditional scales) and a list of notes associated with it.
data Scale = Scale
  { _scaleName       :: Text
  , _scaleTotalCents :: Double
  , _scaleNotes      :: Seq Note
  } deriving (Eq, Show)

instance FromAdnot Scale where
  parseAdnot = withProduct "Scale" $ \o ->
    Scale <$> o .: "name"
          <*> o .: "size"
          <*> o .: "notes"

$(makeLenses ''Scale)

-- | An 'Event' is a typical event associated with a song.
data Event = Event
  deriving (Eq, Show)

data Beats
  = BeatsSimple Word8
  | BeatsAdditive [Word8]
  | BeatsFractional Word8 Word8
    deriving (Eq, Show)

$(makeTraversals ''Beats)


data Signature = Signature
  { _sigPerBar   :: Beats
  , _sigBeatUnit :: Word8
  } deriving (Eq, Show)

$(makeLenses ''Signature)


data TrackChunk = TrackChunk
  { _tcSignature :: Signature
  } deriving (Eq, Show)


data Track = Track
  {
  } deriving (Eq, Show)


data Song = Song
  { _songScale  :: Scale
  , _songTracks :: Seq Track
  } deriving (Eq, Show)

$(makeLenses ''Song)
