with PragmARC.Genetic_Algorithm;

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

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

with Libsens.Frame;

with Ada.Calendar.Formatting;

with Text_Io;                           use Text_Io;

with Interfaces.C;                      use Interfaces.C;

package body Libsens.Processing.Genetical_Synth is
   
   procedure Initialize(Plugin_Process : in Genetical_Synth_Processing;Options : in Work_Options_Access) is
   begin
      Plugin_Process.Process.Initialize(Options);
   end Initialize;   
   
   
   procedure Start(Plugin_Process : in Genetical_Synth_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 Genetical_Synth_Processing) is
   begin
      Plugin_Process.Process.Stop;
   end Stop;
   
   procedure Halt(Plugin_Process : in Genetical_Synth_Processing) is
   begin
      Plugin_Process.Process.Halt;
   end Halt;
   
   
   task body Genetical_Synth_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 Synth_Frame is
	 new Libsens.Frame
	
	(
	 Max_B_Inf => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Max_B_Inf,
	 Max_B_Sup => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Max_B_Sup,
	 Min_Class_Id => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Min_Class_Id,
	 Min_Value_Id => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Min_Value_Id,
	 Min_Data_Id => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Min_Data_Id,
	 Max_Class_Id => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Max_Class_Id,
	 Max_Value_Id => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Max_Value_Id,
	 Max_Data_Id => Gen_Synth_Plugin_Record(Genetical_Synth.Plugin.all).Max_Data_id
	);
	 
	 use Synth_Frame, Synth_Frame.Frame_Rand;
	 procedure Genetic_Frame_Algorithm is new PragmARC.Genetic_Algorithm (Synth_Frame.Frame_Type, 
									      "=", 
									      Synth_Frame.Frame_Rand.Frame_Random,
									      Frame_Fitness,
									      Frame_Mate,
									      Frame_Mutate);
      
      
      
   begin
      while not End_Of_Task loop
	 loop
	    select
	       accept Initialize(Options : in Work_Options_Access) do
		  Put_Line("Plugin id : " &
			     Plugin_Num'Image(Genetical_Synth.Plugin.Id) &
			     " ready for process " &
			     Plugin_Enum'Image(Genetical_Synth.Plugin.Class));
		  Genetical_Synth_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;
		  Genetical_Synth_Process.Tempo := Tempo;
		  Genetical_Synth_Process.Signature := Signature;
		  
		  Bar_Beat := (1, 1, 1);
		  Genetical_Synth_Process.Start_Time := Start_Time;		  
	       end Start;
	       exit;
	    or
	       accept  Stop;
	    or
	       accept Halt;
	       End_Of_Task := True;
	       exit;
	    end select;
	 end loop;
	 
	 
	 declare
	    
	    
	 begin
	    
	    --Put_Line("Genetical synth 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;
		     Genetical_Synth_Process.Tempo := Tempo;
		     Genetical_Synth_Process.Signature := Signature;		  
		     Genetical_Synth_Process.Options := Options;
		  end Start;
	       or
		  accept  Stop;
		  exit;
	       or
		  accept Halt;
		  End_Of_Task := True;
		  exit;
	       or
		  delay until Start_Time;
		  --Put_Line("Genetical synth started ::= MIMI 0.1.1");
		  --if current_form = Genetical_Synth.plugin.played_form then
		  --  if Genetical_Synth.Plugin.Algo = Null_Algo then
		  if (not Genetical_Synth.plugin.mutted) 
		    and is_formed(Options.true_table,			       
				  Genetical_Synth.Plugin.Cat_id, 
				  Options.Prev_Break, 
				  Options.Next_Break) then
		     --Put_Line("Genetical Synth started ::= MIMI 0.1.2");
		     declare
			Context : Genetic_Context_Type;
			
			
			Best_Frame : Synth_Frame.Frame_Type;
			Fit_Frame  : Float := 0.0;
			
			
			Date : Time := Clock;
		     begin
			
			Synth_Frame.Frame_Rand.Reset;
			Genetic_Frame_Algorithm(Population_Size           => Context.Population_Size,
						Max_Generations           => Context.Max_Generations,
						Num_No_Change_Generations => Context.Num_No_Change_Generations,
						Mutation_Probability      => Context.Mutation_Probability,
						Num_Elite_Saved           => Context.Num_Elite_Saved,
						Best                      => Best_Frame,
						Fit                       => Fit_Frame);	       	       
			
			for Digit_Id in Best_Frame.B_Inf..Best_Frame.B_Sup loop
			   declare
			      
			      
			      
			      
			      Message_Note_On  : Long := To_Long(Note_On(Long(Genetical_Synth.Plugin.Ch_Id-1), 
									 Long(Best_Frame.Digital_Series(Digit_Id).Position.Digit.Class_Id), 
									 Long(Best_Frame.Digital_Series(Digit_Id).Position.Digit.Value_Id))); 
			      
			      
			      
			      
			      Message_Note_Off : Long := To_Long(Note_Off(Long(Genetical_Synth.Plugin.Ch_Id-1), 
									  Long(Best_Frame.Digital_Series(Digit_Id).Position.Digit.Class_Id)));
			      
			      Length   : Duration := (Quantum) * Natural((Long(Best_Frame.Digital_Series(Digit_Id).Position.Digit.Data_Id)));
			      
			   begin
			      
			      
			      Date := Clock;
			      Genetical_Synth.Event_Process.Receive(Genetical_Synth.plugin.Device_Id, Date, Message_Note_On, 0.0, Tempo);
			      Genetical_Synth.Event_Process.Receive(Genetical_Synth.plugin.Device_Id, Date, Message_Note_Off, Length, Tempo);

			      
			      if Genetical_Synth.Plugin.Printed then
				 
				 declare				       
				    Printed_Message : constant Printed_Message_Access := new Printed_Message_Type '
				      (Source => new String ' (Positive'Image(positive(Genetical_Synth.Plugin.Id))),
				       Destination => new String ' (Positive'Image(Positive(Genetical_Synth.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(Start_time, True)),
				       Hexa_Sum => new String ' (Hex_Image(Message_Note_On)),
				       Long_Sum => new String ' (Long'Image(Message_Note_On)));
				 begin
				    
				    --Print(Printed_Message);
				    
				    Genetical_Synth.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 ' (Positive'Image(positive(Genetical_Synth.Plugin.Id))),
				       Destination => new String ' (Positive'Image(Positive(Genetical_Synth.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(Start_time+length, True)),
				       Hexa_Sum => new String ' (Hex_Image(Message_Note_Off)),
				       Long_Sum => new String ' (Long'Image(Message_Note_Off)));
				 begin
				    
				    --Print(Printed_Message);
				    
				    Genetical_Synth.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;
		     end;			       
		  else
		     start_time := start_time + Quantum;
		     --Put_Line("tatata 3");		     		     
		  end if;
	       -- end if;
	       --   end if;
	       --Put_Line("Genetical synth started ::= MIMI 0.1.275");
	       end select;	    	       
	   
	       --Put_Line("Genetical synth started ::= MIMI 0.1.276");
	    end loop;
	 end;
      end loop;
   end Genetical_Synth_Process;
   
end Libsens.Processing.Genetical_Synth;