-- computerman is multiway user tools.
-- Computerman is Copyright (C) 2024 Manuel De Girardi ; 
--
--   This program is free software; you can redistribute it and/or modify
--   it under the terms of the GNU General Public License as published by
--   the Free Software Foundation; either version 2 of the License, or
--   (at your option) any later version.
--
--   This program is distributed in the hope that it will be useful,
--   but WITHOUT ANY WARRANTY; without even the implied warranty of
--   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--   GNU General Public License for more details.
--
--   You should have received a copy of the GNU General Public License
--   along with this program; if not, write to the Free Software
--   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
--
-- Date := "2024-05-10 09:03:04"
-- Version := "0.0.0r"
with Ada.Integer_Text_Io;
   
with Ada.Text_Io;
use Ada;


with Computer.MIDI.Porttime;
use Computer.MIDI.Porttime;

package body Computer.Midi.Devices is
      use ErrorText_Conversion, DeviceInfo_Conversion;
   procedure Initialize (Device : in out Device_Type; Mode : in Mode_type) is
      
   begin
      case Mode is
         when MIDI_Out =>
            Device.The_DeviceInfo := new Portmidi.DeviceInfo;
            Device.Addr := new System.Address;
            loop
               begin        
		  Text_Io.Put_Line("Select output device :" );
		  Text_Io.Put_Line("ID, Name" );
		  for I in 0..Portmidi.Pm_CountDevices-1 loop
		     declare
			Name : Portmidi.T_ErrorText;
		     begin
			Device.The_DeviceInfo.all :=
			  To_pointer(Portmidi.Pm_GetDeviceInfo(I)).all;
			if Device.The_Deviceinfo.Output = 1 then
			   Name := To_Pointer(Device.The_Deviceinfo.name).all;
			   Text_Io.Put_Line(Integer'Image(I) & ", " & Interfaces.C.To_Ada(Name));
			end if;
		     end;
		  end loop;
		  Text_Io.Put("Entre device ID : " );
		  Integer_Text_Io.Get(Device.Id);
		  Device.The_DeviceInfo.all :=
		    To_pointer(Portmidi.Pm_GetDeviceInfo(Device.id)).all;
		  if Device.The_Deviceinfo.Output = 1 Then
		     declare
			Name : Portmidi.T_ErrorText;
		     begin
			Device.Initialized := True;
			
			Device.Mode := MIDI_Out;
			Name := To_Pointer(Device.The_Deviceinfo.name).all;
			Text_Io.Put_Line("Your selected device :" );
			Text_Io.Put(Interfaces.C.To_Ada(Name));        
		     end;
		     exit;
		  else
		     Text_Io.Put_Line("!! ********************** !!" );
		     Text_Io.Put_Line("!! Device not initialized !!" );
		     Text_Io.Put_Line("!! ********************** !!" );
		  end if;
		  
               exception
		  when Text_Io.End_Error =>
		     return;
               end;
            end loop;            
         when MIDI_In =>
            Device.The_DeviceInfo := new Portmidi.DeviceInfo;
            Device.Addr := new System.Address;
	    
            loop
               begin        
		  Text_Io.Put_Line("Select input device :" );
		  Text_Io.Put_Line("ID, Name" );
		  for I in 0..Portmidi.Pm_CountDevices-1 loop
		     declare
			Name : Portmidi.T_ErrorText;
		     begin
			Device.The_DeviceInfo.all :=
			  To_pointer(Portmidi.Pm_GetDeviceInfo(I)).all;
			if Device.The_Deviceinfo.Input = 1 then
			   Name := To_Pointer(Device.The_Deviceinfo.name).all;
			   Text_Io.Put_Line(Integer'Image(I) & ", " & Interfaces.C.To_Ada(Name));
			end if;
		     end;
		  end loop;
		  Text_Io.Put("Entre device ID : " );
		  Integer_Text_Io.Get(Device.Id);
		  Device.The_DeviceInfo.all :=
		    To_pointer(Portmidi.Pm_GetDeviceInfo(Device.id)).all;
		  if Device.The_Deviceinfo.Input = 1 Then
		     declare
			Name : Portmidi.T_ErrorText;
		     begin            
			Device.Initialized := True;
			Device.Mode := MIDI_In;
			Name := To_Pointer(Device.The_Deviceinfo.name).all;
			Text_Io.Put_Line("Your selected device :" );
			Text_Io.Put(Interfaces.C.To_Ada(Name));        
		     end;
		     exit;
		  else
		     Text_Io.Put_Line("!! ********************** !!" );
		     Text_Io.Put_Line("!! Device not initialized !!" );
		     Text_Io.Put_Line("!! ********************** !!" );
		  end if;
		  
               exception
		  when Text_Io.End_Error =>
		     return;
               end;
            end loop;            
      end case;
   end Initialize;
   
   function  Initialized(Device : in     Device_Type) return Boolean is
   begin
      return Device.Initialized;
   end Initialized;
   
   procedure Open       (Device : in out Device_Type) is
      
      Pm_Error   : PmError;
      Time_Proc  : constant Porttime.Time_Access := Pt_Time'Access;
      Time_Info  : System.Address;
      Buffer : constant Integer := 4096;
      latency    : constant Long_Integer := 0;      
   begin
      if not Initialized(Device) then
	 raise Not_Initialized;
      end if;
      case Device.Mode is
         
         when MIDI_In =>
            Pm_Error := Pm_OpenInput(Device.Addr, Device.Id, Device.The_DeviceInfo,
				     buffer, Time_Proc, Time_Info);
	    Pm_Error := Pm_SetFilter(Device.Addr.all, Pm_Filt_Active or Pm_Filt_Clock);
         when MIDI_Out =>
            
            Pm_Error := Pm_OpenOutput(Device.Addr, Device.Id, Device.The_DeviceInfo,
				      0, Time_Proc, Time_Info, Latency);
      end case;
   end Open;
   
   
   procedure Write      (Device : in     Device_Type; Message : in C.Long) is
      
      Pm_Error : PmError;
      Pm_Event : constant PmEvent := (Message, 0);
   begin
      if not Device.Initialized then
         raise Not_Initialized;
      end if;
      if Device.Mode /= MIDI_Out then
         raise Mode_Error;
      end if;      
      Pm_Error := Pm_Write(Device.Addr.all, Pm_Event, 1);
   end Write;
   
      
   procedure Read       (Device : in     Device_Type; Message : out C.Long) is
      Pm_Error : PmError := PmNoError;
      Pm_Event : constant PmEvent_Access := new PmEvent;
      
   begin     
      if not Device.Initialized then
         raise Not_Initialized;
      end if;
      if Device.Mode /= MIDI_In then
         raise MODE_Error;
      end if;
      Message := 0;
      loop
	 
   	 Pm_Error := Pm_Poll(Device.Addr.all);      	 
   	 if Pm_Error /= PmNoError then
   	    Pm_Error := Pm_Read(Device.Addr.All, Pm_Event, 1);            	    		  	       		  	    	    
   	 else
   	    exit;
   	 end if;
      end loop;	   
      loop
   	 Pm_Error := Pm_Poll(Device.Addr.all);                  
   	 if Pm_Error /= Pmnoerror  then	    	    	    
   	    Pm_Error := Pm_Read(Device.Addr.All, Pm_Event, 1);           
   	    if Pm_Error /= Pmnoerror then
   	       Message := Pm_Event.Message;	    	    	       	 
   	       return;
   	    end if;	 
   	 end if;      
   	 delay 0.0005;
      end loop;
   end Read;
   
   procedure Close      (Device : in out Device_Type) is
      Pm_Error : PmError;
   begin      
      Pm_Error := Pm_Close(Device.Addr.all);      
   end Close;
   
   function Name(Device : in Device_Type) return String is
   begin
      return C.To_Ada(To_Pointer(Device.The_Deviceinfo.name).all);
   end Name;
   Pm_Error : PmError;
begin      
   Pm_Error := Pm_Initialize;

end Computer.Midi.Devices ;