with Calendar;                          use Calendar;
with Libmy.MIDI.Portmidi;               use Libmy.MIDI.Portmidi;
with Libmy.MIDI.Porttime;               use Libmy.MIDI.Porttime;
with Text_Io;
package body Libmy.MIDI.Scheduler is
 
   task body Keyboard is
      Pm_Event : PmEvent;
   begin
      loop
         Pm_Event.Message := Read_handler(Keyb_Addr.all);
         accept Send(Message : out Long) do
            Message := Pm_Event.Message;
         end Send;
      end loop;
   end Keyboard;
 
   task body Input is
      Pm_Event : PmEvent;
   begin
      loop
         Pm_Event.Message := Read_handler(Input_Addr.all);
         accept Send(Message : out Long) do
            Message := Pm_Event.Message;
         end Send;
      end loop;
   end Input;
 
   task body Output is
      Pm_Event : PmEvent;
      Pm_Error : PmError;
      Date : Time := Clock;
      Sync : Duration := 0.0;
   begin
      loop
         accept receive(Message : in Long) do
            Pm_Event.Message := Message;
         end Receive;
         if Pm_Event.Message > 0 then
            Pm_Event.PmTimestamp := Pt_time;
            Pm_Error := Pm_Write(Output_Addr.All, Pm_Event, 1);
         end if;
      end loop;
      Text_Io.Put_Line("End_output_Drivers" );
   exception
      when others =>
         Text_Io.Put_line("exception in output driver." );
   end Output;
 
 
   task body Scheduler is
 
 
      task Tempo_Drive is
         entry Start(Bpm : in Tempo_Type; Top : in Time);
         entry Stop;
         entry Halt;
      end Tempo_Drive;
 
      task body Tempo_Drive is
 
 
         Suspended, End_Of_Task : Boolean := False;
         Tempo_Message : Message_Type;
         Tempo_Date : Time := Clock;
         Tempo_Sync : Duration;
 
      begin
 
         while not End_Of_Task loop
 
            select
               accept Start(Bpm : in Tempo_Type; Top : in Time) do
                  Suspended := False;
                  Text_Io.Put_Line("start global." );
                  Tempo_Sync := duration(60.0/Float(Bpm)/24.0);
                  Tempo_Date := Top;
               end Start;
            or
               accept Halt do
                  Text_Io.Put_Line("Halt global." );
                  Suspended := True;
                  End_Of_Task := True;
               end Halt;
            end select;
            while not Suspended loop
               select
                  accept Stop do
                     Text_Io.Put_Line("Stop global." );
                     Suspended := True;
                     end Stop;
               or
                  accept Halt do
                     Text_Io.Put_Line("Halt global." );
                     Suspended := True;
                     End_Of_Task := True;
                  end Halt;
               or
                  delay until Tempo_Date;
                  Tempo_Date := Tempo_Date + Tempo_Sync;
                  Tempo_Message := (16#F8#, 0, 0);
                  Output.Receive(Pm_Message(Tempo_Message));
               end select;
            end loop;
         end loop;
         Text_Io.Put_Line("End_Tempo_Drivers" );
      exception
         when others =>
            Text_Io.Put("Exception in Tempo Driver" );
      end Tempo_Drive;
 
 
      The_Sched : Scheduler_Type := (new Seq_Page_Type, new Seq_Page_Type);
      The_Step  : positive := 1;
      The_Page : Boolean := False;
      End_Of_Task : Boolean := False;
      Suspended : Boolean := False;
      Step_Date : Time := Clock;
      Step_Sync : Duration;
      Message : Long := 0;
      Tempo : Tempo_Type := 240.0;
   begin
      while not End_Of_Task loop
         select
            accept Start do
               Suspended := False;
               Step_Sync  := Duration(60.0/Float(Tempo)/16.0);
               Step_Date  := Clock;
               The_Step := 1;
               Tempo_Drive.Start(Tempo,
                                 Step_Date);
               Composer.Composer.Start(The_Sched(not The_Page),
                                       Tempo,
                                       Step_Date);
            end Start;
         or
            accept Halt do
               Suspended := True;
               End_Of_Task := True;
               Composer.Composer.Halt;
               Tempo_Drive.Halt;
            end Halt;
         end select;
         delay until Step_Date;
         while not Suspended loop
 
            select
               accept Stop do
                  Suspended := True;
                  Composer.Composer.Stop;
                  Tempo_Drive.Stop;
               end Stop;
            or
               accept Halt do
                  Suspended := True;
                  End_Of_Task := True;
                  Composer.Composer.Halt;
                  Tempo_Drive.Halt;
               end Halt;
 
            else
               -- Reading data in input while not end of step.
               while Step_Date > Clock loop
                  select
                     keyboard.Send(Message);
                     Insert(The_Sched(not The_Page).Keyb(The_Step),
                            Last_index(The_Sched(not The_Page).Keyb(The_Step)) + 1,
                            Message,
                            1);
                  else
                     null;
                  end select;
                  select
                     input.Send(Message);
                      Insert(The_Sched(not The_Page).Keyb(The_Step),
                            Last_index(The_Sched(not The_Page).Keyb(The_Step)) + 1,
                            Message,
                            1);
                  else
                     null;
                  end select;
               end loop;
 
               -- Writing Data for current step.
               if Last_index(The_Sched(The_Page).Output(The_Step)) /= 0 then
                  for I in 1..Last_index(The_Sched(The_Page).Output(The_Step)) loop
                     Output.Receive(Element(The_Sched(The_Page).Output(The_Step), i));
                  end loop;
 
                  Clear(The_Sched(The_Page).Output(The_Step));
               end if;
 
               -- Step & page managment.
               Step_Date := Step_Date + Step_Sync;
               if The_Step = Step_Index_Type'Last then
 
                  Text_Io.Put(Character'Val(13) & "Step :" &
                                Step_Index_Type'Image(The_Step));
                  Text_Io.Put_Line(", change page..." );
                  The_Step := 1;
                  The_Page := not The_Page;
                  Composer.Composer.Switch(The_Sched(not The_Page));
               else
                  Text_Io.Put(Character'Val(13) & "Step :" &
                                Step_Index_Type'Image(The_Step));
 
 
                  The_Step := The_Step + 1;
               end if;
 
            end select;
         end loop;
      end loop;
      abort Keyboard;
      abort Input;
      abort Output;
      Text_Io.Put_Line("End_scheduler" );
   exception
      when others =>
         Text_Io.Put_Line("Exception in scheduler driver" );
   end Scheduler;
end Libmy.MIDI.Scheduler;