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

-- | Rulers for Balinese and Javanese music.
module Cmd.Ruler.Gong (
    Gongs, Jegogans
    , regular, until
    , measure_dur
    , meter
    , config
    -- * java
    , java
) where
import           Prelude hiding (until)
import qualified Data.Set as Set

import qualified Ui.Meter.Meter as Meter

import           Global
import           Types


type Gongs = Int
type Jegogans = Int

{- | Create a number of gongs, each divided into a number of jegogan strokes.

    Labels start from 0, where 0 represents the last note.  So 0, 1, 2, 3, 4,
    5, 6, 7 can be read 8, 1, 2, 3, 4, 5, 6, 7, and in a 16 count cycle the the
    8 will be on the middle count as expected.

    + lines have labels, and 4 jegogan per gong:

    @
          01234567012345670123456701234567012345670123456701234567012345670
    + s 2 kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk 1/8t
    . e 2 k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k
    . q 2 k   k   k   k   k   k   k   k   k   k   k   k   k   k   k   k   k 1/2t
    + h 2 c       c       c       c       c       c       c       c       c 1t
    . w 4 J               J               J               J               J 2t
    .     o                               p                               o
    + Sec O                                                               O
          0       1       2       3       4       5       6       7       8
        t 0                               1                               2
    @

    So it needs 4 "measures" in one section to make a full 8 count.
-}
regular :: Gongs -> Jegogans -> Meter.Meter
regular :: Gongs -> Gongs -> Meter
regular Gongs
gongs Gongs
jegogans = Config -> [MSection] -> Meter
Meter.meter Config
config (forall a. Gongs -> a -> [a]
replicate Gongs
gongs MSection
section)
    where section :: MSection
section = Gongs -> TrackTime -> AbstractMeter -> MSection
Meter.MSection Gongs
jegogans TrackTime
measure_dur AbstractMeter
meter

until :: TrackTime -> Meter.Meter
until :: TrackTime -> Meter
until TrackTime
end =
    ([MSection] -> [MSection]) -> Meter -> Meter
Meter.modify_sections (TrackTime -> [MSection] -> [MSection]
Meter.sections_take TrackTime
end) forall a b. (a -> b) -> a -> b
$ Gongs -> Gongs -> Meter
regular Gongs
gongs Gongs
jegogans
    where
    jegogans :: Gongs
jegogans = Gongs
4
    gongs :: Gongs
gongs = forall a b. (RealFrac a, Integral b) => a -> b
ceiling forall a b. (a -> b) -> a -> b
$ TrackTime
end forall a. Fractional a => a -> a -> a
/ (forall a b. (Integral a, Num b) => a -> b
fromIntegral Gongs
jegogans forall a. Num a => a -> a -> a
* TrackTime
measure_dur)

-- | This gives a reasonable kotekan speed at tempo=1.  It makes kotekan into
-- "s" with one "cycle" of 8 as a "h".  A "w" is 2 cycles and 8 per section.
measure_dur :: TrackTime
measure_dur :: TrackTime
measure_dur = TrackTime
2

meter :: Meter.AbstractMeter
meter :: AbstractMeter
meter = [Gongs] -> AbstractMeter
Meter.regular_subdivision [Gongs
2, Gongs
2, Gongs
2, Gongs
2, Gongs
2, Gongs
2]

-- | Gong config starts counting from 0.  This is more appropriate for Balinese
-- and Javenese music which are counted n 1 2 3 .. n
config :: Meter.Config
config :: Config
config = Meter.Config
    { config_labeled_ranks :: Set Rank
config_labeled_ranks = Set Rank
labeled_ranks
    , config_label :: LabelConfig
config_label = Gongs -> LabelConfig
Meter.BigNumber Gongs
0
    , config_start_measure :: Gongs
config_start_measure = Gongs
1
    , config_min_depth :: Gongs
config_min_depth = Gongs
1
    , config_strip_depth :: Gongs
config_strip_depth = Gongs
2
    }

labeled_ranks :: Set Meter.Rank
labeled_ranks :: Set Rank
labeled_ranks = forall a. Ord a => [a] -> Set a
Set.fromList [Rank
Meter.Section, Rank
Meter.H, Rank
Meter.S, Rank
Meter.T128]
    -- Section: gong, W: gong stroke, H: jegog, Q: calung, E: kotekan*2,
    -- S: kotekan*4, ...

