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.Polyphonic_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_Poly is
   
   procedure Initialize(Plugin_Process : in Neural_Poly_Processing;Options : in Work_Options_Access) is
   begin
      Plugin_Process.Process.Initialize(Options);
   end Initialize;   
   
   
   procedure Start(Plugin_Process : in Neural_Poly_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_Poly_Processing) is
   begin
      Plugin_Process.Process.Stop;
   end Stop;
   
   procedure Halt(Plugin_Process : in Neural_Poly_Processing) is
   begin
      Plugin_Process.Process.Halt;
   end Halt;
   
   
   task body Neural_Poly_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 Polyphonic is 
	 new Libsens.Polyphonic_Generator
	(Filename => Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Network_Name.all & Plugin_Num'Image(Neural_Poly.Plugin.Id),
	 Width => Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Width * 21,
	 Hidden_Num => Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Width,
	 Samples_Max => Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Sample_Max);   
      use Polyphonic; use Polyphonic.Neural2chord;
      
      Response : Node_Set(1..21 * 4) := Polyphonic.Generator.Problem_Rand;
      
   begin
      --Put_Line("Neural poly int ::= MIMI 0.1.0");
      
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize(Options : in Work_Options_Access) do
		  Polyphonic.Initialize(Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Reuse, Float(Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Converged), Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Max_epoch);
		  --Put_Line("Neural poly initialized ::= MIMI 0.1.0");
		  Nn_Poly_Plugin_Record(Neural_Poly.Plugin.all).Reuse := True;
		  --Put_Line("Neural poly reuse => TRUE ::= MIMI 0.1.0");
		  Put_Line("Plugin id : " &
			     Plugin_Num'Image(Neural_Poly.Plugin.Id) &
			     " ready for process " &
			     Plugin_Enum'Image(Neural_Poly.Plugin.Class));
		  Neural_Poly_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_Poly_Process.Tempo := Tempo;
		  Neural_Poly_Process.Signature := Signature;
		  
		  Bar_Beat := (1, 1, 1);
		  Neural_Poly_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 poly started ::= MIMI 0.1.0");
	 
	 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_Poly_Process.Tempo := Tempo;
		  Neural_Poly_Process.Signature := Signature;		  
		  Neural_Poly_Process.Options := Options;
		  Neural_Poly_Process.Start_Time := Start_Time;		  
	       end Start;
	    or
	       accept  Stop;
	       exit;
	    or
	       accept Halt;
	       End_Of_Task := True;
	       exit;
	    or
	       delay until Start_Time;
	       
	       --if current_form = Neural_Poly.plugin.played_form then
	       --  if Neural_Poly.Plugin.Algo = Null_Algo then
	       if (not Neural_Poly.plugin.mutted) 
		 and is_formed(Options.true_table,			       
			       Neural_Poly.Plugin.Cat_id, 
			       Options.Prev_Break, 
			       Options.Next_Break) then
		  --Put_Line("Neural poly ::= MIMI 0.1.2");
		  declare
		     
		     
		     
		     Notes_Set : Notes_Set_Access;
		     
		     
		     Date : Time := Start_Time;
		  begin
		     --  Text_Io.Put_Line("**********************************************");
		     --  Text_Io.Put_Line("***                                        ***");
		     --  Text_Io.Put_Line("***                                        ***");
		     --  Text_Io.Put_Line("***                                        ***");
		     --  Text_Io.Put_Line("***             NEW POLYPHONIC              ***");
		     --  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("**********************************************");
		     Polyphonic_Lock.Seize;
		     
		     --Response := Polyphonic.Respond(Polyphonic.Generator.Problem_Rand);
		     Response := Polyphonic.Respond(Response);
		     
		     Polyphonic_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 := To_Long(Messages.Note_On(Neural_Poly.Plugin.Channel, 
			   --  						Long(36 + Notes_Set(Note_Id).Key mod 24), 
			   --  						Long(Notes_Set(Note_Id).Sens))); 
			   Note_On  : Long := Messages.To_Long(Messages.Note_On(Long(Neural_Poly.Plugin.Ch_Id-1), 
								       Long(Notes_Set(Note_Id).Key), 
								       Long(Notes_Set(Note_Id).Sens))); 
			   
			   Note_Off : Long := Messages.To_Long(Messages.Note_Off(Long(Neural_Poly.Plugin.Ch_Id-1), 
									Long(Notes_Set(Note_Id).Key)));
			   
			   
			   
			   --  Note_Off : Long := To_Long(Messages.Note_Off(Neural_Poly.Plugin.Channel, 
			   --  						 Long(36 + Notes_Set(Note_Id).Key mod 24)));
			   
			   
			   Length   : Duration := (Quantum/16.0) * Natural((Long(Notes_Set(Note_Id).Length)));
			   
			begin
			   
			   
			   Date := Clock;
			   Neural_Poly.Event_Process.Receive(Neural_Poly.plugin.Device_Id, Date, Note_On, 0.0, Tempo);
			   Neural_Poly.Event_Process.Receive(Neural_Poly.plugin.Device_Id, Date, Note_Off, Length, Tempo);				    
			   
			   if Neural_Poly.Plugin.Printed then				 
			      declare				       
				 Printed_Message : constant Printed_Message_Access := new Printed_Message_Type '
				   (Source => new String ' (Plugin_num'Image(Neural_Poly.Plugin.Id)),
				    Destination => new String ' (Device_num'Image(Neural_Poly.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_Poly.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_Poly.Plugin.Id)),
				    Destination => new String ' (Device_num'Image(Neural_Poly.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_Poly.Box.Receive(Printed_Message);
				 --Text_Io.Put_Line("Messages sended to Message box");
				 
			      end;
			   end if;
			   
			end;				
			delay Quantum * Signature.Unit;
			start_time := start_time + Quantum * Signature.Unit;
		     end loop;			      
		     Free(Notes_Set);
		     Response := Polyphonic.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);		  
	    
	    --Put_Line("step_seq started ::= MIMI 0.1.276");
	 end loop;
      
	 Put_Line("Neural poly stopped::= MIMI 0.1.276");
      end loop;
      Put_Line("Neural poly halted::= MIMI 0.1.276");
   end Neural_Poly_Process;
   
end Libsens.Processing.Neural_Poly;