  MEMBER()
  INCLUDE('unisoft.INC')

  INCLUDE('KEYCODES.CLW')

HANDLE                  EQUATE(UNSIGNED)
DWORD                   EQUATE(ULONG)
LPCSTR                  EQUATE(CSTRING)    !Usage:Pass the Label of the LPCSTR
HWND                    EQUATE(HANDLE)
LPSTR                   EQUATE(CSTRING)    !Usage:Pass the Label of the LPSTR

  MAP
      OMIT('***',_WIDTH32_)
      INCLUDE('FILE16.CLW')
      ***

    MODULE('MMSYSTEM.LIB')
      mmsystemGetVersion(),LONG,RAW,PASCAL
      OMIT('***',_WIDTH32_)
   mciSendString(*LPCSTR,*LPSTR,UNSIGNED,hwnd),DWORD,PASCAL,RAW
       ***
       COMPILE('***',_WIDTH32_)
   mciSendString(*LPCSTR,*LPSTR,UNSIGNED,hwnd),DWORD,PASCAL,RAW,NAME('mciSendStringA')
       ***

     OMIT('***',_WIDTH32_)
   mciGetErrorString(DWORD,*LPSTR,UNSIGNED),BOOL,PASCAL,RAW
       ***
       COMPILE('***',_WIDTH32_)
   mciGetErrorString(DWORD,*LPSTR,UNSIGNED),BOOL,PASCAL,RAW,NAME('mciGetErrorStringA')
       ***
       COMPILE('***',_WIDTH32_)
         CopyFileA(*LPCSTR,*LPCSTR,BOOL),BOOL,PASCAL,RAW
         DeleteFileA(*LPCSTR),BOOL,PASCAL,RAW
       ***
      OMIT('***',_WIDTH32_)
         sndPlaySound(*CString,UShort),UShort,pascal,raw
       ***
      COMPILE('***',_WIDTH32_)
         sndPlaySound(*CString,UShort),UShort,pascal,raw,NAME('sndPlaySoundA')
       ***
         WinExec(*CSTRING,USHORT),USHORT,RAW,PASCAL
         GetModuleUsage(USHORT),USHORT,RAW,PASCAL
    END
 END!MAP



!----------------------------------------------------
!----- Clase Archivo --------------------------------
!----------------------------------------------------
Archivo.Pick  PROCEDURE
 CODE
 IF NOT LEN(CLIP(Self.Extension))
    MisErrores.ThrowMessage(Msg:ClaseArchivo,'El Archivo no tiene seteada la extension')
 END!IF
 IF NOT FILEDIALOG('Seleccione Archivo ',Self.Nombre,Clip(Self.Tipo)&'|*.'&Self.Extension)
    MisErrores.ThrowMessage(Msg:ClaseArchivo,'ERROR EN SELECCION DE ARCHIVO')
 END!IF


Archivo.Copiar PROCEDURE(String InputDestino)
Destino CSTRING(128)
 CODE
 Destino = InputDestino
       COMPILE('***',_WIDTH32_)
 i#=CopyFileA(Self.Nombre,Destino,False)
       ***
       OMIT('***',_WIDTH32_)
 i#=CopyFile(Self.Nombre,Destino,False)
       ***
 IF NOT i#
    MisErrores.ThrowMessage(Msg:ClaseArchivo,'HUBO UN ERROR AL COPIAR ARCHIVO!')
 END!IF

Archivo.Borrar PROCEDURE
 CODE
   COMPILE('***',_WIDTH32_)
 IF NOT DeleteFileA(Self.Nombre)
    ***
    OMIT('***',_WIDTH32_)
 IF NOT Del(Self.Nombre)
    ***
    MisErrores.ThrowMessage(Msg:ClaseArchivo,'HUBO UN ERROR AL BORRAR ARCHIVO!')
 END!IF


! ********** Seccion Multimedia ***********

