with Libsens.Processing.Step_Sequencer; use Libsens.Processing.Step_Sequencer;

with Libsens.Processing.Genetical_Bass; use Libsens.Processing.Genetical_Bass;
with Libsens.Processing.Genetical_Synth;use Libsens.Processing.Genetical_Synth;
with Libsens.Processing.Genetical_Drums;use Libsens.Processing.Genetical_Drums;

with Libsens.Processing.Neural_Mono;    use Libsens.Processing.Neural_Mono;
with Libsens.Processing.Neural_Poly;    use Libsens.Processing.Neural_Poly;
with Libsens.Processing.Neural_Rythm;    use Libsens.Processing.Neural_Rythm;

with Libsens.Processing.Ctrl_Change;    use Libsens.Processing.Ctrl_Change;
with Libsens.Processing.Prgm_Change;    use Libsens.Processing.Prgm_Change;

with Libsens.Processing.Neural_Modulator;use Libsens.Processing.Neural_Modulator;
with Libsens.arch;                      use Libsens.arch;

with Libsens.Processing.Neural_Arpegiator;use Libsens.Processing.Neural_Arpegiator;

with Libsens.Common;                    use Libsens.Common;
with Libsens.MIDI.Drivers;              use Libsens.MIDI.Drivers;
with Libsens.Virtual.Aux;               use Libsens.Virtual.Aux;
with Ada.Calendar;                      use Ada.Calendar;

with Text_Io;                           use Text_Io;

