///////////////////////////////////////////////////////////////////////
// Copyright (C) Andru 2007                                          //
// mail: dr.andru@gmail.com                                          //
// site: http://andru.2x4.ru                                         //
//                                                                   //
// OMEGA Engine is free software.                                    //
//                                                                   //
// You may 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 module 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 module.  If not, write to:                        //
// 	The Free Software Foundation, Inc.,                              //
// 	51 Franklin Street, Fifth Floor                                  //
// 	Boston, MA  02110-1301, USA.                                     //
///////////////////////////////////////////////////////////////////////
unit ENGINE_INI;

{$I DEFINES.pas}

interface
uses
  minClasses,
  minFile,
  Windows,

  ENGINE_VAR,
  ENGINE_CONST,
  ENGINE_TYPES,

  ENGINE_LOG,
  
  UTILS;

procedure INI_LoadFromFile( FileName : PChar ); stdcall;
procedure INI_LoadFromMemory( Memory : PMemory ); stdcall;
procedure INI_SaveToFile( FileName : PChar ); stdcall;
procedure INI_Add( Section, Key : PChar ); stdcall;
function  INI_ReadKeyStr( Section, Key : PChar ) : PChar; stdcall;
function  INI_ReadKeyInt( Section, Key : PChar ) : Integer; stdcall;
function  INI_WriteKeyStr( Section, Key, Value : PChar ) : Boolean; stdcall;
function  INI_WriteKeyInt( Section, Key : PChar; Value : Integer ) : Boolean; stdcall;

function INI_GetID( S, K : String; var idS, idK : Integer ) : Boolean;

type
  TKey = record
  Name  : String;
  Value : String;
end;

type
  TSection = record
  Name : String;
  Keys : Integer;
  Key  : array of TKey;
end;

type
  TINI = record
  FileName : String;
  Sections : Integer;
  Section  : array of TSection;
end;

var
  IniFile : TINI;
  IniMem  : TMemoryStream;

//----------------------------------------------------------------------------//
                                 IMPLEMENTATION
//----------------------------------------------------------------------------//
uses
  ENGINE_UTILS;

procedure INI_Add;
  var
    s, k   : String;
    ns, nk : Integer;
Begin
s := String( Section );
k := String( Key );

INI_GetID( s, k, ns, nk );

if ns = -1 Then
  begin
    INC( IniFile.Sections );
    ns := IniFile.Sections - 1;

    SetLength( IniFile.Section, IniFile.Sections );
    IniFile.Section[ ns ].Name := StrUp( s );
  end;

if nk = -1 Then
  begin
    INC( IniFile.Section[ ns ].Keys );
    nk := IniFile.Section[ ns ].Keys - 1;

    SetLength( IniFile.Section[ ns ].Key, IniFile.Section[ ns ].Keys );
    IniFile.Section[ ns ].Key[ nk ].Name := StrUp( k );
  end;
End;

function DeleteSpaces( str : String ) : String;
  var
    i, b, e : Integer;
Begin
b := 1;
e := length( str );
for i := 1 to length( str ) do
  if str[ i ] = ' ' Then
    INC( b )
  else
    break;

for i := length( str ) downto 1 do
  if str[ i ] = ' ' Then
    DEC( e )
  else
    break;

for i := b to e do
  Result := Result + str[ i ];
End;

procedure INI_Free;
Begin
  IniFile.Sections := 0;
  SetLength( IniFile.Section, 0 );
End;

procedure AddData( str : String );
  var
    i, j, s, k, len : Integer;
Begin

if str = '' Then exit;
len := length( str );

if ( str[ 1 ] = '[' ) and ( str[ len ] = ']' ) Then
  begin
    INC( IniFile.Sections );
    s := IniFile.Sections - 1;
    
    SetLength( IniFile.Section, IniFile.Sections );
    for i := 2 to len - 1 do
      IniFile.Section[ s ].Name := IniFile.Section[ s ].Name + str[ i ];
    IniFile.Section[ s ].Name := DeleteSpaces( IniFile.Section[ s ].Name );
  end else
    begin
      s := IniFile.Sections - 1;
      INC( IniFile.Section[ s ].Keys );
      k := IniFile.Section[ s ].Keys - 1;

      SetLength( IniFile.Section[ s ].Key, IniFile.Section[ s ].Keys );
      for i := 1 to len do
        if str[ i ] <> '=' Then
          IniFile.Section[ s ].Key[ k ].Name := IniFile.Section[ s ].Key[ k ].Name + str[ i ]
        else
          begin
            j := i;
            break;
          end;
      IniFile.Section[ s ].Key[ k ].Name := DeleteSpaces( IniFile.Section[ s ].Key[ k ].Name );

      for i := j + 1 to len do
        IniFile.Section[ s ].Key[ k ].Value := IniFile.Section[ s ].Key[ k ].Value + str[ i ];
      IniFile.Section[ s ].Key[ k ].Value := DeleteSpaces( IniFile.Section[ s ].Key[ k ].Value );
    end;

