/*
 * Xailer source code:
 *
 * Ini.prg
 * Clase TIni
 *
 * Copyright 2003, 2007 Jose Lalin
 * Copyright 2003, 2007 Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"


//--------------------------------------------------------------------------

CLASS XIni FROM TComponent

PUBLISHED:
   PROPERTY cFileName   INIT ""  EDITOR PE_BrowseFile

PUBLIC:
   PROPERTY nBuffer     INIT  1024 * 32 - 1 // 32Kb - 1

   METHOD New( cFileName )      INLINE ::Create( cFileName )
   METHOD Create( cFileName ) CONSTRUCTOR

   METHOD GetEntry( cSection, cEntry, uDefault )   // --> uValue | uDefault
   METHOD SetEntry( cSection, cEntry, uValue, lTrim )  // --> Nil

   METHOD GetString( cSection, cEntry ) ; // --> cString
      INLINE ::GetEntry( cSection, cEntry, "" )

   METHOD GetNumeric( cSection, cEntry ) ; // --> nNumber
      INLINE ::GetEntry( cSection, cEntry, 0 )

   METHOD GetDate( cSection, cEntry ) ; // --> dDate
      INLINE ::GetEntry( cSection, cEntry, Date() )

   METHOD GetLogical( cSection, cEntry ) ; // --> lResult
      INLINE ::GetEntry( cSection, cEntry, .F. )

   METHOD GetArray( cSection, cEntry, cSeparator ) // --> aArray
   METHOD SetArray( cSection, cEntry, aData, cSeparator ) // --> Nil

   METHOD DeleteEntry( cSection, cEntry ) // --> Nil

   METHOD AddSection( cSection ) ; // --> Nil
      INLINE ( ::SetEntry( cSection, "__" + cSection + "__", "" ), ;
               ::DeleteEntry( cSection, "__" + cSection + "__", "" ) )

   METHOD SetSection( cSection, aValues ) // --> Nil
   METHOD SetSectionEx( cSection, cString, cDelimiter ) // --> Nil

   METHOD DeleteSection( cSection ) // --> Nil

   METHOD GetSection( cSection ) // --> aKeys
   METHOD GetSectionEx( cSection ) // --> cString

   METHOD GetSections() // --> aSections
   METHOD GetSectionsEx() ; // --> cString
      INLINE IIf( ! Empty( ::cFileName ), GetPrivateProfileSectionNames( ::cFileName, ::nBuffer ), "" )

   METHOD EnumSectionEntries( cSection ) // --> aEntries
   METHOD EnumSectionValues( cSection ) // --> aValues

   METHOD RenameSection( cSection, cNewSection ) // --> Nil
   METHOD SortSection( cSection ) // --> Nil

   METHOD Commit() // --> Nil

   METHOD DeleteSections() // --> Nil

   METHOD SystemChanged( cSection ) ; // --> Nil
      INLINE SendMessage( HWND_BROADCAST, WM_SETTINGCHANGE, 0, cSection )

ENDCLASS

//--------------------------------------------------------------------------

METHOD Create( cFileName ) CLASS XIni

   UPDATE ::cFileName TO cFileName

RETURN Self

//--------------------------------------------------------------------------

METHOD SetEntry( cSection, cEntry, uValue, lTrim ) CLASS XIni

   LOCAL cData := ToString( uValue )

   DEFAULT lTrim TO .T.

   IF lTrim
      cData := Alltrim( cData )
   ENDIF

   IF Empty( ::cFileName )
      WriteProfileString( cSection, cEntry, cData )
   ELSE
      WritePrivateProfileString( cSection, cEntry, cData, ::cFileName )
   ENDIF

RETURN Nil

//--------------------------------------------------------------------------

METHOD GetEntry( cSection, cEntry, uDefault ) CLASS XIni

   LOCAL uReturn
   LOCAL cType := ValType( uDefault )

   DEFAULT uDefault TO ""

   IF Empty( ::cFileName )
      uReturn := GetProfileString( cSection, cEntry, ToString( uDefault ), ::nBuffer )
   ELSE
      uReturn := GetPrivateProfileString( cSection, cEntry, ToString( uDefault ), ::cFileName, ::nBuffer )
   ENDIF

   DO CASE
      CASE cType == "N"
         uReturn := Val( uReturn )
      CASE cType == "D"
         uReturn := CToD( uReturn )
      CASE cType == "L"
         uReturn := Upper( uReturn ) $ ".T."
   ENDCASE

RETURN uReturn

//--------------------------------------------------------------------------

METHOD SetSection( cSection, aValues ) CLASS XIni

   IF Len( aValues ) > 0 .AND. Valtype( aValues[ 1 ] ) != "A"
      AEval( aValues, {|v,e| ::SetEntry( cSection, ToString( e ), v ) } )
   ELSE
      AEval( aValues, {|aKey| ::SetEntry( cSection, aKey[1], aKey[2] ) } )
   ENDIF

RETURN Nil

//--------------------------------------------------------------------------

METHOD SetSectionEx( cSection, cString, cDelimiter ) CLASS XIni

   LOCAL nPos
   LOCAL nAt
   LOCAL cTemp
   LOCAL aValues := {}

   DEFAULT cDelimiter   TO CRLF

   DO WHILE ( nPos := At( cDelimiter, cString ) ) != 0
      cTemp := AllTrim( Substr( cString, 1, nPos - 1 ) )
      IF ( nAt := At( "=", cTemp ) ) != 0
         AAdd( aValues, { SubStr( cTemp, 1, nAt - 1 ), SubStr( cTemp, nAt + 1 ) } )
      ENDIF
      nPos += Len( cDelimiter )
      cString := SubStr( cString, nPos )
   ENDDO

   IF ( nAt := At( "=", cString ) ) != 0
      AAdd( aValues, { SubStr( cString, 1, nAt - 1 ), SubStr( cString, nAt + 1 ) } )
   ENDIF

   ::SetSection( cSection, aValues )

RETURN Nil

//--------------------------------------------------------------------------

METHOD GetSection( cSection ) CLASS XIni

   LOCAL cString
   LOCAL aKeys    := {}
   LOCAL n

   IF Empty( ::cFileName )
      cString  := GetProfileSection( cSection, ::nBuffer )
   ELSE
      cString  := GetPrivateProfileSection( cSection, ::cFileName, ::nBuffer )
   ENDIF

   DO WHILE ( n := At( Chr( 0 ), cString ) ) > 1
      AAdd( aKeys, Left( cString, n - 1 ) )
      cString := SubStr( cString, n + 1 )
   ENDDO

RETURN aKeys

//--------------------------------------------------------------------------

METHOD GetSections() CLASS XIni

   LOCAL cString     := GetPrivateProfileSectionNames( ::cFileName )
   LOCAL aSections   := {}
   LOCAL n

   DO WHILE ( n := At( Chr( 0 ), cString ) ) > 1
      AAdd( aSections, Left( cString, n - 1 ) )
      cString := SubStr( cString, n + 1 )
   ENDDO

RETURN aSections

//--------------------------------------------------------------------------

METHOD DeleteEntry( cSection, cEntry ) CLASS XIni

   IF Empty( ::cFileName )
      DeleteProfileEntry( cSection, cEntry )
   ELSE
      DeletePrivateProfileEntry( cSection, cEntry, ::cFileName )
   ENDIF

RETURN Nil

//--------------------------------------------------------------------------

METHOD DeleteSection( cSection ) CLASS XIni

   IF Empty( ::cFileName )
      /* NOTA: Esta linea se ha comentado expresamente para
         evitar el borrado de una seccion entera de un
         archivo del sistema [jlalin]

         DeleteProfileSection( cSection )
      */
   ELSE
      DeletePrivateProfileSection( cSection, ::cFileName )
   ENDIF

