with Gmface.Gm_Application.Preferences; use Gmface.Gm_Application.Preferences;
with Gmface.Gm_MIDI.Devices;            use Gmface.Gm_MIDI.Devices;
with Gmface.Gm_MIDI.Portmidi;           use Gmface.Gm_MIDI.Portmidi;
with Ada.Containers.Vectors;            use Ada.Containers;
with Interfaces.C;                      use Interfaces.C;
use Interfaces;

with Text_Io;                           use Text_Io;

package body Gmface.Gm_MIDI.Instruments is
   
   Message_Max : constant Positive := 1024;
   
   subtype Message_Index_Type is Positive range 1..Message_Max;
   
   package Step_Vectors is new Ada.Containers.Vectors(Message_Index_Type, C.Long, C."=");
   
   subtype Step_Vector_Type is Step_Vectors.Vector; 
   
   use Step_Vectors;
   
   
   task body Mod_Sequencer_Type is
      
      
      
      type Sequence_Type is array (Positive  range <>) of Step_Vector_Type;
      
      subtype Input_Sequence_Type is Sequence_Type(1..1024);
      
      type Sheduler_Type is array (Boolean) of Input_Sequence_Type;
                  
      
      
      Message : Long := 0;
      Verax : Boolean := False;
      Sheduled_Sequence : Sheduler_Type;
      Step_index     : Positive := 1;
      Rec_Mod_Status  : Boolean := False;      
      The_Control   : Control_Type := Null_Item;
      
      task Synth_Input_Deamon is     	    
	 entry Start;
	 entry Stop;
	 entry Halt;
      end Synth_Input_Deamon;
      
      task body Synth_Input_Deamon is
	 Message : Interfaces.C.Long;
	 End_Of_Task : Boolean := False;
	 Suspended   : Boolean := True;
      begin
	 while not End_Of_Task loop
	    loop
	       
	       select
		  accept Stop;
	       or
		  accept Halt do
		     End_Of_Task := True;
		     Suspended := True;						
		  end Halt;		  
		  exit;
	       or
		  accept Start do
		     Suspended := False;		  			
		  end Start;
		  exit;
	       end select;
	    end loop;	    	 	 
	    Text_Io.Put_Line("Deamon input starting...");	    
	    
	    while not Suspended loop
	       
	       select
		  accept Start;
		  
	       or
		  accept Stop do			
		     Suspended := True;		  
		  end Stop;
	       or
		  
		  accept Halt do
		     End_Of_Task := True;
		     Suspended := True;			
		  end Halt;
		  
		  exit;
	       or 
		  delay 0.001; 
		  if instrument.Input_Device_Driver /= null then
		     if Rec_Mod_Status then
			select
		     
			   Instrument.Input_Device_Driver.Input_Driver.Send(Message);
			   
			   
			   --			select
			   --Synth_Input_Deamon.Send(Message);
			   --Text_Io.Put_Line("Receive message from channel :" & Channel_Type'Image(Channel_Of(Message)));		     
			   
			   --The_Control := Control_of(Message);
			   
			   --Text_Io.Put_Line(Control_Type'Image(The_Control));		     
			   
			   Sheduled_Sequence(not Verax)(Step_Index) := Step_Vectors."&"(Sheduled_Sequence(not Verax)(Step_Index), Message);
			   
			   --			or
			   --			   delay 0.05;
			   --			end select;
			or delay 0.001;
			--Put("No input message");
			end select;
		     end if;	       
		     
		     
		     
		--  	--	select
		--  	   accept Send(Message : out Long) do
		--  	      Message := Synth_Input_Deamon.Message;			      
		--  	   end Send;
		--  --	or
		--  --	   delay 0.01;
		--  --	end select;
		--    --   or
		--  --	delay 0.05;
		--    --   end select;
		  else
		     delay 0.05;
		  end if;
	       end select;
	    end loop;
	    Put_Line("Deamon input stopped...");	    
	 end loop;
	 Put_Line("Deamon input halted...");	    
      end Synth_Input_Deamon;
      

      
      
      Quantum : Duration := 0.0;
      
      End_Of_Task : Boolean := False;
      Suspended   : Boolean := False;
      Play_Mod_Status : Boolean := False;
      
      
      
      
      
      
      
      
      
      Tempo : Tempo_Type := 120.0;
      Date      : Time := Clock;
   begin
      
      while not End_Of_Task loop
	 loop	    
	    
	    
	    select
	       accept Stop;
	    or
	       accept Reset do		  
		  for I in Sheduled_Sequence(True)'Range loop
		     Step_Vectors.Clear (Sheduled_Sequence(True)(I));
		     Step_Vectors.Clear (Sheduled_Sequence(False)(I));
		  end loop;
	       end Reset;
	    or	       
	       accept Rec(In_Rec : in Boolean) do
		  Rec_Mod_Status := In_Rec;
		  Put_Line("Rec mod " & Boolean'Image(Rec_Mod_Status));
	       end Rec;		     	       
	    or
	       accept Play(In_Play : in Boolean) do
		  Play_Mod_Status := In_Play;
		  Put_Line("Play mod " & Boolean'Image(Play_Mod_Status));
	       end Play;
	       
	    or
	       accept Halt do
		  End_Of_Task := True;
		  Suspended := True;
		  Synth_Input_Deamon.Halt;
	       end Halt;		  
	       exit;
	    or
	       accept Start(Tempo : in Tempo_Type; Signature : in Time_Signature_type) do
		  Quantum :=  Duration(60000.0/Float(Tempo)/16.0/1000.0);	       
		  Suspended := False;
		  Synth_Input_Deamon.Start;
	       end Start;	       	       	       
	       exit;
	    end select;
	    
	 end loop;
	 
	 
	 Text_Io.Put_Line("Mod Sequencer starting...");	    
	 Date := Clock + Quantum;
	 while not Suspended loop	    	       
	    
	    select
	       accept Stop;		  
	       Synth_Input_Deamon.Stop;
	       exit;
	    or
	       accept Start(Tempo : in Tempo_Type; Signature : in Time_Signature_Type) do
		  Quantum :=  Duration(60000.0/Float(Tempo)/16.0/1000.0);
	       end Start;
	    or		  
	       accept Halt do
		  End_Of_Task := True;
		  Synth_Input_Deamon.Halt;	       
	       end Halt;	       	       
	       exit;		  
	    or
	       accept Rec(In_Rec : in Boolean) do
		  Rec_Mod_Status := In_Rec;
		  Put_Line("Rec mod " & Boolean'Image(Rec_Mod_Status));
	       end Rec;
	       
	       
	    or
	       accept Play(In_Play : in Boolean) do
		  Play_Mod_Status := In_Play;
		  Put_Line("Play mod " & Boolean'Image(Play_Mod_Status));
	       end Play;
	       
	    or
	       accept Reset do		     
		  for I in Sheduled_Sequence(True)'Range loop
		     Clear (Sheduled_Sequence(True)(I));
		     Clear (Sheduled_Sequence(False)(I));
		  end loop;
	       end Reset;
	    or delay 0.001;
	    --Put_Line("TETE -1.0");
	    end select;
	    --Put_Line("TETE 0.0");
	    if Play_Mod_Status then	     		     
	       if not Step_Vectors.Is_Empty (Sheduled_Sequence(Verax)(Step_Index)) then
		  --Put_Line("TETE 1.0");
		  for Num_Message in 1..Step_Vectors.Last_Index(Sheduled_Sequence(Verax)(Step_Index)) loop
		     --Put_Line("TETE 1.1");
		     declare
			Message : constant Long := Step_Vectors.Element(Sheduled_Sequence(Verax)(Step_Index), Num_Message);
			
		     begin
			
			--Put_Line("TETE 1.3");
			--Text_Io.Put_Line("outputing message");
			instrument.Output_Device_Driver.Output_Driver.Receive(Message);			      
			--Text_Io.Put_Line("message outputed");
		     end;
		  end loop;
	       end if;		     
	    end if;	       
	    if Step_Index + 1 > Input_Sequence_Type'Last then
	       Step_Index := 1;
	       Verax := not Verax;
	    else
	       Step_Index := Step_Index + 1;
	    end if;		     
	    
	    Date := Date + Quantum;
	    delay until Date - 0.001;
	 end loop;	 
	 Text_Io.Put_Line("Mod Sequencer stopping...");
	 
      end loop;      
      Text_Io.Put_Line("Mod Sequencer halted.");
   end Mod_Sequencer_Type;
   
   
   task body Tempo_Sync_Type is
      Start_Time  : Time := Clock;
      Quantum     : Duration := 0.0;
      
      Tempo : Tempo_Type := 120.0;	 
      Date      : Time := Clock;
      
      End_Of_Task : Boolean := False;
      
   begin	 	 
      
      while not End_Of_Task loop
	 loop
	    
	    select	       
	       accept Start(Start_Time : in Time; Tempo : in Tempo_Type; Signature : in Time_Signature_type) do
		  Quantum :=  Duration(60000.0/Float(Tempo)/24.0/1000.0);
	       end Start;
	       exit;
	    or
	       accept Halt;
	       End_Of_Task := True;
	       exit;
	    or
	       accept Stop;
	    end select;
	 end loop;
	 Start_Time := Clock;
	 while not End_Of_Task loop
	    
	    select
	       accept Stop;
	       exit;
	    or
	       accept Halt;
	       End_Of_Task := True;
	       exit;
	    or
	       accept Start(Start_Time : in Time; Tempo : in Tempo_Type; Signature : in Time_Signature_type) do
		  Quantum :=  Duration(60000.0/Float(Tempo)/24.0/1000.0);
	       end Start;
	    or 
	       delay until Start_Time;		  
	       
	       if Instrument.Sync then
		  Instrument.Output_Device_Driver.Output_Driver.Receive(Portmidi.Pm_Message(16#F8#, 0, 0));		  
	       end if;
	       
	       Start_Time := Start_Time + Quantum;
	       
	    end select;
	    
	 end loop;
      end loop;
   end Tempo_Sync_Type;                        
   
   
   procedure Initialize(Master : in out Bandmaster_Record) is
      Outputs : constant Devices_List_Access := Outputs_List;
      Inputs  : constant Devices_List_Access := Inputs_List;
            
      
      
   begin      
      
      for Id in Instrument_Id'range loop
	 
	 if Inputs_Devices(Integer(Id)) /= (-1) or Outputs_Devices(Integer(Id)) /= (-1) then
	    
	    if Inputs_Devices(Integer(Id)) /= (-1) then
	       if Master.Orchester(Id) = null then
		  Master.Orchester(Id) := new MIDI_Instrument_Record;
	       end if;
	       Master.Orchester(Id).Input_Device_Driver :=
		 new Input_Device_Driver_Type;	 
	       
	       Put_Line("initializing Input driver for device : " & 
			  Inputs(Inputs_Devices(Integer(Id))).Name.all & 
			  "instrument Num " & Instrument_Id'Image(Id));
	       
	       
	       Initialize_Input(Master.Orchester(Id).Input_Device_Driver.all,
				Inputs(Inputs_Devices(Integer(Id))));
	       Gmface.Gm_MIDI.Devices.Open(Master.Orchester(Id).Input_Device_Driver.Input.all);
	       Master.Orchester(Id).Input_Device_Driver.Input_Driver.Start;
	    end if;
	 
	    if Outputs_Devices(Integer(Id)) /= (-1) then
	       if Master.Orchester(Id) = null then
		  Master.Orchester(Id) := new MIDI_Instrument_Record;
	       end if;
	       Master.Orchester(Id).Output_Device_Driver :=
		 new Output_Device_Driver_Type;
	       
	       Put_Line("initializing output driver for device : " & 
			  Outputs(Outputs_Devices(Integer(Id))).Name.all & 
			  "instrument Num " & Instrument_Id'Image(Id));
	       
	       Initialize_Output(Master.Orchester(Id).Output_Device_Driver.all,
				 Outputs(Outputs_Devices(Integer(Id))));
	       
	       Gmface.Gm_MIDI.Devices.Open(Master.Orchester(Id).Output_Device_Driver.Output.all);
	    end if;
	    Master.Inst_Last := Master.Inst_Last + 1;
	 else
	    exit;
	 end if;
      end loop;      
            
   end Initialize;
   
   procedure Finalize(Master : in out Bandmaster_Record) is
   begin
      for Id in Instrument_Id'range loop
	 if Inputs_Devices(Integer(Id)) /= (-1) then
	    Gmface.Gm_MIDI.Devices.Close(Master.Orchester(Id).Input_Device_Driver.Input.all);
	 end if;
	 if Outputs_Devices(Integer(Id)) /= (-1) then
	    Gmface.Gm_MIDI.Devices.Close(Master.Orchester(Id).Output_Device_Driver.Output.all);
	 end if;
      end loop;
   end Finalize;
	 
end Gmface.Gm_MIDI.Instruments;