End;

procedure INI_Porcess;
  var
    c : Char;
    s : String;
    i : Integer;
Begin

s := '';
for i := 0 to IniMem.Size - 1 do
  begin
    IniMem.Read( c, 1 );
    if ( c <> #13 ) and ( c <> #10 ) Then
      s := s + c
    else
      begin
        AddData( s );
        s := '';
      end;
  end;
AddData( s );

End;

function INI_GetID;
  var
    S1, S2 : String;
    i, j   : Integer;
Begin

idS := -1;
idK := -1;
Result := FALSE;
for i := 0 to IniFile.Sections - 1 do
  begin
    S1 := StrUp( IniFile.Section[ i ].Name );
    S2 := StrUp( s );
    if S1 = S2 Then
      begin
        idS := i;
        for j := 0 to IniFile.Section[ i ].Keys - 1 do
          begin
            S1 := StrUp( IniFile.Section[ i ].Key[ j ].Name );
            S2 := StrUp( k );
            if S1 = S2 Then
              begin
                idK := j;
                Result := TRUE;
                exit;
              end;
          end;
      end;
  end;

End;

procedure INI_LoadFromFile;
Begin

FileName := PChar( String( app_Dir ) + String( FileName ) );

INI_Free;
if FileName = '' Then exit;
IniMem := TMemoryStream.Create;
IniFile.FileName := String( FileName );
IniMem.LoadFromFile( IniFile.FileName );
INI_Porcess;
IniMem.Free;

End;

procedure INI_LoadFromMemory;
Begin

INI_Free;
IniMem := TMemoryStream.Create;
IniFile.FileName := 'Memory';
IniMem.Write( Memory^.Ptr^, Memory^.Size );
IniMem.Seek( 0, 0 );
INI_Porcess;
IniMem.Free;

End;

procedure INI_SaveToFile;
  var
    F    : TextFile;
    i, j : Integer;
    s    : String;
Begin

FileName := PChar( String( app_Dir ) + String( FileName ) );

s := FileName;
AssignFile( F, s );
Rewrite( F );
for i := 0 to IniFile.Sections - 1 do
  begin
    s := '[ ' + IniFile.Section[ i ].Name + ' ]';
    Write( F, s );
    WriteLn( F );
    for j := 0 to IniFile.Section[ i ].Keys - 1 do
      begin
        s := IniFile.Section[ i ].Key[ j ].Name + ' = ';
        Write( F, s );
        s := IniFile.Section[ i ].Key[ j ].Value;
        Write( F, s );
        WriteLn( F );
      end;
    if i = IniFile.Sections - 1 Then break;      
    WriteLn( F );
  end;
CloseFile( F );

End;

function  INI_ReadKeyStr;
  var
    s, k   : String;
    i, j   : Integer;
Begin
s := String( Section );
k := String( Key );

if INI_GetID( s, k, i, j ) Then
  Result := PChar( IniFile.Section[ i ].Key[ j ].Value );
End;

function  INI_ReadKeyInt;
  var
    s, k   : String;
    i, j   : Integer;
Begin
s := String( Section );
k := String( Key );

if INI_GetID( s, k, i, j ) Then
  Result := U_StrToInt( PChar( IniFile.Section[ i ].Key[ j ].Value ) );
End;

function  INI_WriteKeyStr;
  var
    s, k   : String;
    i, j   : Integer;
Begin
s := String( Section );
k := String( Key );

if INI_GetID( s, k, i, j ) Then
  begin
    IniFile.Section[ i ].Key[ j ].Value := Value;
    Result := TRUE;
  end else
    begin
      INI_Add( Section, Key );
      INI_WriteKeyStr( Section, Key, Value );    
      Result := FALSE;
    end;
End;

function  INI_WriteKeyInt;
  var
    s, k   : String;
    i, j   : Integer;
Begin
s := String( Section );
k := String( Key );

if INI_GetID( s, k, i, j ) Then
  begin
    IniFile.Section[ i ].Key[ j ].Value := U_IntToStr( Value );
    Result := TRUE;
  end else
    begin
      INI_Add( Section, Key );
      INI_WriteKeyInt( Section, Key, Value );    
      Result := FALSE;
    end;
End;

end.