RETURN Nil

//--------------------------------------------------------------------------

METHOD GetSectionEx( cSection ) CLASS XIni

   LOCAL cString

   IF Empty( ::cFileName )
      cString := GetProfileSection( cSection, ::nBuffer )
   ELSE
      cString := GetPrivateProfileSection( cSection, ::cFileName, ::nBuffer )
   ENDIF

RETURN StrTran( cString, Chr( 0 ), CRLF )

//--------------------------------------------------------------------------

METHOD EnumSectionEntries( cSection ) CLASS XIni

   LOCAL aKeys    := ::GetSection( cSection )
   LOCAL aEntries := {}

   AEval( aKeys, {|cKey| AAdd( aEntries, Left( cKey, At( "=", cKey ) - 1 ) ) } )

RETURN aEntries

//--------------------------------------------------------------------------

METHOD EnumSectionValues( cSection ) CLASS XIni

   LOCAL aKeys    := ::GetSection( cSection )
   LOCAL aValues  := {}

   AEval( aKeys, {|cKey| AAdd( aValues, SubStr( cKey, At( "=", cKey ) + 1 ) ) } )

RETURN aValues

//--------------------------------------------------------------------------

METHOD RenameSection( cSection, cNewSection ) CLASS XIni

   LOCAL aEntries := ::EnumSectionEntries( cSection )
   LOCAL aValues := ::EnumSectionValues( cSection )

   ::DeleteSection( cSection )
   ::AddSection( cNewSection )
   AEval( aEntries, {|cName, i| ::SetEntry( cNewSection, cName, aValues[i] ) } )

