with Libsens.Data.Neural, Libsens.Data.Neural.Network;
use Libsens.Data.Neural;

with PragmARC.Rem_NN_Wrapper;
use PragmARC.Rem_NN_Wrapper;


with Ada.Numerics.Float_Random;
use Ada.Numerics.Float_Random;
with Ada.Numerics.Discrete_Random;

with Ada.Text_Io;
use Ada;

package body Libsens.Data.Drums is
   
   package Bool_Rand is new Ada.Numerics.Discrete_Random(Boolean);   
   
   Bool_Gen : Bool_Rand.Generator;
   
   function Tribe(Den : in Den_Type) return Word16_Type is
      
      Resolution : Positive := 2**Den;
      Word16     : Word16_Type := 0;
   begin
      
      for I in 0..(Word16_Type'Size-1) loop
	 if I mod resolution = 0 then
	    Word16 := Word16 + 2**I;
	    --Text_Io.Put_Line("bit à 1");
	 end if;
      end loop;
      return Word16;
   end Tribe;
   
   
   function Rand(Den : in Den_Type; Length : in Positive) return Word16_Type is
      
      Resolution : Positive := 2**Den;
      Word16     : Word16_Type := 0;
   begin
      
      for I in (Word16_Type'Size-1)-Length..Word16_Type'Size-1 loop
	 if I mod (64/Resolution) = 0 then
	    --Text_Io.Put_Line("mod 2 = 0");
	    if Bool_Rand.Random(Bool_Gen) then
	       Word16 := Word16 + 2**I;
	       --Text_Io.Put_Line("bit à 1");
	    end if;
	 end if;
      end loop;         
      return Word16;
   end Rand;
   
   Network_Size : constant Positive := 16;
   Hidden_Num   : constant Positive := 16;
   
   package Drums_Network is new Libsens.Data.Neural.Network(Network_Size, Hidden_Num);   
   use Drums_Network;         
   
   procedure Rand_Init(Samples : in out Samples_Type) is
      Polyphony : Natural := 0;
      Float_Gen : Generator;
   begin
      Reset(Float_Gen);
      for I in Samples'Range loop
	 for J in reverse 1..Network_Size loop	   
	    if Real(Random(Float_Gen)) >= 0.5 then
	       Samples(I)(J) := 1.0;
	       Polyphony := Polyphony + 1;
	    else
	       Samples(I)(J) := 0.0;
	    end if;	    
	    exit when Polyphony > 5;
	 end loop;
	 
	 Polyphony := 0;
      end loop;
   end Rand_Init;
   
   function Problem_Rand return Node_Set is
      Set : Node_Set(1..Network_Size) := (others => 0.0);
      Float_Gen : Generator;
   begin
      Reset(Float_Gen);
      for I in Set'Range loop
	 if Real(Random(Float_Gen)) >= 0.5 then
	    Set(I) := 1.0;
	 else
	    Set(I) := 0.0;
	 end if;

      end loop;
      return Set;
   end Problem_Rand;
   
   ------------------------
   -- initialize pattern --
   ------------------------               
   procedure Init (Pattern : out Drums_Sentence_Type) is
   begin
      for I in Pattern'Range loop
	 Pattern(I).Key := I;
      end loop;
   end Init;
   
   
   package Value_Rand is new Ada.Numerics.Discrete_Random(Value_Type);
   
   use Seq_vectors;                                
   
   
   package Drums_Value_Rand is new Ada.Numerics.Discrete_Random(Drums_Value_Type);
   
     
   task body Drums_Gen is
      
      
      End_Of_Task : Boolean := False;
      Drums_Sentence, Drums_Pattern, Drums_network : Drums_Sentence_Type;
      
      
      Foot : constant Drums_Value_type := 36;
      Foot2: constant Drums_Value_type := 37;            
      Rim1  : constant Drums_Value_type := 40;
      Rim2  : constant Drums_Value_type := 42;
      Hats1: constant Drums_Value_type := 47;
      Hats2: constant Drums_Value_type := 38;
      Fx1  : constant Drums_Value_type := 50;
      Fx2  : constant Drums_Value_type := 51;
      
      
      Drums_Gen : Drums_Value_Rand.Generator;
      
      Rand_Key : Drums_Value_type := 36;
      
      
      Power_Time : Boolean := True;
      
      Networks_Path : constant String := "./data/networks/";
      
      Network_Name : constant String := "-mod-Drums.wgt";
      
      Network_Filename  : access String;
      
      Problem, Response : Node_Set(1..Network_Size) := (others => 0.0);
      
      
      
      Melodic_Samples : Samples_Type(1..128) := (others => (others => 0.0));
      Value_Gen : Value_Rand.Generator;
      
      The_Seq : Seq_Vectors.Vector;
      
   begin 
            
      while not End_Of_Task loop
	 loop
	    select
	       
	       accept Halt do
		  End_Of_Task := True;
	       end Halt;
	       exit;
	    or
	       
	       accept Initialize(Filename : in String; Reuse : in Boolean; Converged : in Float; Max_Epochs : in Positive) do
		  --Text_Io.Put_Line("Rand initializing");            
		  Rand_Init(Melodic_Samples);
		  --Text_Io.Put_Line("initialize done");            
		  --Text_Io.Put_Line("trainning drums");            
		  
		  Network_Filename := new String ' (Networks_Path & Filename & Network_Name);
		  
		  if not Reuse then
		     Train(Network_Filename.all,
			   Melodic_Samples,
			   Reuse,
			   Real(Converged),
			   Max_Epochs);
		  end if;
		  --Text_Io.Put_Line("Trainning drums done.");            
	       end Initialize;	       
	       
	       exit;
	    end select;
	 end loop;
      
      
	 
	 while not End_Of_Task loop
	    
	    select   	       
	       
	       accept Halt do
		  End_Of_Task := True;
	       end Halt;
	       exit;
	       
	    or
	       accept Respond(Filename : in String; Channel : in Channel_Type; Seq : in out Seq_Vectors.Vector) do
		  Drums_Pattern(foot).Key := Foot;
		  Drums_Pattern(foot).Sens := 100;
		  Drums_Pattern(foot).Sentence := 16#0001#;
		  Drums_Pattern(Rim1).Key := Rim1;
		  Drums_Pattern(Rim1).Sens := 85;
		  Drums_Pattern(Rim1).Sentence := Rime;
		  Drums_Pattern(Rim2).Key := Rim2;
		  Drums_Pattern(Rim2).Sens := 100;
		  Drums_Pattern(Rim2).Sentence := 16#1101#;
		  Drums_Pattern(hats1).Key := Hats1;
		  Drums_Pattern(hats1).Sentence := Hat_roll;
		  Drums_Pattern(hats2).Key := Hats2;
		  Drums_Pattern(hats2).Sentence := Rand(4, 2**4/2);
		  Drums_Pattern(foot2).Key := foot2;
		  Drums_Pattern(foot2).Sens := 95;
		  Drums_Pattern(foot2).Sentence := 16#0001#;
		  
		  Drums_sentence(foot).Key := Foot;
		  Drums_sentence(foot).Sens := 100;
		  Drums_sentence(foot).Sentence := 16#0001#;
		  Drums_Sentence(Rim1).Key := Rim1;
		  Drums_Sentence(Rim1).Sens := 85;
		  Drums_Sentence(Rim1).Sentence := 0;
		  Drums_Sentence(Rim2).Key := Rim2;
		  Drums_Sentence(Rim2).Sens := 100;
		  Drums_Sentence(Rim2).Sentence := 16#1101#;
		  Drums_Sentence(hats1).Key := Hats1;
		  Drums_Sentence(hats1).Sentence := Hat_roll;
		  Drums_Sentence(hats2).Key := Hats2;
		  Drums_Sentence(hats2).Sentence := Rand(4, 2**4/2);
		  Drums_Sentence(foot2).Key := foot2;
		  Drums_Sentence(foot2).Sens := 95;
		  Drums_Sentence(foot2).Sentence := 16#0000#;-- or Rand(Transporter.Signature.Den, 2**Transporter.Signature.Den/2);
		  
		  Drums_Network := (others => Null_Note);
		  Init(Drums_Network);      
		  Problem := Problem_Rand;

		  for I in Word16_Index_type loop
		     
		     if I = 0 or I = 8 or I  = 12 then    
			Response := Respond(Networks_Path & Filename & Network_Name, Problem);
			for J in Response'Range loop
			   if Response(J) >= 0.7 then
			      Response(J) := 1.0;
			      
			   else
			      Response(J) := 0.0;
			      
			   end if;
			end loop;
			To_Sentence(Response, I, Drums_Network);		     		     			
			--  Problem := Problem_Rand;
			--  for J in Response'Range loop
			--  	  if Response(J) = 1.0 then
			--  	     Problem(J) := 1.0;
			--  	  end if;
			--  end loop;
		     end if;
		  end loop;
		  
		  for I in Drums_Pattern'Range loop		  
		     Drums_Network(I).Sens := 100; --Value_Rand.Random(Value_Gen);
		     Drums_Network(I).Length := 8;--Value_Rand.Random(Value_Gen);
						  --Text_Io.Put_Line(Word64_type'Image(Drums_Network(I).Sentence));
		  end loop;
		  
		  
		  --Drums_Sentence(hats2).Sentence := Rand(4, 2**4/2);
		  
		  
		  
		  
		  
		  --  Rand_Key := Drums_Value_Rand.Random(Drums_Gen);
		  --  Drums_Sentence(Rand_Key).Key := Rand_Key;
		  --  Drums_Sentence(Rand_Key).Sens := Value_Rand.Random(Value_Gen);
		  --  Drums_Sentence(Rand_Key).Sentence := Rand(Transporter.Signature.Den, 2**Transporter.Signature.Den/2);
		  
		  
		  if Power_Time then
		     
		     Pattern(From => Drums_Sentence, Channel => Channel, Seq => The_Seq);    		     
		     
		  else
		     Pattern(From => Drums_pattern, Channel => Channel, Seq => The_Seq);    		     
		  end if;
		  Power_Time := not Power_Time;
		  --Text_Io.Put_Line("Respond drums for channel " & Channel_Type'Image(Channel) & " tutut 4");            
		  Pattern(From => Drums_Network, Channel => Channel, Seq => The_Seq);
		  
		  
		  --seq.Gate.Release;
		  --Text_Io.Put_Line("Respond drums for channel " & Channel_Type'Image(Channel) & " tutut 5");            
		  --or delay 1.0;
		  --end select;
		  
		  
		  Seq := The_Seq;
		  The_Seq := Seq_Vectors.Empty_Vector;
	       end Respond;
	    or
	       accept Initialize(Filename : in String; Reuse : in Boolean; Converged : in Float; Max_Epochs : in Positive) do
		  Text_Io.Put_Line("Rand initializing");            
		  Rand_Init(Melodic_Samples);
		  Text_Io.Put_Line("initialize done");            
		  Text_Io.Put_Line("trainning drums");            
		  
		  Network_Filename := new String ' (Networks_Path & Filename & Network_Name);
		  
		  
		  Train(Network_Filename.all,
			Melodic_Samples,
			Reuse,
			Real(Converged),
			Max_Epochs);

		  Text_Io.Put_Line("Trainning drums done.");            
	       end Initialize;
	       Init(Drums_Pattern);      
	       Init(Drums_Network);      

	       
	       
	       --Date := Date + Quantum(Transporter);	       	       	       	       
	    end select;	    
	 end loop;      
      end loop;
   end Drums_Gen;
   
end Libsens.Data.Drums;