-- * java

{-
        t 0               1               2               3               1 4t
    + Sec 1                                                               2
    + w 4 gatra           1               2               3               4 1t
    . h 2 ir1     .       .       .       .       .       .       .       . 1/2t
    + q 2 ir2 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . 1/4t
    . e 2 ir3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1/8t
    + s 2 ir4 .............................................................
          01234567012345670123456701234567012345670123456701234567012345670
    gong    8x gatra
    kenong  2x gatra, or 4x gatra
            S   2t      gatra
    gatra   w   1t
            h   1/2t    note
    note    q   1/4t    ir1
    ir1     e   1/8     ir2
    ir2     s   1/16    ir3
    ir3     t32 1/32    ir4
    ir4     t64 1/64

        cmd ts  subdiv T    function    peking panerus
    +   1   S   n      n    kenong/pul
    +   2   w   2      1    gatra
        3   h   2      1/2
    +   4   q   2      1/4  note        lancar
        5   e   2      1/8              ir1
    +   6   s   2      1/16             ir2     ir1
        7   t32 2      1/32             ir3     ir2
        8   t64 2      1/64             ir4     ir3
            t128 2     1/128                    ir4

        cmd ts  subdiv T    function    peking panerus
    +   1   S   4      4    gatra
    +   2   w   2      1    note        lancar
        3   h   2      1/2              ir1
    +   4   q   2      1/4              ir2
        5   e   2      1/8              ir3
    +   6   s   2      1/16             ir4
        7   t32 2      1/32
        8   t64        1/64

        cmd ts  subdiv T    function    panerus
    +   1   S   n      n    kenong/pul
    +   2   w   4      1    gatra
        3   h   2      1/4  ir1 peking
    +   4   q   2      1/8              ir1
        5   e   2      1/16             ir2
    +   6   s   2      1/32             ir3
        7   t32 2      1/64             ir4
        8   t64        1/128

    This way I can get the range down to 128, but is it awkward to have a 1/4
    right at the top?  I will surely have notes moving at that speed.

    It's difficult if I expect to keep a constant meter across such a wide
    tempo range.  Alternately, I could expect we 2x the tempo at some point.
    What if I 2x tempo at every irama change, and instead keep the meter
    constant per irama?  Gatra length will then change.

    This won't track if I use the same balungan, but that doesn't seem like a
    big deal, except where the rulers won't line up.

    Tempo jumps for irama, timestep relative to irama would be say peking is
    always 1/8.  Panerus / gambang at 1/16.

                tempo   peking  note        gatra
        lancar  .5      e       1/8t e      1/2t h
        ir1     .5      e       1/4t q        1t w
        ir2     .5      e       1/2t h        2  w2
        ir3     .5      e         1t w        4  w4
        ir4     .5      e         2t w2       8  w8

    Tempo continuous, timestep absolute would say 1 gatra = 1t, and peking
    changes per irama:

                tempo   peking  gp  note    gatra
        lancar  1       q        e  1/4t q  1t w
        ir1     .5      e        s  1/4t q  1t w
        ir2     .25     s      t32  1/4t q  1t w
        ir3     .125    t32    t64  1/4t q  1t w
        ir4     .0625   t64   t128  1/4t q  1t w

    This is unworkable, I don't have t128 and I could see going to t256.  Also
    tempos wind up very small if I stick to 1 = 1t = 1s.  If I say one note is
    1t then tempos are x4 and more reasonable.  Rulers get bigger if I need
    to go down to 1/256... though it would mean I only need one kind which is
    shared.  Also this is implying I may have a block of say 8t being one whole
    section.  But in ir4 that's a lot of music, and w timestep is basically
    sections.  It could make a block always represent a section, but that's not
    necessarily a convenient unit to work with.  A block doesn't have to be a
    whole section, but if it's not then the lower end of timesteps are not
    useful.  This is the whole tradeoff with using a wide range of timesteps.

    Another approach would be to make a timestep shift, e.g. shift cmd-1 to be
    w, then h, etc.  This would adjust the UI but it would be one more bit of
    state to remember.  The only other option would be to reduce the resolution
    of timesteps, e.g. by putting some 4 subdivisions in there, but there's no
    convenient place.

    Or, abandon setting absolute timestep, and instead have +/- relative, like
    with octaves.

    Or, base timestep on zoom, which is already relative.  Adjust timestep to
    be the visible step on the current zoom level.  What about note duration?
    I don't seem to use that much, but I can have a relative adjustment for it.

    But if I do discontinuous tempo, what are the ruler divisions?  I
    definitely want gatras to be counted, but they are varying!  I would have
    to change ruler by irama, which is possible actually.  But that means
    for the score track, I'd want to do the promote child ruler thing, since
    ruler is changing.

        cmd ts  subdiv T    function    panerus
    +   1   S          n    kenong/pul
    +   2   w   4      1    gatra
        3   h   4      1/4  ir1 peking
    +   4   q   2      1/8              ir1
        5   e   2      1/16             ir2
    +   6   s   2      1/32             ir3
        7   t32 2      1/64             ir4
        8   t64 2      1/128

    . irama:            1/2    1        2    3     4
                        lancar tanggung dadi wilet rangkep
      peking / note     1       2        4    8     16
      panerus / note    2       4        8   16     32
      peking / gatra    4       8       16   32     64
      panerus / gatra   8      16       32   64    128

      But, this is just ScoreTime, which is fungible.  The main thing is the
      range of timesteps, which is 1-8 aka S to 1/64.  If they are mostly
      binary, then
      1=S, 2=g, 3=1/4, 4 q 1/4t ir1
      1
      2
      3
      4 1/4 q
      5 1/8 e
      6 1/16 s
      7 1/32
      8 1/64

    One nice thing about constant ScoreTime is that the tempo track will
    reflect the actual overall tempo, with no discontinuities, due to
    notational convenience.  Since I don't have note flags, shouldn't I take
    advantage of that?
-}