RETURN Nil

//--------------------------------------------------------------------------

METHOD SortSection( cSection ) CLASS XIni

   LOCAL aData := ::GetSection( cSection )

   ASort( aData, , , {|x,y| x[1] < y[1] } )
   ::SetSection( cSection, aData )

RETURN Nil

//--------------------------------------------------------------------------

METHOD Commit() CLASS XIni

   IF Empty( ::cFileName )
      CommitProfileFile()
   ELSE
      CommitPrivateProfileFile( ::cFileName )
   ENDIF

RETURN Nil

//--------------------------------------------------------------------------

METHOD DeleteSections() CLASS XIni

   LOCAL aSections

   /* NOTA: Solo para archivos propios de la aplicacion. Si se quiere hacer
      con un archivo del sistema, se debe especificar el patch completo al
      crear el objeto [jlalin]
         oIni := TIni():Create( GetWindowsDirectory() + "win.ini" )
   */
   IF ! Empty( ::cFileName )
      aSections := ::GetSections()
      AEval( aSections, {|cSection| ::DeleteSection( cSection ) } )
   ENDIF

RETURN Nil

//--------------------------------------------------------------------------

/*
[Direcciones]
Direccion1=Calle Se;36,1§ Izq
Address1=Street Tis,31

oIni:GetArray( "Direcciones", "Direccion1" ) -> { Calle Se, 36, 1§ Izq }
oIni:GetArray( "Direcciones", "Address1", "," ) -> { "Street Ti", 31 }
*/

METHOD GetArray( cSection, cEntry, cSeparator ) CLASS XIni

   LOCAL aEntry := {}
   LOCAL cData  := ::GetString( cSection, cEntry )
   LOCAL n

   IF ! Empty( cData )

      DEFAULT cSeparator TO ";"

      DO WHILE ( n := At( cSeparator, cData ) ) > 0
         AAdd( aEntry, Left( cData, n - 1 ) )
         cData := SubStr( cData, n + 1 )
      ENDDO

      AAdd( aEntry, cData )

   ENDIF

RETURN aEntry

//--------------------------------------------------------------------------

METHOD SetArray( cSection, cEntry, aData, cSeparator ) CLASS XIni

   LOCAL cStr := ""

   DEFAULT cSeparator TO ";"

   AEVal( aData, {|x| cStr += AllTrim( ToString( x ) ) + cSeparator } )
   cStr := SubStr( cStr, 1, Len( cStr ) - 1 )

   ::SetEntry( cSection, cEntry, cStr )

RETURN Nil

//--------------------------------------------------------------------------

#pragma BEGINDUMP

#include <windows.h>
#include <xailer.h>

/* NOTA: El parametro nBuffer es siempre opcional
*/
#define DEFAULT_BUFFER_SIZE   ( 1024 * 32 - 1 )  // Maximo en W98 = 32767

//--------------------------------------------------------------------------

