-- Copyright 2017 Evan Laforge
-- This program is distributed under the terms of the GNU General Public
-- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt

-- | The 'Tala' type, which describes a Carnatic tala.
module Solkattu.Tala where
import qualified Data.Map as Map
import qualified Data.Text as Text

import qualified Util.Num as Num

import           Global


-- | An akshara is one count of the talam.
type Akshara = Int

anga_aksharas :: Jati -> Anga -> Akshara
anga_aksharas :: Jati -> Anga -> Jati
anga_aksharas Jati
jati Anga
anga = case Anga
anga of
    Clap Jati
n -> Jati
n
    Wave Jati
n -> Jati
n
    Anga
I -> Jati
jati
    Anga
O -> Jati
2
    Anga
U -> Jati
1

anga_labels :: Jati -> Anga -> [Text]
anga_labels :: Jati -> Anga -> [Text]
anga_labels Jati
jati Anga
anga = case Anga
anga of
    Clap Jati
n -> Text
"X" Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: Jati -> Text -> [Text]
forall a. Jati -> a -> [a]
replicate (Jati
nJati -> Jati -> Jati
forall a. Num a => a -> a -> a
-Jati
1) Text
"-"
    Wave Jati
n -> Text
"O" Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: Jati -> Text -> [Text]
forall a. Jati -> a -> [a]
replicate (Jati
nJati -> Jati -> Jati
forall a. Num a => a -> a -> a
-Jati
1) Text
"-"
    Anga
I -> Jati -> [Text] -> [Text]
forall a. Jati -> [a] -> [a]
take Jati
jati ((Integer -> Text) -> [Integer] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> Text
forall a. Show a => a -> Text
showt [Integer
0..])
    Anga
O -> [Text
"X", Text
"O"]
    Anga
U -> [Text
"X"]

tala_angas :: Tala -> [Akshara]
tala_angas :: Tala -> [Jati]
tala_angas Tala
tala = (Anga -> Jati) -> [Anga] -> [Jati]
forall a b. (a -> b) -> [a] -> [b]
map (Jati -> Anga -> Jati
anga_aksharas (Tala -> Jati
_jati Tala
tala)) (Tala -> [Anga]
_angas Tala
tala)

tala_aksharas :: Tala -> Akshara
tala_aksharas :: Tala -> Jati
tala_aksharas = [Jati] -> Jati
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
Num.sum ([Jati] -> Jati) -> (Tala -> [Jati]) -> Tala -> Jati
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Tala -> [Jati]
tala_angas

tala_labels :: Tala -> [Text]
tala_labels :: Tala -> [Text]
tala_labels Tala
tala = (Anga -> [Text]) -> [Anga] -> [Text]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Jati -> Anga -> [Text]
anga_labels (Tala -> Jati
_jati Tala
tala)) (Tala -> [Anga]
_angas Tala
tala)

data Tala = Tala {
    Tala -> Text
_name :: !Text
    , Tala -> [Anga]
_angas :: ![Anga]
    , Tala -> Jati
_jati :: !Jati
    } deriving (Jati -> Tala -> ShowS
[Tala] -> ShowS
Tala -> String
(Jati -> Tala -> ShowS)
-> (Tala -> String) -> ([Tala] -> ShowS) -> Show Tala
forall a.
(Jati -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Tala] -> ShowS
$cshowList :: [Tala] -> ShowS
show :: Tala -> String
$cshow :: Tala -> String
showsPrec :: Jati -> Tala -> ShowS
$cshowsPrec :: Jati -> Tala -> ShowS
Show, Tala -> Tala -> Bool
(Tala -> Tala -> Bool) -> (Tala -> Tala -> Bool) -> Eq Tala
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Tala -> Tala -> Bool
$c/= :: Tala -> Tala -> Bool
== :: Tala -> Tala -> Bool
$c== :: Tala -> Tala -> Bool
Eq)

instance Pretty Tala where pretty :: Tala -> Text
pretty = Tala -> Text
forall a. Show a => a -> Text
showt

tala_name :: Tala -> Text
tala_name :: Tala -> Text
tala_name Tala
tala
    | Tala
tala Tala -> Tala -> Bool
forall a. Eq a => a -> a -> Bool
== Tala
adi_tala = Text
"adi"
    | Tala -> [Anga]
_angas Tala
tala [Anga] -> [Anga] -> Bool
forall a. Eq a => a -> a -> Bool
== [Anga
I] = Jati -> Text
forall a. Show a => a -> Text
showt (Tala -> Jati
_jati Tala
tala) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" beats"
    | Tala -> Jati
_jati Tala
tala Jati -> Jati -> Bool
forall a. Eq a => a -> a -> Bool
== Jati
0 = Tala -> Text
_name Tala
tala -- chapu talams don't have jati
    | Bool
