module Music.Theory.Pitch where

import Music.Theory.Set
import Data.Maybe
import Data.List

-- | Modulo twelve.
mod12 :: (Integral a) => a -> a
mod12 = (`mod` 12)

-- | Pitch class.
pc :: (Integral a) => a -> a
pc = mod12

-- | Map to pitch-class and reduce to set.
pcset :: (Integral a) => [a] -> [a]
pcset = set . map pc

-- | Transpose by n.
tn :: (Integral a) => a -> [a] -> [a]
tn n = map (pc . (+ n))

-- | Transpose so first element is n.
transposeTo :: (Integral a) => a -> [a] -> [a]
transposeTo _ [] = []
transposeTo n (x:xs) = n : tn (n - x) xs

-- | All transpositions.
transpositions :: (Integral a) => [a] -> [[a]]
transpositions p = map (`tn` p) [0..11]

-- | Invert about n.
invert :: (Integral a) => a -> [a] -> [a]
invert n = map (pc . (\p -> n - (p - n)))

-- | Invert about first element.
invertSelf :: (Integral a) => [a] -> [a]
invertSelf [] = []
invertSelf (x:xs) = invert x (x:xs)

-- | Composition of inversion about zero and transpose.
tni :: (Integral a) => a -> [a] -> [a]
tni n = tn n . invert 0

-- | Rotate left by n places.
rotate :: (Integral n) => n -> [a] -> [a]
rotate n p =
    let m = n `mod` genericLength p
        (b, a) = genericSplitAt m p
    in a ++ b

-- | Rotate right by n places.
rotate_right :: (Integral n) => n -> [a] -> [a]
rotate_right = rotate . negate

-- | All rotations.
rotations :: [a] -> [[a]]
rotations p = map (`rotate` p) [0 .. length p - 1]

-- | Modulo 12 multiplication
mn :: (Integral a) => a -> [a] -> [a]
mn n = map (pc . (* n))

-- | M5
m5 :: (Integral a) => [a] -> [a]
m5 = mn 5

all_Tn :: (Integral a) => [a] -> [[a]]
all_Tn p = map (`tn` p) [0..11]

all_TnI :: (Integral a) => [a] -> [[a]]
all_TnI p =
    let ps = all_Tn p 
    in ps ++ map (invert 0) ps

all_RTnI :: (Integral a) => [a] -> [[a]]
all_RTnI p =
    let ps = all_TnI p
    in ps ++ map reverse ps

all_TnMI :: (Integral a) => [a] -> [[a]]
all_TnMI p =
    let ps = all_TnI p
    in ps ++ map m5 ps

all_RTnMI :: (Integral a) => [a] -> [[a]]
all_RTnMI p =
    let ps = all_TnMI p
    in ps ++ map reverse ps

all_rRTnMI :: (Integral a) => [a] -> [[a]]
all_rRTnMI = map snd . sros

-- | Serial Operator, of the form rRTMI.
data SRO a = SRO a Bool a Bool Bool
             deriving (Eq, Show)

-- | Serial operation.
sro :: (Integral a) => SRO a -> [a] -> [a]
sro (SRO r r' t m i) x =
    let x1 = if i then invert 0 x else x
        x2 = if m then m5 x1 else x1
        x3 = tn t x2
        x4 = if r' then reverse x3 else x3
    in rotate r x4

-- | The total set of serial operations.
sros :: (Integral a) => [a] -> [(SRO a, [a])]
sros x = [ let o = (SRO r r' t m i) in (o, sro o x) | 
           r <- [0 .. genericLength x - 1], 
           r' <- [False, True], 
           t <- [0 .. 11], 
           m <- [False, True], 
           i <- [False, True] ]

sro_Tn :: (Integral a) => [SRO a]
sro_Tn = [ SRO 0 False n False False | 
           n <- [0..11] ]

sro_TnI :: (Integral a) => [SRO a]
sro_TnI = [ SRO 0 False n False i | 
            n <- [0..11], 
            i <- [False, True] ]

sro_RTnI :: (Integral a) => [SRO a]
sro_RTnI = [ SRO 0 r n False i | 
             r <- [True, False],
             n <- [0..11], 
             i <- [False, True] ] 

sro_TnMI :: (Integral a) => [SRO a]
sro_TnMI = [ SRO 0 False n m i | 
             n <- [0..11], 
             m <- [True, False], 
             i <- [True, False] ]

sro_RTnMI :: (Integral a) => [SRO a]
sro_RTnMI = [ SRO 0 r n m i | 
              r <- [True, False],
              n <- [0..11],
              m <- [True, False],
              i <- [True, False] ]

-- | Intervals to values, zero is n.
dx_d :: (Num a) => a -> [a] -> [a]
dx_d = scanl (+)

-- | Integrate.
d_dx :: (Num a) => [a] -> [a]
d_dx [] = []
d_dx (_:[]) = []
d_dx (x:xs) = zipWith (-) xs (x:xs)

-- | Morris INT operator.
int :: (Integral a) => [a] -> [a]
int = map mod12 . d_dx

-- | Interval class.
ic :: (Integral a) => a -> a
ic i =
    let i' = mod12 i
    in if i' <= 6 then i' else 12 - i'

-- | Elements of p not in q
difference :: (Eq a) => [a] -> [a] -> [a]
difference p q =
    let f e = e `notElem` q
    in filter f p

-- | Pitch classes not in set.
complement :: (Integral a) => [a] -> [a]
complement = difference [0..11]

-- | Is p a subsequence of q.
subsequence :: (Eq a) => [a] -> [a] -> Bool
subsequence = isInfixOf

-- | The standard t-matrix of p.
tmatrix :: (Integral a) => [a] -> [[a]]
tmatrix p = map (`tn` p) (transposeTo 0 (invertSelf p))

-- | Interval class vector.
icv :: (Integral a) => [a] -> [a]
icv s =
    let i = map (ic . uncurry (-)) (dyads s)
        j = map f (group (sort i))
        k = map (`lookup` j) [1..6]
        f l = (head l, genericLength l)
    in map (fromMaybe 0) k

-- | Is p a subset of q.
is_subset :: Eq a => [a] -> [a] -> Bool
is_subset p q = p `intersect` q == p

-- | Is p a superset of q.
is_superset :: Eq a => [a] -> [a] -> Bool
is_superset = flip is_subset