program kgOrdersServer;

uses
  FastMM4,
  System.SyncObjs,
  System.SysUtils,
  Vcl.StdCtrls,
  IniFiles,
  Vcl.Forms,
  Api.Server.Module in 'Source\Api.Server.Module.pas' {ApiServerModule: TDataModule},
  Main in 'Source\Main.pas' {FMain},
  Common.Logging in 'Source\Common.Logging.pas',
  Api.Database in 'Source\Api.Database.pas' {ApiDatabase: TDataModule},
  Common.Middleware.Logging in 'Source\Common.Middleware.Logging.pas',
  Common.Config in 'Source\Common.Config.pas',
  Auth.Server.Module in 'Source\Auth.Server.Module.pas' {AuthServerModule: TDataModule},
  Auth.Database in 'Source\Auth.Database.pas' {AuthDatabase: TDataModule},
  uLibrary in 'Source\uLibrary.pas',
  Auth.Service in 'Source\Auth.Service.pas',
  Lookup.Service in 'Source\Lookup.Service.pas',
  Auth.ServiceImpl in 'Source\Auth.ServiceImpl.pas',
  Lookup.ServiceImpl in 'Source\Lookup.ServiceImpl.pas',
  App.Server.Module in 'Source\App.Server.Module.pas' {AppServerModule: TDataModule},
  Data in 'Source\Data.pas' {FData},
  rOrderList in 'Source\rOrderList.pas' {rptOrderList: TDataModule},
  rOrderCorrugated in 'Source\rOrderCorrugated.pas' {rptOrderCorrugated: TDataModule},
  rOrderWeb in 'Source\rOrderWeb.pas' {rptOrderWeb: TDataModule},
  rOrderCutting in 'Source\rOrderCutting.pas' {rptOrderCutting: TDataModule},
  qbAPI in 'Source\qbAPI.pas' {fQB},
  Common.Ini in 'Source\Common.Ini.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;
    FLogFile: string;
    FCriticalSection: TCriticalSection;
  public
    constructor Create(ALogLevel: Integer; AFilename: string; AFileNum: Integer);
    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
  logMsg: string;
  logTime: TDateTime;
  formattedMessage: 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; AFileNum: integer);
var
  iniFile: TIniFile;
  logsDir: string;
begin
  FLogLevel := ALogLevel;
  FCriticalSection := TCriticalSection.Create;
  logsDir := ExtractFilePath(Application.ExeName) + 'logs\';
  if not DirectoryExists(logsDir) then
    CreateDir(logsDir);

  FLogFile := logsDir + AFilename + Format( '%.4d', [AFileNum] ) + '.log';
end;

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

procedure TFileLogAppender.Send(logLevel: Integer; Log: ILog);
var
  logFile: TextFile;
  logMsg: string;
  logTime: TDateTime;
  formattedMessage: string;
begin
  if logLevel > FLogLevel then
    Exit;

  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;

      AssignFile( logFile, FLogFile );
    if FileExists(FLogFile) then
      Append(logFile)
    else
      Rewrite(logFile);

    try
      Writeln(logFile, formattedMessage);
    finally
      CloseFile(logFile);
    end;
  finally
    FCriticalSection.Release;
  end;
end;

{$R *.res}

var
  iniFilename: string;
  iniFile: TIniFile;
  iniStr: string;
begin
  ReportMemoryLeaksOnShutdown := True;

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TFMain, FMain);
  iniFilename := ChangeFileExt( Application.ExeName, '.ini' );
  iniFile := TIniFile.Create( iniFilename );
  try
    Logger.AddAppender( TMemoLogAppender.Create(iniEntries.memoLogLevel, FMain.memoinfo) );
    Logger.AddAppender( TFileLogAppender.Create(iniEntries.fileLogLevel, 'kgOrdersServer', iniEntries.logFileNum) );

    Logger.Log( 1, '*******************************************************' );
    Logger.Log( 1, '*                  kgOrdersServer                     *' );
    Logger.Log( 1, Format('                  Version: %s               ', [FMain.ExeInfo1.FileVersion]));
    Logger.Log( 1, '*            Developed by EM Systems, Inc.            *' );
    Logger.Log( 1, '*******************************************************' );
    Logger.Log( 1, '' );

    //iniEntries is automatically instantiated when the file Common.Ini is used
    //we added LoadIniEntries call to put an entry in the Log file
    LoadIniEntries;
    Logger.Log( 1, 'iniFile: ' + iniFilename );

    iniStr := iniFile.ReadString( 'Settings', 'MemoLogLevel', '' );
    if iniStr.IsEmpty then
      Logger.Log( 1, '--Settings->MemoLogLevel: ini entry not found - default: ' + IntToStr(iniEntries.memoLogLevel) )
    else
      Logger.Log( 1, '--Settings->MemoLogLevel: ini entry: ' + iniStr );

    iniStr := iniFile.ReadString( 'Settings', 'FileLogLevel', '' );
    if iniStr.IsEmpty then
      Logger.Log( 1, '--Settings->FileLogLevel: ini entry not found - default: ' + IntToStr(iniEntries.fileLogLevel) )
    else
      Logger.Log( 1, '--Settings->FileLogLevel: ini entry: ' + iniStr );

    Logger.Log( 1, '' );
    iniStr := iniFile.ReadString( 'Settings', 'LogFileNum', '' );
    if iniStr = '1' then
      Logger.Log( 1, '--Settings->LogFileNum: ini entry not found - LogFileNum 1 added to iniFile' )
    else
      Logger.Log( 1, '--Settings->LogFileNum: ini entry: ' + iniStr );
  finally
    iniFile.Free;
  end;

  Application.Run;
end.

