with Libsens.MIDI.Portmidi;             use Libsens.MIDI.Portmidi;
with Libsens.MIDI.Drivers;              use Libsens.MIDI.Drivers;
with Libsens.MIDI.Messages;             use Libsens.MIDI.Messages;
with Libsens.Virtual.Nl_Network;        use Libsens.Virtual.Nl_Network;

with Libsens.Processing.Common;         use Libsens.Processing.Common;

with Libsens.Mal;                       use Libsens.Mal;
with Libsens.Data;                      use Libsens.Data;
with Libsens.Data.Lead;
with Libsens.Data.Bass;
with Libsens.Data.Drums;


with Libsens.Rhetorica;

with Ada.Containers;                    use Ada.Containers;

with Ada.Directories;                   use Ada.Directories;

with Ada.Calendar.Formatting;

with Text_Io;                           use Text_Io;

with Interfaces.C;                      use Interfaces.C;

package body Libsens.Processing.Neural_Modulator is
   
   procedure Initialize(Plugin_Process : in Neural_Modulator_Processing;Options : in Work_Options_Access) is
   begin
      Plugin_Process.Process.Initialize(Options);
   end Initialize;   
   
   
   procedure Start(Plugin_Process : in Neural_Modulator_Processing;
		   Start_Time : in Time;
		   Tempo : in Tempo_Type;
		   Signature : in Time_Signature_Type) is
   begin
      Plugin_Process.Process.Start(Start_Time, Tempo, Signature);
   end Start;
   
   procedure Stop(Plugin_Process : in Neural_Modulator_Processing) is
   begin
      Plugin_Process.Process.Stop;
   end Stop;
   
   procedure Halt(Plugin_Process : in Neural_Modulator_Processing) is
   begin
      Plugin_Process.Process.Halt;
   end Halt;
   
   
   
   
   
   task body Neural_Modulator_Process is      
      
      
      Start_Time  : Time := Clock;
      Quantum     : Duration := 0.0;
      
      Tempo : Tempo_Type := 120.0;	 
      Date      : Time := Clock;
      
      End_Of_Task : Boolean := False;
      Bar_Beat : Bar_Beat_Type;
      Signature : Time_Signature_Type := (4, 4);
      
      Current_Form : Form_Index_Type := 1;
      Current_Break    : Break_Type := Down_Break;
      Prev_Break       : Break_Type := Null_Break;	 
      Next_Break       : Break_Type := Down_Break;
      
      

      
      Options : access Work_Options_Record;
      
      
      package Rhetorica is new Libsens.Rhetorica;
      
      use Rhetorica;
      
      
      Modulator : Composer_Type(Form_Type'Val(Neural_Modulator.Plugin.Form_Id-1), Category_Type'Val(Neural_Modulator.Plugin.Cat_Id));
      
      
      
      All_Seq : Seq_Vectors.Vector;
      
      
   begin
      --  Put_Line("Going to initialize Modular generator...");
      
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize(Options : in Work_Options_Access) do
		  Put_Line("Plugin id : " &
			     Plugin_Num'Image(Neural_Modulator.Plugin.Id) &
			     " ready for process " &
			     Plugin_Enum'Image(Neural_Modulator.Plugin.Class));
		  Neural_Modulator_Process.Options := Options;
		  
		  --  Put_Line("Iniitializing generators");
      
		  if not NL_Network_Plugin_Record(Neural_Modulator.Plugin.all).Gen_Reuse then
		     --  Put_Line("Iniitializing Drums generators");
		     
		     Libsens.Data.Drums.Drums_Gen.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id), False, 0.1, 2048);      
		     
		     --  Put_Line("Iniitializing Bass generators");
		     
		     Libsens.Data.Bass.Bass_Gen.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id), False, 0.1, 2048);	 
		     
		     
		     --  Put_Line("Iniitializing Lead generators");
	    
		     Libsens.Data.Lead.Lead_Gen.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id), False, 0.1, 2048);
		  else
		     --  Put_Line("Iniitializing Drums generators");
		     
		     Libsens.Data.Drums.Drums_Gen.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id), True, 0.1, 1);      
		     
		     --  Put_Line("Iniitializing Bass generators");
		     
		     Libsens.Data.Bass.Bass_Gen.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id), True, 0.07, 1);	 
		     
		     
		     --  Put_Line("Iniitializing Lead generators");
	    
		     Libsens.Data.Lead.Lead_Gen.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id), True, 0.05, 1);
		  end if;
	 
		  NL_Network_Plugin_Record(Neural_Modulator.Plugin.all).Gen_Reuse := True;
	 
		  --Put_Line("Neural_Modulator started ::= MIMI 0.1.0");
		  --  Put_Line("Iniitializing modular generator...");
		  Modulator.Compositor.Initialize(Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id));
		  --  Put_Line("Modular generator iniitialized.");
		  
		  
	       end Initialize;
	       	     
	       
	    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 * Signature.Number)/1000.0;
		  Neural_Modulator_Process.Tempo := Tempo;
		  Neural_Modulator_Process.Signature := Signature;
		  
		  Bar_Beat := (1, 1, 1);
		  Neural_Modulator_Process.Start_Time := Start_Time;		  
		  --Modulator.Compositor.Start(Start_Time, Tempo);
	       end Start;
	       exit;
	    or
	       accept  Stop;
	       
	    or
	       accept Halt;
	       Modulator.Compositor.Halt;
	       End_Of_Task := True;
	       exit;
	    end select;
	 end loop;
	 
	 
	 declare
	    task Mod_Composer is
	       entry Halt;
	       entry Send(The_Sequence : out Seq_Vectors.Vector);
	    end Mod_Composer;
	      
	    task body Mod_Composer is
	       All_Channel_Seq : Seq_Vectors.Vector;
	       New_Seq : Seq_Vectors.Vector;
	       
	       End_Of_Task : Boolean := False;
	       
	    begin
	       
	       while not End_Of_Task loop
		  Modulator.Compositor.Respond
		    
		    (Options.Filename.all & Plugin_Num'Image(Neural_Modulator.Plugin.Id),
		     Long(Neural_Modulator.Plugin.Ch_Id-1),
		     New_seq);
		  

		  select
		     accept Halt;
		     exit;
		  or
		     accept Send(The_Sequence : out Seq_Vectors.Vector) do
			
			The_Sequence := New_Seq;
			
			new_Seq := Seq_Vectors.Empty_Vector;			
		     end Send;
		  end select;
	       end loop;
	    end Mod_Composer;
	    
	    
	    Length : Count_type := 0;
	    
	 begin
	 
	    
	    while not End_Of_Task loop
	       select
		  accept Initialize(Options : in Work_Options_Access);
	       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 * Signature.Number)/1000.0;
		     Neural_Modulator_Process.Tempo := Tempo;
		     Neural_Modulator_Process.Signature := Signature;		  		     
		  end Start;
	       or
		  accept  Stop;		  		  
		  End_Of_Task := True;
		  exit;
		  
	       or
		  accept Halt;
		  End_Of_Task := True;
		  Mod_Composer.Halt;
		  Modulator.Compositor.Halt;
		  exit;
	       or
		  delay until Start_Time;
		  --Put_Line("Neural_Modulator started ::= MIMI 0.1.1");
		  --if current_form = Neural_Modulator.plugin.played_form then
		  --  if Neural_Modulator.Plugin.Algo = Null_Algo then
		  if (not Neural_Modulator.plugin.mutted) 
		    and is_formed(Options.true_table,			       
				  Neural_Modulator.Plugin.Cat_id, 
				  Options.Prev_Break, 
				  Options.Next_Break) then
		     --Put_Line("Neural modulator started ::= MIMI 0.1.2");
		     
		     ---------------------------------
		     --
		     Mod_Composer.Send(All_Seq);
		     --
		     ---------------------------------
		     
		     if not Seq_Vectors.Is_Empty(All_Seq) then
			Length := Seq_Vectors.length(All_Seq);
			--Put_Line("Natural language write...");
			
			for I in 1..Length loop
			
			   
			   Date := Start_Time;
			   declare
			      Step : constant Step_Vectors.Vector := Seq_Vectors.First_Element(All_Seq);
			   begin
			      Seq_Vectors.Delete_First(All_Seq);
			      if not Step_Vectors.Is_Empty(Step) then
				 for I in 1..Step_Vectors.Last_Index(Step) loop
				    
				    declare 
				       
				       Message : Long := Step_Vectors.Element(Step,I);
				       
				    begin
				       --Put_Line("tutu 1.4");
				       if Neural_Modulator.plugin.printed then
					  --Put_Line("tutu 1.4.1");
					  declare				       
					     printed_message : constant printed_message_access := new printed_message_type '
					       (source => new string ' (Positive'image(Positive(Neural_Modulator.Plugin.id))),
						destination => new string ' (Positive'Image(Positive(Neural_Modulator.Plugin.device_id))),
						data_type => new string ' (control_type'image(control_of(Message))),
						channel => new string ' (channel_type'image(channel_of(Message))),
						data1 => new string ' (interfaces.c.long'image(data1(Message))),
						data2 => new string ' (interfaces.c.long'image(data2(Message))),
						hour => new string ' (formatting.image(date, true)),
						hexa_sum => new string ' (hex_image(Message)),
						long_sum => new string ' (long'image(Message)));
					  begin
					     --Put_Line("tutu 1.4.1.0");
					     --print(printed_message.all);
					     
					     Neural_Modulator.box.receive(printed_message);
					     --Put_Line("messages sended to message box");
					     
					  end;
					  --Put_Line("tutu 1.4.2");
					  
					  --Put_Line("tutu 1.4.3");
				       end if;
				       
				       Neural_modulator.Event_Process.Receive(Neural_Modulator.Plugin.Device_Id, date, Step_Vectors.Element(Step,I), 0.0, tempo);
				       
				    end;
				    
				    
				    
				 end loop;		  
				 
			      end if;	
			      
			   end;			   
			   
			   Start_Time := Start_Time + Quantum;
			   delay until Start_Time;
			end loop;		     
		     end if;
		     
		  else
		     
		     Start_Time := Start_Time + Quantum;
		     
		     --Put_Line("tatata 3");		     		     
		  end if;
		  -- end if;
		  --   end if;
		  --Put_Line("Neural_Modulator started ::= MIMI 0.1.275");
	       end select;	    
	       --Next(bar_beat, Options.Track_length, signature.number, signature.unit);		  
	       
	       
	    end loop;
	 end;
      end loop;
      Put_Line("Neural_Modulator halted.");
   end Neural_Modulator_Process;
   
end Libsens.Processing.Neural_Modulator;