// ---- 
// ---- file   : script_quantized_pattern_restart.tks 
// ---- author : (c) 2018 by bsp 
// ---- legal  :  
// ----          Permission is hereby granted, free of charge, to any person obtaining a copy 
// ----          of this software and associated documentation files (the "Software"), to deal 
// ----          in the Software without restriction, including without limitation the rights 
// ----          to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// ----          copies of the Software, and to permit persons to whom the Software is 
// ----          furnished to do so, subject to the following conditions: 
// ----           
// ----          The above copyright notice and this permission notice shall be included in all 
// ----          copies or substantial portions of the Software. 
// ----           
// ----          THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// ----          IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// ----          FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// ----          AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// ----          LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// ----          OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
// ----          SOFTWARE. 
// ---- 
// ---- created: 15Sep2018
// ---- changed: 
// ---- 
// ----    info: 
// ----           - The queue lengths (16ths) can be set separately for each pattern sub group (A,B,C,D)
// ----           - A queue length of 0 disables queueing
// ----           - Also queues recordable program changes (RPN_COMMON_UI_PRGCHG, has prio over regular prgchg)
// ----           
// ---- 
// ---- 

boolean b_debug = false;

int tick_nr;

tag int p_num_16th_a;
tag int p_num_16th_b;
tag int p_num_16th_c;
tag int p_num_16th_d;

int queued_num_16th;
int queued_patnr;
boolean b_queued_patnr_rec;

function OnReset() {
   queued_patnr = -1;
}

function OnSeek(int ticks) {
   tick_nr = ticks;
}

function HandlePrgChg(MIDIPipeFrame frame) {
   MIDIPipeEvent ev; 
   int evIdx; 

   // Regular program change
   int numEventsPC = frame.getNumEventsPrgChgByFlt(io_dev, io_ch); 
   evIdx = 0; 
   loop(numEventsPC)
   { 
      frame.getEventByIdxAndFlt(evIdx, ev, MIDIPIPE_EVENT_TYPE_PRGCHG, io_dev, io_ch); 

      queued_patnr = ev.prgChg;
      b_queued_patnr_rec = false;

      switch(queued_patnr & 3)
      {
         case 0: // 'A'
            queued_num_16th = p_num_16th_a;
            break;

         case 1: // 'B'
            queued_num_16th = p_num_16th_b;
            break;

         case 2: // 'C'
            queued_num_16th = p_num_16th_c;
            break;

         case 3: // 'D'
            queued_num_16th = p_num_16th_d;
            break;
      }

      if(b_debug)
         Global.Debug("qpatrestart: PrgChg queued_patnr="+queued_patnr+" queued_num_16th="+queued_num_16th);

      evIdx++;
   }

   frame.deletePrgChgByFlt(io_dev, io_ch);

   // Recordable program change
   int numEventsRPN = frame.getNumEventsRPNByFlt(io_dev, io_ch); 
   evIdx = 0; 
   loop(numEventsRPN)
   { 
      frame.getEventByIdxAndFlt(evIdx, ev, MIDIPIPE_EVENT_TYPE_RPN, io_dev, io_ch); 

      if(Node.RPN_COMMON_UI_PRGCHG == ev.rpn)
      {
         queued_patnr = ev.rpnValue & 127;
         b_queued_patnr_rec = true;

         switch(queued_patnr & 3)
         {
            case 0: // 'A'
               queued_num_16th = p_num_16th_a;
               break;

            case 1: // 'B'
               queued_num_16th = p_num_16th_b;
               break;

            case 2: // 'C'
               queued_num_16th = p_num_16th_c;
               break;

            case 3: // 'D'
               queued_num_16th = p_num_16th_d;
               break;
         }

      if(b_debug)
         Global.Debug("qpatrestart: RPN queued_patnr="+queued_patnr+" queued_num_16th="+queued_num_16th);
      }

      evIdx++;
   }

   frame.deleteRPNByFlt(io_dev, io_ch, Node.RPN_COMMON_UI_PRGCHG);
   
}

function OnProcess(MIDIPipeFrame framePlay,
                   MIDIPipeFrame frameRec,
                   boolean       bMuted,
                   boolean       bPlaySeq
                   ) {
   if(bPlaySeq)
   {
      if(!bMuted)
      {
         int numTicksPer16th = st2::current_song.ppq / 4;

         // Handle/Queue incoming program changes
         HandlePrgChg(framePlay);
         HandlePrgChg(frameRec);
         
         if(-1 != queued_patnr)
         {
            if(0 == (tick_nr % (numTicksPer16th * queued_num_16th)))
            {
               if(b_queued_patnr_rec)
               {
                  framePlay.rpn(true/*bSet*/, io_dev, io_ch, Node.RPN_COMMON_UI_PRGCHG, queued_patnr);
               }
               else
               {
                  framePlay.prgChg(true/*bSet*/, io_dev, io_ch, queued_patnr);
               }
               framePlay.rpn(true/*bSet*/, io_dev, io_ch, Node.RPN_COMMON_SEEK_OFFSET, 0/*16th*/);
               queued_patnr = -1;
            }
         }
      }

      tick_nr++;
   }
}