MCIClass.Comando PROCEDURE(String Accion)
AccionCumplida BYTE
MCIString      CSTRING(128)
 CODE
 MCIString  = Accion
 Self.Error = mciSendString(MCIString,Self.Respuesta, 0, 0)
 IF Self.Error
    IF MciGetErrorString(Self.Error, Self.ErrorTexto, LEN(Self.ErrorTexto)-1)
        MisErrores.ThrowMessage(Msg:FalloComando,Self.ErrorTexto)
    ELSE
        MisErrores.Throw(Msg:FalloComando)
    END
    Self.Comando = 'Error!'
    AccionCumplida = False
 ELSE
    Self.Comando = Accion
    AccionCumplida = True
 END!IF
 RETURN(AccionCumplida)


MCIClass.Pick     PROCEDURE
   CODE
   IF Self.Existe
      IF NOT Self.Comando('close '& Self.Nombre)
      END!IF
   END!IF
   Parent.Pick()
   Self.Estado    = Stop


MCIClass.Play     PROCEDURE
  CODE
  IF NOT Self.Comando('play '& Self.Nombre &' from 0 ')
     Self.Estado = Stop
  ELSE
     Self.Estado = Play
  END

MCIClass.Rewind PROCEDURE
 CODE

MCIClass.Pick_Play PROCEDURE
 CODE
 Self.Pick
 Self.Play

MCIClass.Stop     PROCEDURE
 CODE
 i#=Self.Comando('stop '  & Self.Nombre)
 i#=Self.Comando('close ' & Self.Nombre)
 Self.Estado = Stop

MCIClass.Foward   PROCEDURE
 CODE

MCIClass.Pause   PROCEDURE
 CODE
 CASE Self.Estado
   OF Play
      IF NOT Self.Comando('pause '& Self.Nombre)
         Self.Estado = Play
      ELSE
         Self.Estado = Pause
      END!IF
   OF Pause
      IF NOT Self.Comando('resume '& Self.Nombre)
         Self.Estado = Stop
      ELSE
         Self.Estado = Play
      END!IF
 END!CASE
 RETURN

MCIClass.StepByStep   PROCEDURE
 CODE
 i# = Self.Comando ( 'step '&Clip(Self.Nombre) & ' by ' & Self.Step )


MCIClass.Estado PROCEDURE
Comando CSTRING(100)
 CODE
 Comando = 'status '& Clip(Self.Nombre) & ' mode'

 Self.Estado = mciSendString(Comando,Self.Respuesta, 0, 0)
 Message(Self.Estado)

MCIClass.Destruct   PROCEDURE
 CODE
 IF Self.Estado
    !MESSAGE( 'Voy a ejecutar el Destructor de la Clase MCI Objeto:'&Self.Nombre)
    i#=Self.Comando('close ' & Self.Nombre)
 END!IF
! *********************************************************************
! ****************** Clase SONIDO *************************************
! *********************************************************************

Sonido.Construct PROCEDURE()
 CODE
 Self.Extension = 'WAV'
 Self.Tipo      = 'Sonidos'
 Self.Esperar    = False

 ! Este método es necesario para Relacionar el objeto Global que maneja la lista de errores
 ! al estar en distinto Scope es necesario pasar al objeto como puntero.
 ! Esta técnica la utiliza tambien la librería ABC

Sonido.Pick PROCEDURE()
 CODE
 Parent.Pick()
 IF NOT Self.Comando('open ' & Self.Nombre & ' alias '& Clip(Self.Nombre) & ' type waveaudio')
    Self.Existe = False
 ELSE
    Self.Existe = True
 END!IF

Sonido.Record PROCEDURE()
 CODE
 IF NOT Self.Comando ( 'record '&Self.Nombre)
    MESSAGE('No Pudo grabar el Sonido!')
 END!IF

Sonido.Play PROCEDURE()
WaitString   STRING(10)
 CODE
  IF Self.Esperar THEN WaitString='wait'.
  !MiProfilerLocal.Inicio
  IF NOT Self.Comando('play '& Self.Nombre &' from 0 ' & Clip(WaitString))
     Self.Estado = Stop
  ELSE
     Self.Estado = Play
  END
  !MiProfilerLocal.Fin('Fin de Tocar Wav : ' & Self.Nombre)


! *********************************************************************
! ****************** Clase VIDEO * ************************************
! *********************************************************************