otherwise = [Text] -> Text
Text.unwords
        [ Tala -> Text
_name Tala
tala
        , Text -> Jati -> Map Jati Text -> Text
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault (Jati -> Text
forall a. Show a => a -> Text
showt (Tala -> Jati
_jati Tala
tala)) (Tala -> Jati
_jati Tala
tala) Map Jati Text
jatis
        , Text
"jati"
        ]
    where
    jatis :: Map Jati Text
jatis = [(Jati, Text)] -> Map Jati Text
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
        [ (Jati
3, Text
"tisra")
        , (Jati
4, Text
"chatusra")
        , (Jati
5, Text
"kanda")
        , (Jati
9, Text
"sankirna")
        ]

data Anga = Clap !Akshara | Wave !Akshara
    -- | laghu, drutam, anudrutam
    | I | O | U
    deriving (Jati -> Anga -> ShowS
[Anga] -> ShowS
Anga -> String
(Jati -> Anga -> ShowS)
-> (Anga -> String) -> ([Anga] -> ShowS) -> Show Anga
forall a.
(Jati -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Anga] -> ShowS
$cshowList :: [Anga] -> ShowS
show :: Anga -> String
$cshow :: Anga -> String
showsPrec :: Jati -> Anga -> ShowS
$cshowsPrec :: Jati -> Anga -> ShowS
Show, Anga -> Anga -> Bool
(Anga -> Anga -> Bool) -> (Anga -> Anga -> Bool) -> Eq Anga
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Anga -> Anga -> Bool
$c/= :: Anga -> Anga -> Bool
== :: Anga -> Anga -> Bool
$c== :: Anga -> Anga -> Bool
Eq)

instance Pretty Anga where pretty :: Anga -> Text
pretty = Anga -> Text
forall a. Show a => a -> Text
showt

type Jati = Int

dhruva, matya, rupaka, jhampa, triputa, ata, eka :: [Anga]
dhruva :: [Anga]
dhruva = [Anga
I, Anga
O, Anga
I, Anga
I]
matya :: [Anga]
matya = [Anga
I, Anga
O, Anga
I]
rupaka :: [Anga]
rupaka = [Anga
O, Anga
I]
jhampa :: [Anga]
jhampa = [Anga
I, Anga
U, Anga
O]
triputa :: [Anga]
triputa = [Anga
I, Anga
O, Anga
O]
ata :: [Anga]
ata = [Anga
I, Anga
I, Anga
O, Anga
O]
eka :: [Anga]
eka = [Anga
I]

-- | Talas with default jati.
dhruva_tala, matya_tala, rupaka_tala, jhampa_tala, triputa_tala, ata_tala,
    eka_tala :: Tala
dhruva_tala :: Tala
dhruva_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"dhruva" [Anga]
dhruva Jati
4
matya_tala :: Tala
matya_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"matya" [Anga]
matya Jati
4
rupaka_tala :: Tala
rupaka_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"rupaka" [Anga]
rupaka Jati
4
jhampa_tala :: Tala
jhampa_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"jhampa" [Anga]
jhampa Jati
7
triputa_tala :: Tala
triputa_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"triputa" [Anga]
triputa Jati
3
ata_tala :: Tala
ata_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"ata" [Anga]
ata Jati
5
eka_tala :: Tala
eka_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"eka" [Anga]
eka Jati
4

adi_tala :: Tala
adi_tala :: Tala
adi_tala = Text -> [Anga] -> Jati -> Tala
Tala Text
"adi" [Anga]
triputa Jati
4 -- chatusra jati triputa tala

misra_chapu :: Tala
misra_chapu :: Tala
misra_chapu = Text -> [Anga] -> Jati -> Tala
Tala Text
"misra chapu" [Jati -> Anga
Wave Jati
1, Jati -> Anga
Wave Jati
2, Jati -> Anga
Clap Jati
2, Jati -> Anga
Clap Jati
2] Jati
0
    -- These have no laghu, so jati doesn't matter.

kanda_chapu :: Tala
kanda_chapu :: Tala
kanda_chapu = Text -> [Anga] -> Jati -> Tala
Tala Text
"kanda chapu" [Jati -> Anga
Clap Jati
2, Jati -> Anga
Clap Jati
1, Jati -> Anga
Clap Jati
2] Jati
0

rupaka_fast :: Tala
rupaka_fast :: Tala
rupaka_fast = Text -> [Anga] -> Jati -> Tala
Tala Text
"rupaka" [Jati -> Anga
Clap Jati
1, Jati -> Anga
Clap Jati
1, Jati -> Anga
Wave Jati
1] Jati
0

-- | A special "tala" that just counts beats and turns off alignment checking.
-- I use 4 beats just to get a nice wrapping, but I should do wrapping
-- automatically.
any_beats :: Tala
any_beats :: Tala
any_beats = Text -> [Anga] -> Jati -> Tala
Tala Text
"any beats" [Anga
I] Jati
4

-- | For a fragment which fits a certain number of beats.
beats :: Akshara -> Tala
beats :: Jati -> Tala
beats Jati
aksharas = Text -> [Anga] -> Jati -> Tala
Tala Text
"beats" [Anga
I] Jati
aksharas