XA_FUNC( WRITEPRIVATEPROFILESTRING ) // WritePrivateProfileString( <cSection>, <cEntry>, <cString>, <cFilename> ) --> lSuccess
{
   hb_retl( WritePrivateProfileString( hb_parc( 1 ), hb_parc( 2 ), hb_parc( 3 ), hb_parc( 4 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( DELETEPRIVATEPROFILESECTION ) // DeletePrivateProfileSection( <cSection>, <cFilename> ) --> lSuccess
{
   hb_retl( WritePrivateProfileString( hb_parc( 1 ), NULL, "", hb_parc( 2 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( DELETEPRIVATEPROFILEENTRY ) // DeletePrivateProfileEntry( <cSection>, <cEntry>, <cFilename> ) --> lSuccess
{
   hb_retl( WritePrivateProfileString( hb_parc( 1 ), hb_parc( 2 ), NULL, hb_parc( 3 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPRIVATEPROFILEINT ) // GetPrivateProfileInt( <cSection>, <cEntry>, <nValue>, <cFilename> ) --> nResult
{
   hb_retnl( GetPrivateProfileInt( hb_parc( 1 ), hb_parc( 2 ), hb_parnl( 3 ), hb_parc( 4 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPRIVATEPROFILESTRING ) // GetPrivateProfileString( <cSection>, <cEntry>, <cValue>, <cFilename>, [<nBuffer>] ) --> cResult
{
   LONG lBuffer = HB_ISNUM( 5 ) ? hb_parnl( 5 ) : DEFAULT_BUFFER_SIZE;
   char * cString = (char *) hb_xgrab( lBuffer );
   DWORD dwSize;

   dwSize = GetPrivateProfileString( hb_parc( 1 ), hb_parc( 2 ),
      hb_parc( 3 ), cString, lBuffer - 1, hb_parc( 4 ) );

   if( dwSize )
      hb_retclen( cString, dwSize );
   else
      hb_retc( "" );

   hb_xfree( cString );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPRIVATEPROFILESECTION ) // GetPrivateProfileSection( <cSection>, <cFilename>, [<nBuffer>] ) --> cSection
{
   LONG lBuffer = HB_ISNUM( 3 ) ? hb_parnl( 3 ) : DEFAULT_BUFFER_SIZE;
   char * cString = (char *) hb_xgrab( lBuffer );
   DWORD dwSize;

   dwSize = GetPrivateProfileSection( hb_parc( 1 ), cString,
      lBuffer - 1, hb_parc( 2 ) );

   if( dwSize )
      hb_retclen( cString, dwSize );
   else
      hb_retc( "" );

   hb_xfree( cString );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPRIVATEPROFILESECTIONNAMES ) // GetPrivateProfileSectionNames( <cFilename>, [<nBuffer>] ) --> cResult
{
   LONG lBuffer = HB_ISNUM( 2 ) ? hb_parnl( 2 ) : DEFAULT_BUFFER_SIZE;
   char * cString = (char *) hb_xgrab( lBuffer );
   DWORD dwSize;

   dwSize = GetPrivateProfileSectionNames( cString, lBuffer - 1, hb_parc( 1 ) );

   if( dwSize )
      hb_retclen( cString, dwSize );
   else
      hb_retc( "" );

   hb_xfree( cString );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPROFILESECTION ) // GetProfileSection( <cSection>, [<nBuffer>] ) --> cResult
{
   LONG lBuffer = HB_ISNUM( 2 ) ? hb_parnl( 2 ) : DEFAULT_BUFFER_SIZE;
   char * cString = (char *) hb_xgrab( lBuffer );
   DWORD dwSize;

   dwSize = GetProfileSection( hb_parc( 1 ), cString, lBuffer - 1 );

   if( dwSize )
      hb_retclen( cString, dwSize );
   else
      hb_retc( "" );

   hb_xfree( cString );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPROFILESTRING ) // GetProfileString( <cSection>, <cEntry>, <cValue>, [<nBuffer>] ) --> cResult
{
   LONG lBuffer = HB_ISNUM( 4 ) ? hb_parnl( 4 ) : DEFAULT_BUFFER_SIZE;
   char * cString = (char * ) hb_xgrab( lBuffer );
   DWORD dwSize;

   dwSize = GetProfileString( hb_parc( 1 ), hb_parc( 2 ),
      hb_parc( 3 ), cString, lBuffer - 1 );

   if( dwSize )
      hb_retclen( cString, dwSize );
   else
      hb_retc( "" );

   hb_xfree( cString );
}

//--------------------------------------------------------------------------

XA_FUNC( GETPROFILEINT ) // GetProfileInt( <cSection>, <cEntry>, <nValue> ) --> nResult
{
   hb_retnl( GetProfileInt( hb_parc( 1 ), hb_parc( 2 ), hb_parnl( 3 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( WRITEPROFILESTRING ) // WriteProfileString( <cSection>, <cEntry>, <cString> ) --> lSuccess
{
   hb_retl( WriteProfileString( hb_parc( 1 ), hb_parc( 2 ), hb_parc( 3 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( COMMITPRIVATEPROFILEFILE ) // CommitPrivateProfileFile( <cFileName> ) --> lSuccess
{
   hb_retl( WritePrivateProfileString( NULL, NULL, NULL, hb_parc( 1 ) ) );
}

//--------------------------------------------------------------------------

XA_FUNC( COMMITPROFILEFILE )     // CommitProfileFile() --> lSuccess
{
   hb_retl( WriteProfileString( NULL, NULL, NULL ) );
}

//--------------------------------------------------------------------------

XA_FUNC( DELETEPROFILESECTION ) // DeleteProfileSection( <cSection> ) --> lSuccess
{
   hb_retl( WriteProfileString( hb_parc( 2 ), NULL, "" ) );
}

//--------------------------------------------------------------------------

XA_FUNC( DELETEPROFILEENTRY ) // DeleteProfileEntry( <cSection>, <cEntry> ) --> lSuccess
{
   hb_retl( WriteProfileString( hb_parc( 1 ), hb_parc( 2 ), NULL ) );
}

#pragma ENDDUMP

//--------------------------------------------------------------------------