package body Libsens.Processing.Work_Processing is         
   
   
   function Initialize(Class : in Plugin_Enum;
		       Plugin : in Abstract_Plugin_Access) return Plugin_Processing_Access is
      
      Process : Plugin_Processing_Access;
      
   begin
      case Class is
	 when Null_Plugin =>
	    null;
	 when Step_Seq =>
	    Process := new Step_Sequencer_Processing(Plugin);
	 when Arp_Seq =>
	    Process := new Neural_Arpegiator_Processing(Plugin);
	 when NN_Mono =>
	    Process := new Neural_Mono_Processing(Plugin);
	 when NN_Poly =>	    
	    Process := new Neural_Poly_Processing(Plugin);
	 when NN_Rythm =>
	    Process := new Neural_Rythm_Processing(Plugin);
	 when Nl_Mod =>
	    Process := new Neural_Modulator_Processing(Plugin);
	 when Gen_Bass =>
	    Process := new Genetical_Bass_Processing(Plugin);
	 when Gen_synth =>
	    Process := new Genetical_Synth_Processing(Plugin);	    
	 when Gen_Drums =>
	    Process := new Genetical_Drums_Processing(Plugin);
	 when CC_List =>
	    Process := new Ctrl_change_Processing(Plugin);
	 when PC_List =>
	    Process := new Prgm_change_Processing(Plugin);
      end case;            
      return Process;
   end Initialize;
   
   
   task body Work_Process is
      
      
      task Band_Master_Driver is
	 entry Start(Start_Time : in Time; Tempo : in Tempo_Type; Signature : in Time_Signature_type);	 
	 entry Stop;
	 entry Halt;
	 entry End_Of_Track(Verax : out Boolean);
      end Band_Master_Driver;
      

      task body Band_Master_Driver is

	 Quantum : Duration := 0.0;                  
	 Bar_Beat : Bar_Beat_Type;
	 New_Signature, Signature : Time_Signature_Type := (4, 4);
	 Tempo : Tempo_Type := 120.0;	 
	 
	 Form  : Form_Index_Type := Form_Index_Type'First;
	 Form_Mod : Positive := 2;	 	 
	 
	 
	 Break_Gen : Break_Rand.Generator;
	 Form_Gen  : Form_Rand.Generator;      
	 
	 
	 End_Of_Task : Boolean := False;
	 Start_Time  : Time := Clock;
      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))/Signature.Unit/1000.0;
		     Band_Master_Driver.Tempo := Tempo;
		     Processing.Work.States.Start_Time := Start_Time;
		     Band_Master_Driver.Signature := Signature;
		     Band_Master_Driver.Start_Time := Start_Time;
		     Processing.Work.States.Bar_Beat := (1, 1, 1);
		     Bar_Beat := (1, 1, 1);
		  end Start;		  
		  Text_Io.Put_Line("Band_Master Driver started");
		  exit;
	       or
		  accept Halt;
		  End_Of_Task := True;		  
		  exit;
	       or
		  accept Stop;
	       end select;
	    end loop;	    
	    while not End_Of_Task loop
	       
	       select
		 
		  accept End_Of_Track(Verax : out Boolean) do
		     Verax := False;
		  end End_Of_Track;
	       or
		  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))/Signature.Unit/1000.0;
		     Band_Master_Driver.Tempo := Tempo;
		     Band_Master_Driver.Start_Time := Start_Time;
		  end Start;
	       or 
		  delay until Start_Time;
		  
		  
		  NEXT(Processing.Work.States.Bar_Beat, Processing.Work.Options.Track_length, Processing.Work.Options.Signature.Number, Processing.Work.Options.Signature.Unit);		  
		  if Processing.Work.States.Bar_Beat.bar = Processing.Work.Options.Track_Length then
		     accept End_Of_Track(Verax : out Boolean) do
			Verax := True;
		     end End_Of_Track;
		     Processing.Work.States.Bar_Beat := (1, 1, 1);
		  end if;
		  
		  --Put_Line("Next Enter");
		  
		  --Put_Line("TITI 2");
		  
		  if Signature.Number =  Processing.Work.States.Bar_Beat.Time_Number and
		    Processing.Work.States.Bar_Beat.Time_unit = 1 then
		     --Put_Line("TITI 3");
		     
			if Form_Mod = 7 then
			   Put_Line("Form Mod 7 entry...");
			   Processing.Work.Options.Prev_Break := Break_Rand.Random(Break_Gen);
			   Processing.Work.Options.Next_Break := Break_Rand.Random(Break_Gen);		  
			   
			   Put_Line("Form Mod 7 out !");
			   Form_Mod := Form_Mod + 1;
			elsif Form_Mod = 8 then			   
			   Form_Mod := 1;
			   Put_Line("Form Mod 8 entry...");
			   Processing.Work.Options.Prev_Break := Break_Rand.Random(Break_Gen);
			   Processing.Work.Options.Next_Break := Break_Rand.Random(Break_Gen);		  
			   Put_Line("Form Mod 8 out...");
			   --  if Prgm = 15 then
			   --     Prgm := 0;
			   --  else
			   --     Prgm := Prgm + 1;
			   --  end if;
			   --  Message := To_Long(Program_Change(0, Prgm));
			   --  --for Id in Processing.Work.Driver_Set.Set'Range loop
			   --     if Processing.Work.Driver_Set.Set(1) /= null and then				
			   --  	Processing.Work.Driver_Set.Set(1) /= null then
			   --  	 Processing.Work.Driver_Set.Set(1).Event_Process.Receive(Start_time, message, 0.02, Tempo);
			   --     end if;
			   --  --end loop;
			else
			   Form_Mod := Form_Mod + 1;
			end if;
			--Text_Io.Put_Line("New Break...");
			
		  end if;
		  Start_Time := Start_Time + Quantum;
		  
		  
	       end select;
	       
	    end loop;
	 end loop;
	 Put_Line("Bandmaster halted.");
      end Band_Master_Driver;
      
      
      Track_End   : Boolean := False;
      End_Of_Task : Boolean := False;      
      Initialized : Boolean := False;

      
      Date : Time := Clock;
      
   begin
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize do		     
		  Put_Line("Work [" & Processing.Work.Filename.all & "] ready.");
		  Initialized :=  True;
	       end Initialize;
	    or
	       when Initialized =>
		  accept Start do
		     for Plugin_Id in Processing.Work.Plugins'Range loop			
			if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then
			   --if Processing.Processes(Plugin_Id) = null then
			      Processing.Processes(Plugin_Id) := 
				Initialize(Processing.Work.Plugins(Plugin_Id).Class,
					   Processing.Work.Plugins(Plugin_Id));
			   --  elsif Processing.Processes(Plugin_Id).Class /=
			   --    Processing.Work.Plugins(Plugin_Id).Class then
			   --     Processing.Processes(Plugin_Id) := 
			   --  	Initialize(Processing.Work.Plugins(Plugin_Id).Class,
			   --  		   Processing.Work.Plugins(Plugin_Id));
			      
			   --  end if;	    
			      Processing.Processes(Plugin_Id).Instruments := Processing.Instruments;
			      Processing.Processes(Plugin_Id).Box := Processing.Box'Access;
			end if;
		     end loop;
		     
		     
		     for Plugin_Id in Processing.Work.Plugins'Range loop			   			   
			
			if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then			      
			   --Put_Line("MIMI 0.0");
			   --  if Processing.Processes(Plugin_Id) = null then
			   --     case Processing.Work.Plugins(Plugin_Id).Class is
			   --  	 when Null_Plugin =>
			   --  	    null;
			   --  	 when Step_Seq =>				      
			   --  	    Processing.Processes(Plugin_Id) := new Step_Sequencer_Processing(Processing.Work.Plugins(Plugin_Id));
			   --  	    Processing.Processes(Plugin_Id).Box := Processing.Box'Access;
			   --  	 when others =>
			   --  	    null;
			   --     end case;
			   --  end if;
			   if Processing.Processes(Plugin_Id) /= null then				 
			      --Put_Line("MIMI 0.1");
			      Processing.Processes(Plugin_Id).Initialize(Processing.Work.Options'access);
			      --Put_Line("MIMI 0.2");
			   end if;
			end if;
		     end loop;
		     
		     New_Line(3);
		     Put_Line("Starting project name : " & Processing.Work.Options.Filename.all);
		     New_Line(3);
		     
		     Date := Clock;
		     Processing.Work.States.Start_Time := Date;
		     
		     Band_Master_Driver.Start(date, Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
		     --Put_Line("MAMA 0.0");
		     for Device_Id in 1..Processing.Instruments.Inst_Last loop
			if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
			  Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then			
			    --Put_Line("MAMA 0.2");
			   Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Tempo_Sync.Start(Date, Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
			   --Put_Line("MAMA 0.3");
			   Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Start(Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
			   --Put_Line("MAMA 0.4");
			end if;
		     end loop;
		     
		     
		     
		     
		     
		     for Plugin_Id in Processing.Work.Plugins'Range loop			   			   
			
			if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then			      
			   --Put_Line("MIMI 0.0");
			   --  if Processing.Processes(Plugin_Id) = null then
			   --     case Processing.Work.Plugins(Plugin_Id).Class is
			   --  	 when Null_Plugin =>
			   --  	    null;
			   --  	 when Step_Seq =>				      
			   --  	    Processing.Processes(Plugin_Id) := new Step_Sequencer_Processing(Processing.Work.Plugins(Plugin_Id));
			   --  	    Processing.Processes(Plugin_Id).Box := Processing.Box'Access;
			   --  	 when others =>
			   --  	    null;
			   --     end case;
			   --  end if;
			   if Processing.Processes(Plugin_Id) /= null then				 
			      --Put_Line("MIMI 0.1");
			      Processing.Processes(Plugin_Id).Start(Date, Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
			      --Put_Line("MIMI 0.2");
			   end if;
			end if;
		     end loop;
		     --Put_Line("MAMA 1.0");
		     Processing.Work.Started := True;		  
		     --Put_Line("MAMA 2.0");
		     
		     --Put_Line("MAMA 3.0");
		  end Start;		  
		  exit;
	    or
	       accept Stop;	       
	       
	    or
	       when Track_End =>
		  accept End_Of_Process;
	    or
	       accept Mod_Rec(Status : in Boolean) do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
		     if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		       Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then
			Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Rec(Status);
		     end if;		     
		  end loop;
	       end Mod_Rec;
	    or
	       accept Mod_Play(Status : in Boolean) do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
		     if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		       Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then
			Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Play(Status);
		     end if;		     
		  end loop;
	       end Mod_Play;
	    or
	       accept Mod_Reset do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
		     if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		       Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then
			Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Reset;
		     end if;		     
		  end loop;
	       end Mod_Reset;
	    or
	       accept Halt do
		  Band_Master_Driver.Halt;
		  for Plugin_Id in Processing.Work.Plugins'Range loop			   			   
		     if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then			      
			if Processing.Processes(Plugin_Id) /= null then
			   Processing.Processes(Plugin_Id).Halt;
			   Processing.Processes(Plugin_Id).Event_Process.Halt;
			end if;
		     end if;
		  end loop;		  
		  End_Of_Task := True;
	       end Halt;
	       exit;
	    end select;
	 end loop;
	 while not End_Of_Task loop
	    select
	       accept Initialize do		     
		  Put_Line("Work [" & Processing.Work.Filename.all & "] ready.");
		  Initialized :=  True;
	       end Initialize;
	    or
	       accept Start do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
			if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
			  Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then			
			    --Put_Line("MAMA 0.2");
			   Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Tempo_Sync.Start(Date, Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
			   --Put_Line("MAMA 0.3");
			   Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Start(Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
			   --Put_Line("MAMA 0.4");
			end if;
		     end loop;
		     for Plugin_Id in Processing.Work.Plugins'Range loop			   			   
			
			if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then			      
			   --Put_Line("MIMI 0.0");
			   --  if Processing.Processes(Plugin_Id) = null then
			   --     case Processing.Work.Plugins(Plugin_Id).Class is
			   --  	 when Null_Plugin =>
			   --  	    null;
			   --  	 when Step_Seq =>				      
			   --  	    Processing.Processes(Plugin_Id) := new Step_Sequencer_Processing(Processing.Work.Plugins(Plugin_Id));
			   --  	    Processing.Processes(Plugin_Id).Box := Processing.Box'Access;
			   --  	 when others =>
			   --  	    null;
			   --     end case;
			   --  end if;
			   if Processing.Processes(Plugin_Id) /= null then				 
			      --Put_Line("MIMI 0.1");
			      Processing.Processes(Plugin_Id).Start(Date, Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
			      --Put_Line("MIMI 0.2");
			   end if;
			end if;
		     end loop;
		     Band_Master_Driver.Start(date, Processing.Work.Options.Tempo, Processing.Work.Options.Signature);
	       end Start;
	    or
	       accept Stop;
	       Band_Master_Driver.Stop;
	       for Device_Id in 1..Processing.Instruments.Inst_Last loop
		  if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		    Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then			
		     Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Tempo_Sync.Stop;
		     Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Stop;
		  end if;
	       end loop;
	       for Plugin_Id in Processing.Work.Plugins'Range loop			   			   
		  if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then			      
		     if Processing.Processes(Plugin_Id) /= null then
			Processing.Processes(Plugin_Id).halt;
			Processing.Processes(Plugin_Id).Event_Process.halt;
			Processing.Processes(Plugin_Id) := null;
		     end if;
		  end if;
	       end loop;
	       Processing.Work.Started := False;		  
	       
	       exit;
	    or
	       when Track_End =>
		  accept End_Of_Process;
	    or

	       accept Mod_Rec(Status : in Boolean) do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
		     if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		       Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then
			Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Rec(Status);
		     end if;		     
		  end loop;
	       end Mod_Rec;
	    or
	       accept Mod_Play(Status : in Boolean) do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
		     if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		       Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then
			Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Play(Status);
		     end if;		     
		  end loop;
	       end Mod_Play;
	    or
	       accept Mod_Reset do
		  for Device_Id in 1..Processing.Instruments.Inst_Last loop
		     if Processing.Instruments.Orchester(Instrument_Id(Device_Id)) /= null and then
		       Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Output_Device_Driver /= null then
			Processing.Instruments.Orchester(Instrument_Id(Device_Id)).Mod_Seq.Reset;
		     end if;		     
		  end loop;
	       end Mod_Reset;

	    or
	       accept Halt;	       
	       Band_Master_Driver.Halt;
	       for Plugin_Id in Processing.Work.Plugins'Range loop			   			   
		  if Processing.Work.Plugins(Plugin_Id) /= null and then
			  Processing.Work.Plugins(Plugin_Id).Class /= Null_Plugin then			      
		     if Processing.Processes(Plugin_Id) /= null then
			Processing.Processes(Plugin_Id).Halt;			
			Processing.Processes(Plugin_Id).Event_Process.Halt;
			Processing.Processes(Plugin_Id) := null;
		     end if;
		  end if;
	       end loop;
	       
	       
	       
	       End_Of_Task := True;
	       exit;
	    or
	       delay 0.15;
	       select
		  Band_Master_Driver.End_Of_Track(Track_End);		  		  
		  if Track_End then
		     Processing.Work.Started := not Track_End;
		     Put_Line("Track end : startd value is : " & Boolean'Image(Processing.Work.Started));
		  end if;
		     
	       or
		  delay 1.0;
	       end select;
	    end select;
	 end loop;
      end loop;
   end Work_Process;
   
   procedure Initialize(Processing : in out Work_Processing_Record;
			Work       : in Work_Access;
			Insts      : access Bandmaster_record) is
   begin
      if Insts = null then
	 raise Program_Error;
      end if;
      Processing.Work := Work;
      Processing.Instruments := Insts;      
   end Initialize;
   
end Libsens.Processing.Work_Processing;