with Ada.Integer_Text_Io;
with Ada.Text_Io;
use Ada;
package body Np.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  : Porttime.Time_Access := Pt_Time'Access;
      Time_Info  : System.Address;
      latency    : Long_Integer := 0;      
   begin
      case Device.Mode is
     
     when MIDI_In =>
        Pm_Error := Pm_OpenInput(Device.Addr, Device.Id, Device.The_DeviceInfo,
                     128, Time_Proc, Time_Info);
     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 : 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 : 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;
      Pm_Error := Pm_SetFilter(Device.Addr.all, Pm_Filt_Active or Pm_Filt_Clock);
      Pm_Error := Pm_Read(Device.Addr.All, Pm_Event, 1);            
      case Pm_Error is
     when PmNoError =>        
        Message := Pm_Event.Message;                        
     when others =>
        Text_Io.Put(PmError'Image(Pm_Error));
        raise Constraint_Error;
      end case;     
   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 Np.Midi.Devices;