-- Copyright 2023 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

{- | Support for Hindustani style bols for pakhawaj and tabla.

    It uses the same konnakol / solkattu framework, but of course the
    match is not perfect.  I don't know how much difference because I don't
    know much about Hindustani, but at least these things:

    - Structures are different.  At least there is simple x3 in tihais,
    though they seem to think of them as simply x3 rather than
    seq (gap) seq (gap) seq.

    - On the other hand, there's a notion of structured variation that doesn't
    seem to be present in Carnatic.  It may be too ad-hoc to usefully notate.
    variation by taking from one place and adding to another.  I have
    incomplete similar notions for Carnatic in combinators like "varying"
    and replacements, but those things are probably better done on the fly than
    notated.

    - Hindustani thinks of gaps as "only the space" rather than "karvai stroke
    and space".  This means that "dha.__4" would look like 5 matras from a
    Hindostani point of view.

    - Notion of kali is in Hindustani, but not clear if it could have any
    useful notation presence.

    - Talas are all different.  At the least they need different ruler symbols.

    - Stricter relationship between bols and strokes.  I can probably have a
    single hardcoded stroke map, but beyond that I don't need to realize to
    strokes at all for reading.  It would still be useful for machine
    realization.  However, there is at least some variation between
    instruments, e.g. tabla typically doesn't use the "chapu" stroke, so
    pakhawaj dha is like thom + chapu while tabla is like thom + din.  When
    realizing bols on mridangam, I can use the pakhawaj style.
-}
module Solkattu.Bol (
    Bol(..)
    , parseBols
    , Bols(..)
) where
import qualified Data.Text as Text

import qualified Util.Lists as Lists
import qualified Derive.Expr as Expr
import qualified Solkattu.Instrument.Tabla as Tabla
import qualified Solkattu.Realize as Realize
import qualified Solkattu.Solkattu as Solkattu

import           Global


-- | Text representation of a Bol.
type BolT = Text

type Error = Text

data Bol =
    Dha | Dhet |Dhom | Di | Din | Dhi | Dhin | Dhit | Ga | Gi | Ge | Ghen
    | Ka | Kat | Kre | Ki | Na | Ne | Ra | Ri | Ran
    | Ta | Tak | Taa | Te | Tet | Ti | Tu
    | Kran -- TODO 2 beats?
    deriving (Bol -> Bol -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Bol -> Bol -> Bool
$c/= :: Bol -> Bol -> Bool
== :: Bol -> Bol -> Bool
$c== :: Bol -> Bol -> Bool
Eq, Eq Bol
Bol -> Bol -> Bool
Bol -> Bol -> Ordering
Bol -> Bol -> Bol
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Bol -> Bol -> Bol
$cmin :: Bol -> Bol -> Bol
max :: Bol -> Bol -> Bol
$cmax :: Bol -> Bol -> Bol
>= :: Bol -> Bol -> Bool
$c>= :: Bol -> Bol -> Bool
> :: Bol -> Bol -> Bool
$c> :: Bol -> Bol -> Bool
<= :: Bol -> Bol -> Bool
$c<= :: Bol -> Bol -> Bool
< :: Bol -> Bol -> Bool
$c< :: Bol -> Bol -> Bool
compare :: Bol -> Bol -> Ordering
$ccompare :: Bol -> Bol -> Ordering
Ord, Int -> Bol
Bol -> Int
Bol -> [Bol]
Bol -> Bol
Bol -> Bol -> [Bol]
Bol -> Bol -> Bol -> [Bol]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Bol -> Bol -> Bol -> [Bol]
$cenumFromThenTo :: Bol -> Bol -> Bol -> [Bol]
enumFromTo :: Bol -> Bol -> [Bol]
$cenumFromTo :: Bol -> Bol -> [Bol]
enumFromThen :: Bol -> Bol -> [Bol]
$cenumFromThen :: Bol -> Bol -> [Bol]
enumFrom :: Bol -> [Bol]
$cenumFrom :: Bol -> [Bol]
fromEnum :: Bol -> Int
$cfromEnum :: Bol -> Int
toEnum :: Int -> Bol
$ctoEnum :: Int -> Bol
pred :: Bol -> Bol
$cpred :: Bol -> Bol
succ :: Bol -> Bol
$csucc :: Bol -> Bol
Enum, Bol
forall a. a -> a -> Bounded a
maxBound :: Bol
$cmaxBound :: Bol
minBound :: Bol
$cminBound :: Bol
Bounded, Int -> Bol -> ShowS
[Bol] -> ShowS
Bol -> [Char]
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [Bol] -> ShowS
$cshowList :: [Bol] -> ShowS
show :: Bol -> [Char]
$cshow :: Bol -> [Char]
showsPrec :: Int -> Bol -> ShowS
$cshowsPrec :: Int -> Bol -> ShowS
Show)

instance Solkattu.Notation Bol where
    notation :: Bol -> (Style, BolT)
notation = BolT -> (Style, BolT)
Solkattu.textNotation forall b c a. (b -> c) -> (a -> b) -> a -> c
. BolT -> BolT
Text.toLower forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> BolT
showt
instance Pretty Bol where pretty :: Bol -> BolT
pretty = forall a. Notation a => a -> BolT
Solkattu.notationText

