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



with Libsens.Virtual.Nn_Plugin;         use Libsens.Virtual.Nn_Plugin;
with Libsens.Processing.Common;         use Libsens.Processing.Common;


---------------------------------------
with Libsens.Rythmic_Generator;   --
---------------------------------------





with PragmARC.REM_NN_Wrapper;          use PragmARC.REM_NN_Wrapper;






with Ada.Calendar.Formatting;

with Text_Io;                           use Text_Io;

with Interfaces.C;                      use Interfaces.C;
package body Libsens.Processing.Neural_Rythm is
   
   procedure Initialize(Plugin_Process : in Neural_Rythm_Processing; Options : in Work_Options_Access) is
   begin
      Plugin_Process.Process.Initialize(Options);
   end Initialize;   
   
   
   procedure Start(Plugin_Process : in Neural_Rythm_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_Rythm_Processing) is
   begin
      Plugin_Process.Process.Stop;
   end Stop;
   
   procedure Halt(Plugin_Process : in Neural_Rythm_Processing) is
   begin
      Plugin_Process.Process.Halt;
   end Halt;
   
   
   task body Neural_Rythm_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 Rythmic is 
	 new Libsens.Rythmic_Generator
	(Filename => Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Network_Name.all  & Plugin_Num'Image(Neural_rythm.Plugin.Id),
	 Width => Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Width * 21,
	 Hidden_Num => Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Width,
	 Samples_Max => Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Sample_Max);   
      use Rythmic; use Rythmic.Neural2chord;
   begin
      
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize(Options : in Work_Options_Access) do
		  Rythmic.Initialize(Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Reuse, Float(Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Converged), Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Max_epoch);		  
		  Nn_Rythm_Plugin_Record(Neural_Rythm.Plugin.all).Reuse := True;
		  Put_Line("Plugin id : " &
			     Plugin_Num'Image(Neural_Rythm.Plugin.Id) &
			     " ready for process " &
			     Plugin_Enum'Image(Neural_Rythm.Plugin.Class));
		  Neural_Rythm_Process.Options := Options;
	       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/1000.0;
		  Neural_Rythm_Process.Tempo := Tempo;
		  Neural_Rythm_Process.Signature := Signature;
		  
		  Bar_Beat := (1, 1, 1);
		  Neural_Rythm_Process.Start_Time := Start_Time;		  		  		  
	       end Start;
	       exit;
	    or
	       accept  Stop;
	    or
	       accept Halt;
	       End_Of_Task := True;
	       exit;
	    end select;
	 end loop;
	 --Put_Line("Neural rythm started ::= MIMI 0.1.0");
	 declare
	    
	    Response : Node_Set(1..21 * 4) := Rythmic.Generator.Problem_Rand;
	 begin

	    while not End_Of_Task loop
	       select
		  accept Initialize(Options : in Work_Options_Access) do
		     Neural_Rythm_Process.Options := Options;
		  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/1000.0;
		     Neural_Rythm_Process.Tempo := Tempo;
		     Neural_Rythm_Process.Signature := Signature;		  
		     
		  end Start;
	       or
		  accept  Stop;
		  exit;
	       or
		  accept Halt;
		  End_Of_Task := True;
		  exit;
	       or
		  delay until Start_Time;
		  --Put_Line("step_seq started ::= MIMI 0.1.1");
		  --if current_form = Neural_Rythm.plugin.played_form then
		  --  if Neural_Rythm.Plugin.Algo = Null_Algo then
		  if (not Neural_Rythm.plugin.mutted) 
		    and is_formed(Options.true_table,			       
				  Neural_Rythm.Plugin.Cat_id, 
				  Options.Prev_Break, 
				  Options.Next_Break) then
		     --Put_Line("Neural rythm started ::= MIMI 0.1.2");
		     declare
			
			
			Notes_Set : Notes_Set_Access;
			
			
			Date : Time := Clock;
		     begin
			--  Text_Io.Put_Line("**********************************************");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***             NEW RYTHMIC                ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("**********************************************");			   

			--  Text_Io.Put_Line("**********************************************");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***               RESPOND                  ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");			   
			--  Text_Io.Put_Line("**********************************************");
			Rythmic_Lock.Seize;
			--Response := Rythmic.Respond(Rythmic.Generator.Problem_Rand);
			Response := Rythmic.Respond(Response);
			Rythmic_Lock.Release;
			--  Text_Io.Put_Line("**********************************************");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***           NEURAL TO CHORD              ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("**********************************************");			   
			Notes_Set := Neural2chord.To_Chord(Response);
			
			--  Text_Io.Put_Line("**********************************************");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***             PLAYING NOTES              ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("***                                        ***");
			--  Text_Io.Put_Line("**********************************************");			   
			
			
			for Note_Id in Notes_Set'range loop
			   declare
			      
			      Note_On  : Long := Messages.To_Long(Messages.Note_On(Long(Neural_Rythm.Plugin.Ch_Id-1), 
										   Long(36 + Notes_Set(Note_Id).Key mod 32), 
										   Long(Notes_Set(Note_Id).Sens))); 
			      
			      
			      
			      
			      Note_Off : Long := Messages.To_Long(Messages.Note_Off(Long(Neural_Rythm.Plugin.Ch_Id-1), 
										    Long(36 + Notes_Set(Note_Id).Key mod 32)));
			      
			      Length   : Duration := (Quantum/16.0) * Natural((Long(Notes_Set(Note_Id).Length)));
			      
			   begin
			      
			      Date := Clock;
			      Neural_Rythm.Event_Process.Receive(Neural_Rythm.plugin.Device_Id, Date, Note_On, 0.0, Tempo);
			      Neural_Rythm.Event_Process.Receive(Neural_Rythm.plugin.Device_Id, Date, Note_Off, Length, Tempo);

			      
			      
			      if Neural_Rythm.Plugin.Printed then
				 
				 declare				       
				    Printed_Message : constant Printed_Message_Access := new Printed_Message_Type '
				      (Source => new String ' (Plugin_num'Image(Neural_Rythm.Plugin.Id)),
				       Destination => new String ' (Device_num'Image(Neural_Rythm.Plugin.Device_Id)),
				       Data_Type => new String ' (Control_Type'Image(Control_Of(Note_On))),
				       Channel => new String ' (Channel_Type'Image(Channel_Of(Note_On))),
				       Data1 => new String ' (Interfaces.C.Long'Image(Portmidi.Data1(Note_On))),
				       Data2 => new String ' (Interfaces.C.Long'Image(Portmidi.Data2(Note_On))),
				       Hour => new String ' (Formatting.Image(Date, True)),
				       Hexa_Sum => new String ' (Hex_Image(Note_On)),
				       Long_Sum => new String ' (Long'Image(Note_On)));
				 begin
				    
				    --Print(Printed_Message);
				    
				    Neural_Rythm.Box.Receive(Printed_Message);
				    --Text_Io.Put_Line("Messages sended to Message box");
				    
				 end;
				 
				 declare				       
				    Printed_Message : constant Printed_Message_Access := new Printed_Message_Type '
				      (Source => new String ' (Plugin_num'Image(Neural_Rythm.Plugin.Id)),
				       Destination => new String ' (Device_num'Image(Neural_Rythm.Plugin.Device_Id)),
				       Data_Type => new String ' (Control_Type'Image(Control_Of(Note_Off))),
				       Channel => new String ' (Channel_Type'Image(Channel_Of(Note_Off))),
				       Data1 => new String ' (Interfaces.C.Long'Image(Portmidi.Data1(Note_Off))),
				       Data2 => new String ' (Interfaces.C.Long'Image(Portmidi.Data2(Note_Off))),
				       Hour => new String ' (Formatting.Image(Date+length, True)),
				       Hexa_Sum => new String ' (Hex_Image(Note_Off)),
				       Long_Sum => new String ' (Long'Image(Note_Off)));
				 begin
				    
				    --Print(Printed_Message);
				    
				    Neural_Rythm.Box.Receive(Printed_Message);
				    --Text_Io.Put_Line("Messages sended to Message box");
				    
				 end;
			      end if;
			      
			      
			      delay Quantum * Signature.Unit;
			      start_time := start_time + Quantum * Signature.Unit;
			   end;
			end loop;			      
			Free(Notes_Set);
			Response := Rythmic.Generator.Problem_Rand;
		     end;
		     
		     --Put_Line("tatata 3");		     		     
		  else
		     start_time := start_time + Quantum;
		  end if;
		  -- end if;
		  --   end if;
		  --Put_Line("step_seq started ::= MIMI 0.1.275");
	       end select;	    
	       Next(bar_beat, Options.Track_length, signature.number, signature.unit);		  
	       
	       
	    end loop;
	    Put_Line("Neural rythm stopped::= MIMI 0.1.276");
	 end;
      end loop;
      Put_Line("Neural rythm halted::= MIMI 0.1.276");
   end Neural_Rythm_Process;
   
end Libsens.Processing.Neural_Rythm;