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.Arp_Sequencer;     use Libsens.Virtual.Arp_sequencer;

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

with Libsens.REM_Common;                use Libsens.REM_Common;
with Libsens.Neural_Chord;              use Libsens.Neural_Chord;
with Libsens.Neural_Chord.Io;           use Libsens.Neural_Chord.Io;
with Libsens.Neural_Chord.Trainner;     use Libsens.Neural_Chord.Trainner;



with PragmARC.REM_NN_Wrapper;
use PragmARC.REM_NN_Wrapper;

with Ada.Numerics.Discrete_Random;

with Ada.Unchecked_Deallocation;

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_Arpegiator is
   
   
   procedure Free_Chord is new Ada.Unchecked_Deallocation(T_Chord, Chord_Access);
   
   package Value_Rand is new Ada.Numerics.Discrete_Random(Value_Type);
   
   
   procedure Initialize(Plugin_Process : in Neural_Arpegiator_Processing;Options : in Work_Options_Access) is
   begin
      Plugin_Process.Process.Initialize(Options);
   end Initialize;   
   
   
   procedure Start(Plugin_Process : in Neural_Arpegiator_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_Arpegiator_Processing) is
   begin
      Plugin_Process.Process.Stop;
   end Stop;
   
   procedure Halt(Plugin_Process : in Neural_Arpegiator_Processing) is
   begin
      Plugin_Process.Process.Halt;
   end Halt;
   
   
   task body Neural_Arpegiator_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;
      
      

      Message : Message_Type;
      Message_Note_On  : Long := 0;
      Message_Note_Off : Long := 0;
      
      
      Current_Step : Positive := 1;
      
      The_Chord : Chord_Access;
      
      Value_Gen : Value_Rand.Generator;
      
      use Value_Rand;
      
      Register : T_Register;
      
      
      Networks_Path : constant String := "./data/sources/";
      
      Network_Filename  : access String;
      
      
      Prob_File : Register_Io.File_Type;
      Prob_Flt : access String;
      
      Channel : Channel_Type := Long(Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Ch_Id-1);
      
   begin
      
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize(Options : in Work_Options_Access) do
		  Put_Line("Plugin id : " &
			     Plugin_Num'Image(Neural_Arpegiator.Plugin.Id) &
			     " ready for process " &
			     Plugin_Enum'Image(Neural_Arpegiator.Plugin.Class));
		  
		  Neural_Arpegiator_Process.Options := Options;
		  
		  
		  
		  Prob_flt:= new String ' (Networks_Path & Options.Filename.all & "-" & Plugin_Num'Image(Neural_Arpegiator.Plugin.Id) & Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network.all & ".pbm");            
		  
		  Put_Line(Networks_Path & Options.Filename.all & "-" &  Plugin_Num'Image(Neural_Arpegiator.Plugin.Id) & Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network.all);
		  
		  Network_Filename := new String ' (Networks_Path & Options.Filename.all & "-" &  Plugin_Num'Image(Neural_Arpegiator.Plugin.Id) & Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network.all);
		  
		  
		  
		  if Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network /= null and then
		    not Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Reuse then		     
		     
		     
		     Put_Line(Networks_Path & Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network.all);
		     Put_Line(Network_Filename.all);
		     
		     Read_From_File(Networks_Path & Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network.all, Network_Filename.all);
		     
		     Put_Line(Network_Filename.all);
		     
		     Train_From_File(Network_Filename.all,
				     Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Reuse,
				     Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Converged,
				     Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Max_Epoch);
		     
		     Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Reuse := True;
		  end if;
		  The_Chord := new T_Chord ' (Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Start_Chord.all);
		  --Put_Line("tutu 1.4.11");
		  Register := Chord_To_Register(The_Chord.all);
		  --Put_Line("tutu 1.4.12");
		  Register_Io.Create(Prob_File, Register_Io.Out_File, Prob_Flt.all);
		  --Put_Line("tutu 1.4.13");		     
		  Register_Io.write(Prob_File, Register);
		  
		  Register_Io.close(Prob_File);
		  
		  
		  
	       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_Arpegiator_Process.Tempo := Tempo;
		  Neural_Arpegiator_Process.Signature := Signature;
		  
		  Bar_Beat := (1, 1, 1);
		  Neural_Arpegiator_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_Arpegiator 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_Arpegiator_Process.Tempo := Tempo;
		  Neural_Arpegiator_Process.Signature := Signature;		  
		  Neural_Arpegiator_Process.Options := Options;
	       end Start;
	    or
	       accept  Stop;
	       exit;
	    or
	       accept Halt;
	       End_Of_Task := True;
	       exit;
	       
	       
	    or
	       delay until Start_Time;
	       --Put_Line("Neural_Arpegiator started ::= MIMI 0.1.1");
	       --if current_form = Neural_Arpegiator.plugin.played_form then
	       --  if Neural_Arpegiator.Plugin.Algo = Null_Algo then
	       if (not Neural_Arpegiator.plugin.mutted) 
		 and is_formed(Options.true_table,			       
			       Neural_Arpegiator.Plugin.Cat_id, 
			       Options.Prev_Break, 
			       Options.Next_Break) then
		  --Put_Line("Neural arpegiator started ::= MIMI 0.1.2");
		  
		  
		  if Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Step_Table(Current_Step).Set then
		     --Put_Line("Plantage 0.0");
		     --Put_Line("Plantage 0.1");
		     
		     --Put_Line("tutu 1.4.5");
		     if Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Network /= null then
			
			--Put_Line("tutu 1.4.6");
			Register := REM_NN_Expl(Network_Filename.all & ".wgt", 
						Prob_Flt.all);
			
			--Put_Line("tutu 1.4.7");
		     end if;
		     --Put_Line("tutu 1.4.8");
		     for I in Register'Range loop
			if Register(I) < 0.5 then
			   Register(I) := 0.0;
			elsif Register(I) >= 0.5 then
			   Register(I) := 1.0;
			end if;
		     end loop;
		     --Put_Line("tutu 1.4.9");
		     -------------------------------------------------------------------------------------------
		     Free_Chord(The_Chord);
		     --Put_Line("tutu 1.4.10");
		     --
		     The_chord := new T_Chord' (Register_To_Chord(Register));
		     
		     
		     Date := Clock;
		     
		     for I in The_Chord'range loop
			
			
			--Put_Line("The_Chord(i).Key : " & Value_type'image(The_Chord(i).Key));
			--Put_Line("The_Chord(i).Vel : " & Value_type'image(The_Chord(i).Vel));
			--Put_Line("The_Chord(i).Len : " & Value_type'image(The_Chord(i).len));
			
			if ((The_Chord(i).Key + Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Transpose > 0) and
			      (The_Chord(i).Key + Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Transpose < 128)) and
			  ((The_Chord(i).Vel > 0) and
			     (The_Chord(i).Vel < 128)) then
			   
			   
			   
			   
			   
			   Message := Note_On(Channel, The_Chord(i).Key + Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).transpose, The_Chord(i).vel);
			   --Intelligence.Driver.Receive(Message);
			   
			   
			   --Put_Line("Plantage 0.0.2");
			   Message_Note_On := To_Long(Message);
			   --Put_Line("Plantage 0.3");
			   if Neural_Arpegiator.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_Arpegiator.Plugin.id))),
				    destination => new string ' (Positive'Image(Positive(Neural_Arpegiator.Plugin.device_id))),
				    data_type => new string ' (control_type'image(control_of(Message_Note_On))),
				    channel => new string ' (channel_type'image(channel_of(Message_Note_On))),
				    data1 => new string ' (interfaces.c.long'image(data1(Message_Note_On))),
				    data2 => new string ' (interfaces.c.long'image(data2(Message_Note_On))),
				    hour => new string ' (formatting.image(date, true)),
				    hexa_sum => new string ' (hex_image(Message_Note_On)),
				    long_sum => new string ' (long'image(Message_Note_On)));
			      begin
				 --Put_Line("tutu 1.4.1.0");
				 --print(printed_Message_Note_On.all);
				 
				 Neural_Arpegiator.box.receive(printed_Message);
				 --Put_Line("Message_Note_On sended to message box");
				 
			      end;
			      --Put_Line("tutu 1.4.2");
			      
			      
			   end if;
			   
			   Neural_arpegiator.Event_Process.Receive(Neural_Arpegiator.Plugin.Device_Id, date, Message_Note_On, 0.0, tempo);
			   --Put_Line("tutu 1.4.3");
			   
			   --Put_Line("Plantage 0.0.2");
			   Message := Note_off(Channel, The_Chord(i).Key + Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).transpose);
			   --Intelligence.Driver.Receive(Message);
			   --Put_Line("Plantage 0.3");
			   
			   
			   Message_Note_Off := To_Long(Message);
			   
			   if Neural_Arpegiator.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_Arpegiator.Plugin.id))),
				    destination => new string ' (Positive'Image(Positive(Neural_Arpegiator.Plugin.device_id))),
				    data_type => new string ' (control_type'image(control_of(Message_Note_Off))),
				    channel => new string ' (channel_type'image(channel_of(Message_Note_Off))),
				    data1 => new string ' (interfaces.c.long'image(data1(Message_Note_Off))),
				    data2 => new string ' (interfaces.c.long'image(data2(Message_Note_Off))),
				    hour => new string ' (formatting.image(Date + quantum, true)),
				    hexa_sum => new string ' (hex_image(Message_Note_Off)),
				    long_sum => new string ' (long'image(Message_Note_Off)));
			      begin
				 ----Put_Line("tutu 1.4.1.0");
				 --print(printed_Message_Note_On.all);
				 
				 Neural_Arpegiator.box.receive(printed_Message);
				 ----Put_Line("Message_Note_Off sended to message box");
				 
			      end;
			      --Put_Line("tutu 1.4.2");
			      
			      
			   end if;
			   
			   Neural_arpegiator.Event_Process.Receive(Neural_Arpegiator.Plugin.Device_Id, Date+quantum, Message_Note_Off, quantum, tempo);
			   --Put_Line("tutu 1.4.3");
			   
			   
			   
			end if;
									
			
		     end loop;
		     
		  end if;
		  
		  
		  
		  Free_Chord(The_Chord);
		  --Put_Line("tutu 1.4.14");
		  The_chord := new T_Chord(1..3);
		  Reset(Value_Gen);
		  for I in The_Chord'Range loop
		     The_Chord(I) := new T_Note ' (Random(Value_Gen), Random(Value_Gen), Random(Value_Gen));
		  end loop;
		  
		  -------------------------------------------------------------------------------------------
		  
		  Register := Chord_To_Register(The_Chord.all);
		  Register_Io.Create(Prob_File, Register_Io.Out_File, Prob_Flt.all);
		  --Put_Line("tutu 1.4.11");
		  Register_Io.write(Prob_File, Register);
		  --Put_Line("tutu 1.4.12");
		  delay 0.015;
		  Register_Io.close(Prob_File);
		  --Put_Line("tutu 1.4.13");
		  
		  
		  if Current_Step = Arp_Seq_Plugin_Record(Neural_Arpegiator.Plugin.all).Last_step then
		     Current_Step := 1;
		  else
		     Current_Step := Current_Step + 1;
		  end if;
		  --Put_Line("tutu 1.4.15");
		  start_time := start_time + Quantum;
	       else
		  --Put_Line("tutu 1.4.16");
		  start_time := start_time + Quantum;
	       end if;		  
	       --Put_Line("tatata 3");		     		     		  		  
	       -- end if;
	       --   end if;
	       
	    end select;	    
	    
	    --Put_Line("Neural_Arpegiator loop..... ::= MIMI 0.1.275");
	    
	 end loop;
      end loop;
      Put_Line("Neural_Arpegiator halted ::= MIMI 0.1.276");
   end Neural_Arpegiator_Process;
   
end Libsens.Processing.Neural_Arpegiator;