with Gmface.Gm_Common;                  use Gmface.Gm_Common;
package Gmface.Gm_Virtual is
   
   -----------------------------------------------
   -- Gmface Options;
   
   type Form_Index_Type is new Positive range 1..8;
   
   -- The forms used on mixed forms.
   type Forms_Names_Set_Type is array (Form_Index_Type) of access String;
   
   Forms_Names_Set : constant Forms_Names_Set_Type := 
     (
      new String ' ("Minimal"),      
      new String ' ("Hard tech"),                  
      new String ' ("Acid"),
      new String ' ("Transe"),      
      new String ' ("Techno"),
      new String ' ("Tribe"),      
      new String ' ("Drums and Bass"),
      new String ' ("Hard Core"));
   
   type Category_Index_Type is new Positive range 1..16;
   
   type Categories_Set_Type is array (Category_Index_Type range <>) of access String;
   
   
   Categories_Default : constant Categories_Set_Type(1..16) :=
     (new String ' ("lead"),
      new String ' ("bass"),	 
      new String ' ("string N pad"),
      new String ' ("Guitar N Pluck"),
      new String ' ("Fx"),
      new String ' ("synth hard"),
      new String ' ("synth soft"),
      new String ' ("keyboard"),
      new String ' ("hit N drum"),
      new String ' ("drums kit"),
      new String ' ("audio in"),
      new String ' ("motion"),	 
      new String ' ("bell N Decay"),
      new String ' ("user"),
      new String ' ("arp seq"),
      new String ' ("voccoder")
     );
   
   type Categories_Record(Category_Max : Category_Index_Type) is tagged
      record
	 Category : Categories_Set_type(1..Category_Max);
	 Last       : Category_Index_type := 1;
      end record;
   
   --type Categories_Access is access all Categories_Record'Class;
   
   type Break_Type is (Null_Break, Down_Break, Up_Break, Full_Break);            
   
   
   type True_Table_Type is array (Category_Index_Type, Break_Type, Break_Type) of Boolean;
   
   procedure True_Table_Initialize(True_Table : in out True_Table_Type);
   
   function Is_Formed(True_Table : True_Table_Type;			 		      
		      Category_Index : Category_Index_Type;
		      Prev       : Break_Type := Null_Break;
		      Next       : Break_Type := Null_Break) return Boolean;
   
   
   type Breaks_Set_Type is array (Positive range <>) of Break_Type;
   
   type Categories_Access is access Categories_Record;
   
   type Breaks_Set_Access is access Breaks_Set_Type;
   
   
   
   --
   -----------------------------------------------
   
   
   -- Abstract Work Class
   subtype Work_Num is Positive range 1..128;
   
   type Work_Options_Record is tagged
      record
	 Filename : access String := new String ' ("");
	 
	 
	 -------------------------------
	 --
	 Track_Length : Positive := 129;
	 Tempo      : Tempo_Type := 120.0;
	 Signature  : Time_Signature_Type := (4, 4);	    
	 In_Loop    : Boolean := False;	 	 
	 Current_Form : Form_Index_Type := 1;
	 Current_Break    : Break_Type := Down_Break;
	 Prev_Break       : Break_Type := Null_Break;	 
	 Next_Break       : Break_Type := Down_Break;
	 Forms_Names    : Forms_Names_Set_Type := Forms_Names_Set;
	 Categories      : Categories_Access := new Categories_Record ' (16, Categories_Default, 16);
	 Breaks_Set : Breaks_Set_Access := new Breaks_Set_Type ' (Null_Break, Down_Break,
								  Up_Break, Full_Break,
								  Down_Break, Null_Break,
								  Up_Break, Null_Break,
								  Full_Break, Full_Break,
								  Down_Break, Null_Break,
								  Full_Break, Down_Break);
	 True_Table : True_Table_Type;
      end record;
   
   type Work_Options_Access is access all Work_Options_Record'Class;
   
   
   -- Abstract Plugin Class
   type Device_Num is new Positive range 1 .. 128;
   
   type Channel_num is new Positive range 1 .. 16;

   type Plugin_num is new Positive range 1 .. 256;
   
   type Abstract_Plugin(Class : Plugin_Enum) is abstract tagged
      record
	 Id        : Plugin_Num := 1;
	 Printed   : Boolean := False;
	 Mutted    : Boolean := False;	 
	 
	 Cat_Id    : Category_Index_Type := 1;	 	 
	 ---------------------------
	 --
	 Name      : access String := new String ' ("");	 
	 Device_ID : Device_Num := 1;
	 Ch_ID     : Channel_Num := 1;	 	 	 
	 
	 --------------------------
	 --
	 Form_Id   : Form_Index_Type := 1;
      end record;
   
   type Abstract_Plugin_Access is access all Abstract_Plugin'Class;
   
   procedure Print(Filename : in String; Plug : in Abstract_Plugin) is abstract;   
         
   type Plugin_Class is abstract new Abstract_Plugin with
      record
	 null;
      end record;
   
   
   type Plugin_Set is array (Plugin_num'range) of Abstract_Plugin_Access;
   
   type Work_Class is abstract tagged
      record
	 Class      : Work_Enum := Gmface_Work;
	 Id         : Work_Num := 1;	 
	 Started    : Boolean := False;
	 Filename   : access String := new String ' ("");
	 Plugins    : Plugin_Set;
      end record;   
   
   procedure Initialize(Work : in out Work_Class'Class;
			Work_Id : in Work_Num;
			Filename : in String) is abstract;
   
   type Work_Access is access all Work_Class'Class;
   
   
   type Work_Array is array (Work_Num'Range) of Work_Access;
   
   
   
   procedure Initialize (Plug      : in out Plugin_Class;
			 Id        : in Plugin_num;
			 Device    : in Device_Num;
			 Ch        : in Channel_Num);
   
   procedure Set_Id(Plug       : in out Plugin_Class;
		    Id        : in Plugin_Num);
   
   function Get_Id(Plug        : in Plugin_Class) return Plugin_Num;
   
   procedure Set_Device(Plug       : in out Plugin_Class;
			Device    : in Device_Num);
   
   function Get_Device(Plug        : in Plugin_Class) return Device_Num;
   
   procedure Set_Channel(Plug       : in out Plugin_Class;
			 Ch        : in Channel_Num);
   
   function Get_Channel(Plug        : in Plugin_Class) return Channel_Num;         
   
   procedure Print (Filename : in String; Plug : in Plugin_Class);      
   
   
end Gmface.Gm_Virtual;