-- | (number, name, peking/gatra)
irama :: [(Double, Text, Int)]
irama :: [(Double, Text, Gongs)]
irama =
    [ (Double
1forall a. Fractional a => a -> a -> a
/Double
2, Text
"lancar",   Gongs
4)
    , (Double
1,   Text
"tanggung", Gongs
8)
    , (Double
2,   Text
"dadi",     Gongs
16)
    , (Double
3,   Text
"wilet",    Gongs
32)
    , (Double
4,   Text
"rangkep",  Gongs
64)
    ]

{-
        cmd ts  subdiv   T4    T  function    peking panerus
        1   S   n              n  kenong/pul
    +   2   w   2         4    1  gatra
        3   h   2         2  1/2
    +   4   q   2         1  1/4  note        lancar
        5   e   2       1/2  1/8              ir1
        6   s   2       1/4 1/16              ir2     ir1
        7   t32 2       1/8 1/32              ir3     ir2
    +   8   t64 2      1/16 1/64              ir4     ir3
            t128 2     1/32 1/128                     ir4
-}

java :: Int -> Meter.Meter
java :: Gongs -> Meter
java Gongs
lines = Config -> [MSection] -> Meter
Meter.meter Config
java_config (forall a. Gongs -> a -> [a]
replicate Gongs
lines MSection
section)
    where
    section :: MSection
section = Meter.MSection
        { section_measures :: Gongs
section_measures = Gongs
4
        , section_measure_duration :: TrackTime
section_measure_duration = TrackTime
4
        , section_measure :: AbstractMeter
section_measure = AbstractMeter
meter
        }
    meter :: AbstractMeter
meter = [Gongs] -> AbstractMeter
Meter.regular_subdivision [Gongs
2, Gongs
2, Gongs
2, Gongs
2, Gongs
2, Gongs
2]

java_config :: Meter.Config
java_config :: Config
java_config = Meter.Config
    { config_labeled_ranks :: Set Rank
config_labeled_ranks = forall a. Ord a => [a] -> Set a
Set.fromList [Rank
Meter.W, Rank
Meter.Q, Rank
Meter.T64]
    , config_label :: LabelConfig
config_label = Gongs -> LabelConfig
Meter.BigNumber Gongs
0
    , config_start_measure :: Gongs
config_start_measure = Gongs
1
    , config_min_depth :: Gongs
config_min_depth = Gongs
1
    , config_strip_depth :: Gongs
config_strip_depth = Gongs
2
    }