instance Expr.ToExpr Bol where
    to_expr :: Bol -> Expr MiniVal
to_expr = forall val. Symbol -> Expr val
Expr.generator0 forall b c a. (b -> c) -> (a -> b) -> a -> c
. BolT -> Symbol
Expr.Symbol forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Pretty a => a -> BolT
pretty
instance Expr.ToExpr (Realize.Stroke Bol) where
    to_expr :: Stroke Bol -> Expr MiniVal
to_expr = forall a. ToExpr a => Stroke a -> Expr MiniVal
Realize.toExpr

parseBols :: BolT -> Either Error [Maybe Bols]
parseBols :: BolT -> Either BolT [Maybe Bols]
parseBols = forall sollu.
Show sollu =>
[(BolT, sollu)] -> BolT -> Either BolT [Maybe sollu]
Solkattu.parseSyllables [(BolT, Bols)]
allBols

allBols :: [(BolT, Bols)]
allBols :: [(BolT, Bols)]
allBols = [(BolT, Bols)]
sequences
    forall a. [a] -> [a] -> [a]
++ forall a b. (a -> b) -> [a] -> [b]
map (forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second Bol -> Bols
S1) (forall a k. (a -> k) -> [a] -> [(k, a)]
Lists.keyOn forall a. Notation a => a -> BolT
Solkattu.notationText [forall a. Bounded a => a
minBound ..])

-- | parseBols can return 2nd speed sequences.  This gets turned into a
-- Sequence in Dsl.Bol.
data Bols = S1 Bol | S2 Bol Bol
    deriving (Int -> Bols -> ShowS
[Bols] -> ShowS
Bols -> [Char]
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [Bols] -> ShowS
$cshowList :: [Bols] -> ShowS
show :: Bols -> [Char]
$cshow :: Bols -> [Char]
showsPrec :: Int -> Bols -> ShowS
$cshowsPrec :: Int -> Bols -> ShowS
Show)

sequences :: [(BolT, Bols)]
sequences :: [(BolT, Bols)]
sequences =
    [ (BolT
"tr", Bol -> Bol -> Bols
S2 Bol
Ti Bol
Ra)
    , (BolT
"kt", Bol -> Bol -> Bols
S2 Bol
Ki Bol
Ta)
    , (BolT
"tk", Bol -> Bol -> Bols
S2 Bol
Ta Bol
Ka)
    ]

-- TODO
bolMap :: Realize.SolluMap Bol Tabla.Stroke
Right (SolluMap Bol Stroke
bolMap, [(SolluMapKey Bol, [Maybe (Stroke Stroke)])]
extras) = forall stroke sollu g.
(Pretty stroke, Pretty sollu, Ord sollu) =>
[(Sequence g (Note sollu), [SNote stroke])]
-> Either
     BolT
     (SolluMap sollu stroke,
      [(SolluMapKey sollu, [Maybe (Stroke stroke)])])
Realize.solluMap []

{-
-- Single strokes.
single_bols :: [([Syllable], Bol)]
single_bols =
    [ (["tet"], One Tet)
    , (["te"], One Te)
    , (["ne", "re"], One Ne)
    , (["na"], One Na)
    , (["ta"], One Ta)
    , (["di", "din"], One Di)
    -- bayan
    , (["ka", "kat", "ki"], One Ka)
    , (["ge", "gen", "ga"], One Ge)
    -- both
    , (["dha"], Together Ge Ta)
    , (["dhin"], Together Ge Di)
    , (["dhet"], Together Ge Tette)
    ]

-- kre: p+k
-- dhet: ok
-- dhi: dha
-- dhom: dha

-- TODO the length of the syllables should be the same as the length of the
-- bols or the extras will be silently dropped.
sequences :: [([BoolT], [Note Bol])]
sequences =
    [ (["kre"], note $ Flam Ka Tet)
    , (["gre"], note $ Flam Ge Tet)
    , (["te", "re", "ki", "ta"], notes [Tet, Te, Ka, Tet])
    , (["ki", "ta", "ta", "ka"], notes [Tet, Te, Ka, Tet])
    , (["tr", "kt"], notes2 [[Tet, Te], [Ka, Tet]])
    , (["te", "re", "ki", "ta", "ta", "ka"], notes [Tet, Te, Ka, Tet, Te, Ka])
    , (["tr", "kt", "tk"], notes2 [[Tet, Te], [Ka, Tet], [Te, Ka]])
    , (["kt", "tk"], notes2 [[Tet, Te], [Ka, Tet]])
    , (["ta", "ki"], notes [Tet, Ka])
    , (["te", "ran"], notes [Di3, Di1])
    , (["dhu", "ma"], map Note [Together Ge Di, One Te])
    -- Abbreviations.
    , (["tetekata"], [Notes $ notes [Tet, Te, Ka, Ta, Ge, Di, Ge, Ne]])
    ]
    where
    note = (:[]) . Note
    notes = map (Note . One)
    notes2 = map (Notes . notes)
-}