program emSystemsWebServer;

uses
  System.SyncObjs,
  System.SysUtils,
  Vcl.StdCtrls,
  IniFiles,
  Vcl.Forms,
  Api.Database in 'source\Api.Database.pas' {ApiDatabase: TDataModule},
  Api.Server.Module in 'source\Api.Server.Module.pas' {ApiServerModule: TDataModule},
  Common.Logging in 'source\Common.Logging.pas',
  Api.Service in 'source\Api.Service.pas',
  Api.ServiceImpl in 'source\Api.ServiceImpl.pas',
  Main in 'source\Main.pas' {FMain},
  App.Server.Module in 'source\App.Server.Module.pas' {AppServerModule: TDataModule},
  Common.Config in 'source\Common.Config.pas',
  Common.Middleware.Logging in 'source\Common.Middleware.Logging.pas';

type
  TMemoLogAppender = class( TInterfacedObject, ILogAppender )
  private
    FLogLevel: Integer;
    FLogMemo: TMemo;
    FCriticalSection: TCriticalSection;
  public
    constructor Create(ALogLevel: Integer; ALogMemo: TMemo);
    destructor Destroy; override;
    procedure Send(logLevel: Integer; Log: ILog);
  end;

  TFileLogAppender = class( TInterfacedObject, ILogAppender )
  private
    FLogLevel: Integer;
    FFilename: string;
    FLogDirectory: string;
    FCriticalSection: TCriticalSection;
  public
    constructor Create(ALogLevel: Integer; AFilename: string);
    destructor Destroy; override;
    procedure Send(logLevel: Integer; Log: ILog);
  end;

{ TMemoLogAppender }
constructor TMemoLogAppender.Create(ALogLevel: Integer; ALogMemo: TMemo);
begin
  FLogLevel := ALogLevel;
  FLogMemo := ALogMemo;
  FCriticalSection := TCriticalSection.Create;
end;

destructor TMemoLogAppender.Destroy;
begin
  FCriticalSection.Free;
  inherited;
end;

procedure TMemoLogAppender.Send(logLevel: Integer; Log: ILog);
var
  FormattedMessage: string;
  LogTime: TDateTime;
  LogMsg: string;
begin
  FCriticalSection.Acquire;
  try
    LogTime := Now;
    FormattedMessage := FormatDateTime('[yyyy-mm-dd HH:nn:ss.zzz]', LogTime);
    LogMsg := Log.GetMessage;
    if LogMsg.IsEmpty then
      FormattedMessage := ''
    else
      FormattedMessage := FormattedMessage + '[' + IntToStr(logLevel) +'] ' + LogMsg;

    if logLevel <= FLogLevel then
      FLogMemo.Lines.Add( FormattedMessage );
  finally
    FCriticalSection.Release;
  end;
end;

{ TFileLogAppender }
constructor TFileLogAppender.Create(ALogLevel: integer; AFilename: string);
var
  iniFile: TIniFile;
  fileNum: integer;
begin
  FLogLevel := ALogLevel;
  FCriticalSection := TCriticalSection.Create;
  FLogDirectory := ExtractFilePath(Application.ExeName) + 'logs\';
  if not DirectoryExists(FLogDirectory) then
    CreateDir(FLogDirectory);

  iniFile := TIniFile.Create( ChangeFileExt(Application.ExeName, '.ini') );
  try
    fileNum := iniFile.ReadInteger( 'Settings', 'LogFileNum', 0 );
    FFilename := AFilename + Format( '%.4d', [fileNum] );
    iniFile.WriteInteger( 'Settings', 'LogFileNum', fileNum + 1 );
  finally
    iniFile.Free;
  end;
end;

destructor TFileLogAppender.Destroy;
begin
  FCriticalSection.Free;
  inherited;
end;

procedure TFileLogAppender.Send(logLevel: integer; Log: ILog);
var
  FormattedMessage: string;
  LogFile: string;
  LogTime: TDateTime;
  LogMsg: string;
  FLogFile: TextFile;
begin
  FCriticalSection.Acquire;
  try
    LogTime := Now;
//    LogFile := ExtractFilePath(Application.ExeName) + FFilename + '.log';
    LogFile := FLogDirectory + FFilename + '.log';

    FormattedMessage := FormatDateTime('[yyyy-mm-dd HH:nn:ss.zzz]', LogTime);
    LogMsg := Log.GetMessage;
    if LogMsg.IsEmpty then
      FormattedMessage := ''
    else
      FormattedMessage := FormattedMessage + '[' + IntToStr(logLevel) +'] ' + LogMsg;

    try
      AssignFile( FLogFile, LogFile );
      if logLevel <= FLogLevel then
      begin
        if FileExists(LogFile) then
          Append( FLogFile )
        else
          ReWrite( FLogFile );
        WriteLn( FLogFile, FormattedMessage );
      end;
    finally
      CloseFile(FLogFile);
    end;
  finally
    FCriticalSection.Release;
  end;
end;

{$R *.res}

var
  iniFile: TIniFile;
  memoLogLevel: Integer;
  fileLogLevel: Integer;

begin
  ReportMemoryLeaksOnShutdown := False;
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TFMain, FMain);
  // Read INI for log levels
  IniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    memoLogLevel := IniFile.ReadInteger('Settings', 'memoLogLevel', 3);
    fileLogLevel := IniFile.ReadInteger('Settings', 'fileLogLevel', 4);
  finally
    IniFile.Free;
  end;

  Logger.AddAppender(TMemoLogAppender.Create(memoLogLevel, FMain.memoInfo));
  Logger.AddAppender(TFileLogAppender.Create(fileLogLevel, 'emSystemsWebServer'));

  Application.Run;
end.