Video.Construct PROCEDURE()
 CODE
 Self.Extension = 'AVI'
 Self.Tipo      = 'Videos AVI'
 Self.Step      = 1 ! usado para el metodo Step (cuadro a cuadro)

Video.Init  PROCEDURE
 CODE
 MESSAGE('EL VIDEO REQUIERE DE UN HANDLE| Utilice el Metodo Video.Init(Long)','NO ES POSIBLE!')

Video.Init  PROCEDURE(LONG WinHandle)
 CODE
 Self.WinHandle = WinHandle

Video.Pick  PROCEDURE
 CODE
 IF NOT Self.WinHandle
    MESSAGE('FALTA DEFINIR EL HANDLE DE LA VENTANA')
 END!IF
 Parent.Pick()

 IF NOT Self.Comando('open ' & Self.Nombre & ' alias '& Clip(Self.Nombre) &' parent ' & Self.WinHandle)
    Self.Existe = False
 ELSE
    Self.Existe = True
 END!IF
 i#=Self.Comando('window '&Self.Nombre& ' handle '& Self.WinHandle & ' text "Tocando el Avi" ')


! *********************************************************************
! ****************** Clase PROFILER ***********************************
! *********************************************************************

ProfilerClass.Inicio PROCEDURE()
 CODE
 MESSAGE( 'La Clase Sonido le esta pidiendo a la Clase Profiler|que Registre sus actividades en un Archivo de Log','Colaboración',ICON:EXCLAMATION)
 Self.Mascara    = '@t4'
 Self.HoraInicio = Clock()
 IF Self.UsarLog
    OPEN(ProfilerClass_Log)
    IF ERRORCODE()
       CREATE(ProfilerClass_Log)
       IF ERRORCODE() THEN STOP(ERROR()).
       OPEN(ProfilerClass_Log)
    END!IF
 END!IF

ProfilerClass.Fin PROCEDURE(STRING Descripcion)
 CODE
 Self.HoraFin    = CLOCK()
 Self.Tiempo = Self.HoraFin - Self.HoraInicio
 IF Self.UsarVentana
    MESSAGE('El tiempo transcurrido fue de :|'& FORMAT(Self.Tiempo,Self.Mascara),'Profiler Class',ICON:Exclamation)
 END!IF

 IF Self.UsarLog
    Plog:Fecha           = FORMAT( TODAY()          , '@d6'         )
    PLog:Inicio          = FORMAT( Self.HoraInicio  , Self.Mascara  )
    PLog:Fin             = FORMAT( Self.HoraFin     , Self.Mascara  )
    PLog:Descripcion     = Descripcion
    PLog:Tiempo          = FORMAT( Self.Tiempo      , Self.Mascara  )
    ADD(ProfilerClass_Log)
    IF ERRORCODE() THEN STOP(ERROR()).
    CLOSE(ProfilerClass_Log)
 END!IF

ProfilerClass.MostrarLog PROCEDURE()
 CODE
 ! Este es un ejemplo perfecto para comprender el concepto de encapsulamiento
 ! Sabemos que la clase Profiler tiene que tener una responsabilidad que sea
 ! MostrarLog pero seleccionamos el método de implementación mas sencillo.
 ! Seguramente en una segunda etapa este método tenga una mejor implementación
 ! y eso no impactará en lo mas mínimo en nuestros desarrollos.
 ! Ellos usan el metodo, si el metodo cambia su implementación no nos importa.
 RUN( 'NOTEPAD profiler.log')

!---------------------------------------------------
!---------------------------------------------------
!---------------------------------------------------
SonidosColeccion.Construct         PROCEDURE
  CODE
  Self.Items &= NEW(MiQueueSonidos) !ojo POR SER VARIABLE DE REFERENCIA
                                    !TIENE QUE SER CREADO
                                    !DINAMICAMENTE CON UN NEW()
