with Libsens.MIDI.Messages;             use Libsens.MIDI.Messages;
use Libsens.MIDI;

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

with Libsens.Virtual.Nn_Plugin;         use Libsens.Virtual.Nn_Plugin;
with Libsens.Processing.Common;         use Libsens.Processing.Common;
with Libsens.Monophonic_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_Mono is
   
   procedure Initialize(Plugin_Process : in Neural_Mono_Processing;Options : in Work_Options_Access) is
   begin
      Plugin_Process.Process.Initialize(Options);
   end Initialize;   
   
   
   procedure Start(Plugin_Process : in Neural_Mono_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_Mono_Processing) is
   begin
      Plugin_Process.Process.Stop;
   end Stop;
   
   procedure Halt(Plugin_Process : in Neural_Mono_Processing) is
   begin
      Plugin_Process.Process.Halt;
   end Halt;
   
   task body Neural_Mono_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 Monophonic is 
	 new Libsens.Monophonic_Generator
	(Filename => Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Network_Name.all & Plugin_Num'Image(Neural_Mono.Plugin.Id),
	 Width => Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Width * 21,
	 Hidden_Num => Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Width,
	 Samples_Max => Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Sample_Max);   
      use Monophonic; use Monophonic.Neural2chord;
   begin
      
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize(Options : in Work_Options_Access) do
		  Monophonic.Initialize(Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Reuse, Float(Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Converged), Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Max_epoch);		  		  
		  Nn_Mono_Plugin_Record(Neural_Mono.Plugin.all).Reuse := True;
		  Put_Line("Plugin id : " &
			     Plugin_Num'Image(Neural_Mono.Plugin.Id) &
			     " ready for process " &
			     Plugin_Enum'Image(Neural_Mono.Plugin.Class));
		  Neural_Mono_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_Mono_Process.Tempo := Tempo;
		  Neural_Mono_Process.Signature := Signature;
		  
		  Bar_Beat := (1, 1, 1);
		  Neural_Mono_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 mono started ::= MIMI 0.1.0");
	 
	 
	 declare
	 
	    Response : Node_Set(1..21 * 4) := Monophonic.Generator.Problem_Rand;
	 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/1000.0;
		     Neural_Mono_Process.Tempo := Tempo;
		     Neural_Mono_Process.Signature := Signature;		  
		     Neural_Mono_Process.Options := Options;
		     Neural_Mono_Process.Start_Time := Start_Time;
		  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_Mono.plugin.played_form then
		  --  if Neural_Mono.Plugin.Algo = Null_Algo then
		  if (not Neural_Mono.plugin.mutted) 
		    and is_formed(Options.true_table,			       
				  Neural_Mono.Plugin.Cat_id, 
				  Options.Prev_Break, 
				  Options.Next_Break) then
		     --Put_Line("Neural mono started ::= MIMI 0.1.2");
		     
		     
		     Start_Time := Start_Time + (Quantum*Signature.Unit);
		     
		     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 MONOPHONIC              ***");
			--  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("**********************************************");
			Monophonic_Lock.Seize;
			Response := Monophonic.Respond(Monophonic.Generator.Problem_Rand);
			Response := Monophonic.Respond(response);
			Monophonic_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
			   
			   
			   -- Text_Io.Put_Line("********************* Step 1 ***********************");
			   declare
			      Note_On  : Long := Messages.To_Long(Messages.Note_On(Long(Neural_Mono.Plugin.Ch_Id-1), 
										   Long((Notes_Set(Note_Id).Key mod 36) + 24), 
										   Long(Notes_Set(Note_Id).Sens))); 
			      
			      
			      
			      
			      Note_Off : Long := Messages.To_Long(Messages.Note_Off(Long(Neural_Mono.Plugin.Ch_Id-1), 
										    Long((Notes_Set(Note_Id).Key mod 36) + 24)));
			      
			      
			      --  Note_On  : Long := To_Long(Messages.Note_On(Neural_Mono.Plugin.Channel, 
			      --  						   Long(Notes_Set(Note_Id).Key), 
			      --  						   Long(Notes_Set(Note_Id).Sens))); 
			      
			      --  Note_Off : Long := To_Long(Messages.Note_Off(Neural_Mono.Plugin.Channel, 
			      --  						    Long(Notes_Set(Note_Id).Key)));
			      
			      
			      Length   : Duration := (Quantum) * Natural((Long(Notes_Set(Note_Id).Length)));
			      
			   begin
			      -- Text_Io.Put_Line("********************* Step 1.0 *********************");
			      Date := Clock;				    
			      Neural_Mono.Event_Process.Receive(Neural_Mono.plugin.Device_Id, Date, Note_On, 0.0, Tempo);
			      Neural_Mono.Event_Process.Receive(Neural_Mono.plugin.Device_Id, Date, Note_Off, Length, Tempo);

			      -- Text_Io.Put_Line("********************* Step 1.1 *********************");
			      if Neural_Mono.Plugin.Printed then
				 
				 declare				       
				    Printed_Message : constant Printed_Message_Access := new Printed_Message_Type '
				      (Source => new String ' (Plugin_num'Image(Neural_Mono.Plugin.Id)),
				       Destination => new String ' (Device_num'Image(Neural_Mono.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_Mono.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_Mono.Plugin.Id)),
				       Destination => new String ' (Device_Num'Image(Neural_Mono.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_Mono.Box.Receive(Printed_Message);
				    --Text_Io.Put_Line("Messages sended to Message box");
				    
				 end;
			      end if;
			      
			      --Text_Io.Put_Line("********************* Step 1.2 *********************");
			      
			   end;
			   
			   delay Quantum;
			   start_time := start_time + Quantum;
			end loop;
			Free(Notes_Set);
			Response := Monophonic.Generator.Problem_Rand;
		     end;
		     
		  else
		     start_time := start_time + Quantum;
		     --Put_Line("tatata 3");		     		     
		  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);		  
	       
	       --Put_Line("step_seq started ::= MIMI 0.1.276");
	    end loop;
	 end;
	 Put_Line("Neural mono stopped::= MIMI 0.1.276");
      end loop;
      Put_Line("Neural mono halted::= MIMI 0.1.276");
   end Neural_Mono_Process;
   
end Libsens.Processing.Neural_Mono;