-- |  The Interval module implements diatonic intervals.
module Music.Diatonic.Interval (
    Interval(
        Unison,Min2nd,Maj2nd,Min3rd,Maj3rd,Perf4th,
        Perf5th,Min6th,Maj6th,Min7th,Maj7th
      ), augment, diminish, 
      steps, semitones
  ) where


import Music.Diatonic.Quality


-- | Use these constructors to create 'Interval's. To alter them, use the 'diminish' or 'augment' functions.
data Interval = Unison | Min2nd | Maj2nd | Min3rd | Maj3rd | Perf4th
              | Perf5th | Min6th | Maj6th | Min7th | Maj7th 
              | Aug Interval | Dim Interval
              deriving (Eq)


instance Show Interval where
  show Unison = "P1" ; show Min2nd = "m2" ; show Maj2nd = "M2" ; show Min3rd = "m3"
  show Maj3rd = "M3" ; show Perf4th = "P4" ; show Perf5th = "P5" ; show Min6th = "m6"
  show Maj6th = "M6" ; show Min7th = "m7" ; show Maj7th = "M7" 
  show (Aug i@(Aug _)) = "A" ++ show i
  show (Aug i) = "A" ++ (tail . show $ i)
  show (Dim i@(Dim _)) = "d" ++ show i
  show (Dim i) = "d" ++ (tail . show $ i)


instance Qual Interval where
  quality i | i `elem` [Maj2nd, Maj3rd, Maj6th, Maj7th] = Major
  quality i | i `elem` [Min2nd, Min3rd, Min6th, Min7th] = Minor
  quality i | i `elem` [Perf4th, Perf5th] = Perfect
  quality (Aug i) = Augmented 
  quality (Dim i) = Diminished 


-- | Augments an 'Interval' by a semitone. The interval type remains the same.
augment :: Interval -> Interval
augment (Dim i) = i
augment Min2nd = Maj2nd ; augment Min3rd = Maj3rd
augment Min6th = Maj6th ; augment Min7th = Maj7th
augment i = Aug i


-- | Diminishes an 'Interval' by a semitone. The interval type remains the same.
diminish :: Interval -> Interval
diminish (Aug i) = i
diminish Maj2nd = Min2nd ; diminish Maj3rd = Min3rd
diminish Maj6th = Min6th ; diminish Maj7th = Min7th
diminish i = Dim i

-- | Returns the number of scale steps in an 'Interval'.
steps :: Interval -> Int
steps Unison = 0 ; steps Min2nd  = 1  ; steps Maj2nd  = 1  ; steps Min3rd = 2 
steps Maj3rd = 2 ; steps Perf4th = 3  ; steps Perf5th = 4  ; steps Min6th = 5 
steps Maj6th = 5 ; steps Min7th  = 6  ; steps Maj7th  = 6
steps (Aug i) = steps i
steps (Dim i) = steps i


-- | Returns the number of semitones in an 'Interval'.
semitones :: Interval -> Int
semitones Unison = 0 ; semitones Min2nd  = 1  ; semitones Maj2nd  = 2  ; semitones Min3rd = 3 
semitones Maj3rd = 4 ; semitones Perf4th = 5  ; semitones Perf5th = 7  ; semitones Min6th = 8 
semitones Maj6th = 9 ; semitones Min7th  = 10 ; semitones Maj7th  = 11 
semitones (Aug i) = semitones i + 1
semitones (Dim i) = semitones i - 1