SonidosColeccion.Destruct          PROCEDURE
  CODE
  X#=RECORDS ( Self.Items)
  LOOP UNTIL X#=0
      GET(Self.Items,X#)
      DISPOSE(Self.Items.UnSonido)
      X#-=1
  END
  FREE(Self.Items)
  DISPOSE(Self.Items)               !ojo POR SER VARIABLE DE REFERENCIA
                                    !TIENE QUE SER destruido
                                    !DINAMICAMENTE CON UN DISPOSE()

SonidosColeccion.CantItems PROCEDURE()
 CODE
 RETURN( RECORDS ( Self.Items) )

SonidosColeccion.Agregar PROCEDURE(String Descripcion)
 CODE

  Self.Items.NroArchivo  = Self.CantItems() + 1
  Self.Items.Descripcion = Descripcion
  Self.Items.UnSonido  &= NEW(Sonido)
  Self.Items.UnSonido.Nombre    = '*.WAV'
  
  Self.Items.UnSonido.Pick()
  Self.Items.UnSonido.Play()

  ADD(Self.Items)
  IF ERRORCODE() THEN STOP(ERROR()).

SonidosColeccion.Borrar PROCEDURE(LONG NroItem)
  CODE
  GET(Self.Items,NroItem)
  IF ~ERRORCODE() THEN 
    DISPOSE(Self.Items.UnSonido)
    DELETE(Self.Items)
  END
SonidosColeccion.PlayItem PROCEDURE(LONG NroItem)
 CODE
 GET(Self.Items,NroItem)
 IF ERRORCODE() THEN STOP(ERROR()).
 Self.Items.UnSonido.Play()


!---------------------------------------------------
! Clases !!!!!!BONUS!!!!!!
!---------------------------------------------------

!---------------------------------------------------
!--- EditClassConEnter -----------------------------
!--- Permite usar el el ENTER para moverse por los
!--- campos en EDIT IN PLACE ENTRY
!---------------------------------------------------

EditClassConEnter.TakeEvent PROCEDURE(UNSIGNED Event)
Evento  UNSIGNED
  CODE
    ! Esto permite reusar la lógica del padre
    Evento = Parent.TakeEvent(Event())
    CASE Evento
      ! Si detecto el evento Complete entonces lo remapeo
      OF EditAction:Complete           
         RETURN EditAction:Forward
      ELSE
         ! Para todos los otros casos devuelvo lo que devuelve el padre
         RETURN(Evento)
     END!CASE

EditClassSpin.Init PROCEDURE(UNSIGNED FieldNumber,UNSIGNED ListBox,*? UseVar)
  CODE
  Self.Feq = CREATE(0,Create:Spin)
  ASSERT(~ErrorCode())
  Self.Feq{Prop:Text}       = ListBox{PropList:Picture,FieldNumber}
  Self.Feq{Prop:Use}        = UseVar
  Self.Feq{Prop:Alrt , 1 }  = TabKey
  Self.Feq{Prop:Alrt , 2 }  = ShiftTab
  Self.Feq{Prop:Alrt , 3 }  = EnterKey
  Self.Feq{Prop:Alrt , 4 }  = EscKey
  Self.Feq{Prop:RangeLow}   = 1
  Self.Feq{Prop:RangeHigh}  = 999999
  RETURN

EditClassCheck.Init PROCEDURE(UNSIGNED FieldNumber,UNSIGNED ListBox,*? UseVar)
  CODE
  Self.Feq = CREATE(0,Create:Check)
  ASSERT(~ErrorCode())
  Self.Feq{Prop:Text}       = ListBox{PropList:Header,FieldNumber}
  Self.Feq{Prop:Use}        = UseVar
  Self.Feq{Prop:Alrt , 1 }  = TabKey
  Self.Feq{Prop:Alrt , 2 }  = ShiftTab
  Self.Feq{Prop:Alrt , 3 }  = EnterKey
  Self.Feq{Prop:Alrt , 4 }  = EscKey
  !Self.Feq{Prop:Alrt , 5 }  = SpaceKey
  RETURN


EditClassDrop.Init PROCEDURE(UNSIGNED FieldNumber,UNSIGNED ListBox,*? UseVar)
  CODE
  IF NOT LEN(CLIP(Self.PropFrom))
     Self.PropFrom = 'Bueno|Regular|Malo'
  END!IF
  Self.Feq = CREATE(0,Create:DropCombo)
  ASSERT(~ErrorCode())
  Self.Feq{Prop:Text}       = ListBox{PropList:Picture,FieldNumber}
  Self.Feq{Prop:Use}        = UseVar
  Self.Feq{Prop:From}       = Self.PropFrom
  Self.Feq{Prop:Alrt , 1 }  = TabKey
  Self.Feq{Prop:Alrt , 2 }  = ShiftTab
  Self.Feq{Prop:Alrt , 3 }  = EnterKey
  Self.Feq{Prop:Alrt , 4 }  = EscKey
  Self.Feq{Prop:RangeLow}   = 1
  Self.Feq{Prop:RangeHigh}  = 999999
  RETURN

DireccionClass.Construct    PROCEDURE()
    CODE
    SELF.Dir &=NEW(StringClass)
    SELF.Texto=''
    SELF.Calle=''
    SELF.Numero=0
    SELF.NumeroHasta=0
    SELF.Piso =''
    SELF.Departamento=''

DireccionClass.Destruct    PROCEDURE()
    CODE
    DISPOSE(SELF.Dir)

DireccionClass.Init    PROCEDURE()
    CODE
    SELF.Dir.Init()
    SELF.Calle=''
    SELF.Numero=0
    SELF.NumeroHasta=0
    SELF.Piso =''
    SELF.Departamento=''

DireccionClass.Interpretar  PROCEDURE(String DireccionEnTexto)
!DEVUELVE 1 SI PARSEA CALLE Y NUMERO, 2 SI PARSEA ADEMAS PISO Y 3 DEPARTAMENTO
!DEVUELVE 0 SI NO PARSEA POR LO MENOS CALLE Y NUMERO
    CODE
    SELF.Init()
    SELF.Texto   = DireccionEnTexto
    SELF.Dir.Text= SELF.Texto
    SELF.CALLE=SELF.DIR.NextToken()
    IF UPPER(CLIP(SELF.CALLE))='CALLE' THEN
        SELF.CALLE= CLIP(SELF.CALLE) & ' ' & SELF.DIR.NextToken()
    END!IF

    LOOP
        SELF.DIR.NextToken()
        CASE UPPER(CLIP (SELF.DIR.Token))
        OF 'NRO' OROF 'NUMERO' OROF 'N¦' OROF 'NO' OROF 'NUM'
            SELF.DIR.NextToken()
        END!CASE

        IF CLIP (SELF.DIR.Token)='' THEN RETURN 0.
        IF NUMERIC(SELF.DIR.Token) THEN BREAK.
        SELF.CALLE=CLIP(SELF.CALLE) & ' ' & CLIP(SELF.DIR.Token)
    END

!NUMERO
    IF NUMERIC(SELF.DIR.Token)
        SELF.Numero=SELF.DIR.Token
    ELSE
        RETURN 0
    END!IF

!2¦ NUMERO
    IF NUMERIC(SELF.DIR.NextToken())
        CASE SUB(SELF.DIR.TEXT,SELF.DIR.SelStart -1 ,1)
            OF '/' OROF '\' OROF '-'
                SELF.NumeroHasta=SELF.DIR.Token
            ELSE!CASE
                SELF.DIR.PrevToken()
        END!CASE
    ELSE
        SELF.DIR.PrevToken()
    END!IF

!PISO
    LOOP
        SELF.DIR.NextToken()
        IF CLIP (SELF.DIR.Token)='' THEN RETURN 1.
        IF NUMERIC(SELF.DIR.Token)
            SELF.Piso=SELF.DIR.Token
            BREAK
        END!IF
    END!LOOP

!Departamento
    LOOP
        SELF.DIR.NextToken()
        IF CLIP (SELF.DIR.Token)=''
            IF CLIP(SELF.Departamento)=''
                RETURN 2
            ELSE
                RETURN 3
            END!IF
        END!IF

        IF CLIP(SELF.Departamento)=''
            SELF.Departamento=CLIP(SELF.Departamento) & CLIP(SELF.DIR.Token)
        ELSE
            SELF.Departamento=CLIP(SELF.Departamento) & CLIP(SELF.DIR.Token)
        END!IF
    END

    RETURN 3
