{ $HDR$}
{**********************************************************************}
{ Unit archived using Team Coherence                                   }
{ Team Coherence is Copyright 2002 by Quality Software Components      }
{                                                                      }
{ For further information / comments, visit our WEB site at            }
{ http://www.TeamCoherence.com                                         }
{**********************************************************************}
{}
{ $Log:  19162: IdReply.pas
{
{   Rev 1.13    2003.10.18 9:33:28 PM  czhower
{ Boatload of bug fixes to command handlers.
}
{
    Rev 1.12    10/15/2003 7:49:38 PM  DSiders
  Added IdCoreResourceStrings to implementation uses clause.
}
{
    Rev 1.11    10/15/2003 7:46:42 PM  DSiders
  Added formatted resource string for the exception raised in
  TIdReply.SetCode.
}
{
{   Rev 1.10    2003.09.06 1:30:30 PM  czhower
{ Removed abstract modifier from a class method so that C++ Builder can compile
{ again.
}
{
{   Rev 1.9    2003.06.05 10:08:50 AM  czhower
{ Extended reply mechanisms to the exception handling. Only base and RFC
{ completed, handing off to J Peter.
}
{
{   Rev 1.8    2003.05.30 10:25:56 PM  czhower
{ Implemented IsEndMarker
}
{
{   Rev 1.7    2003.05.30 10:06:08 PM  czhower
{ Changed code property mechanisms.
}
{
{   Rev 1.6    5/26/2003 04:29:56 PM  JPMugaas
{ Removed GenerateReply and ParseReply.  Those are now obsolete duplicate
{ functions in the new design.
}
{
{   Rev 1.5    5/26/2003 12:19:54 PM  JPMugaas
}
{
{   Rev 1.4    2003.05.26 11:38:18 AM  czhower
}
{
{   Rev 1.3    2003.05.25 10:23:44 AM  czhower
}
{
    Rev 1.2    5/20/2003 12:43:46 AM  BGooijen
  changeable reply types
}
{
{   Rev 1.1    5/19/2003 05:54:58 PM  JPMugaas
}
{
{   Rev 1.0    5/19/2003 12:26:16 PM  JPMugaas
{ Base class for reply format objects.
}
unit IdReply;

interface

uses
  Classes,
  IdException;

type
  TIdReplies = class;
                                                                               
  // into the new one for old TextCode and to ignore NumericCode which has been
  // removed
  TIdReply = class(TCollectionItem)
  protected
    FCode: string;
    FFormattedReply: TStrings;
    FReplyTexts: TIdReplies;
    FText: TStrings;
    //
    procedure AssignTo(ADest: TPersistent); override;
    function CheckIfCodeIsValid(const ACode: string): Boolean; virtual;
    function GetDisplayName: string; override;
    function GetFormattedReply: TStrings; virtual;
    function GetNumericCode: Integer;
    procedure SetFormattedReply(const AValue: TStrings); virtual; abstract;
    procedure SetText(const AValue: TStrings);
    procedure SetCode(const AValue: string);
    procedure SetNumericCode(const AValue: Integer);
  public
    procedure Clear; virtual;
    constructor Create(
      ACollection: TCollection
      ); overload; override;
    constructor Create(
      ACollection: TCollection;
      AReplyTexts: TIdReplies
      ); reintroduce; overload; virtual;
    destructor Destroy; override;
    // Is not abstract because C++ cannot compile abstract class methods
    class function IsEndMarker(const ALine: string): Boolean; virtual;
    procedure RaiseReplyError; virtual; abstract;
    function ReplyExists: Boolean; virtual;
    procedure SetReply(const ACode: Integer; const AText: string);
     overload; virtual;
    procedure SetReply(const ACode: string; const AText: string);
     overload; virtual;
    //
    property FormattedReply: TStrings read GetFormattedReply
     write SetFormattedReply;
    property NumericCode: Integer read GetNumericCode write SetNumericCode;
  published
    property Code: string read FCode write SetCode;
    property Text: TStrings read FText write SetText;
  end;
  TIdReplyClass = class of TIdReply;

  TIdReplies = class(TOwnedCollection)
  protected
    function GetItem(Index: Integer): TIdReply;
    procedure SetItem(Index: Integer; const Value: TIdReply);
  public
    function Add: TIdReply; overload;
    function Add(ANumericCode: Integer; AText: string): TIdReply; overload;
    constructor Create(AOwner: TPersistent); reintroduce;
    function Find(
      ACode: string
      ): TIdReply;
      virtual;
    procedure UpdateText(AReply: TIdReply);
    //
    property Items[Index: Integer]: TIdReply read GetItem write SetItem; default;
  end;

  EIdReplyError = class(EIdException);

implementation

uses
  IdCoreGlobal, IdCoreResourceStrings,
  SysUtils;

{ TIdReply }

procedure TIdReply.AssignTo(ADest: TPersistent);
var LR : TIdReply;
begin
  if ADest is TIdReply then begin
    LR := TIdReply(ADest);
    LR.Clear;
    LR.Text.Assign(Self.Text);
    LR.Code := Self.Code;
  end else begin
    inherited;
  end;
end;

procedure TIdReply.Clear;
begin
  FText.Clear;
  FCode := '';
end;

constructor TIdReply.Create(
  ACollection: TCollection;
  AReplyTexts: TIdReplies
  );
begin
  Create(ACollection);
  FReplyTexts := AReplyTexts;
end;

destructor TIdReply.Destroy;
begin
  FreeAndNil(FText);
  FreeAndNil(FFormattedReply);
  inherited;
end;

function TIdReply.GetDisplayName: string;
begin
  if Text.Count > 0 then begin
    Result := Code + ' ' + Text[0];
  end else begin
    Result := Code;
  end;
end;

function TIdReply.ReplyExists: Boolean;
begin
  Result := Code <> '';
end;

procedure TIdReply.SetNumericCode(const AValue: Integer);
begin
  FCode := IntToStr(AValue);
end;

procedure TIdReply.SetText(const AValue: TStrings);
begin
  FText.Assign(AValue);
end;

procedure TIdReply.SetReply(const ACode: Integer; const AText: string);
begin
  SetReply(IntToStr(ACode), AText);
end;

function TIdReply.GetNumericCode: Integer;
begin
  Result := StrToIntDef(Code, 0);
end;

procedure TIdReply.SetCode(const AValue: string);
begin
  if not CheckIfCodeIsValid(AValue) then begin
    raise EIdException.Create(Format(RSReplyInvalidCode, [AValue]));
  end;
  FCode := AValue;
end;

procedure TIdReply.SetReply(const ACode, AText: string);
begin
  Code := ACode;
  FText.Text := AText;
end;

function TIdReply.CheckIfCodeIsValid(const ACode: string): Boolean;
begin
  Result := True;
end;

class function TIdReply.IsEndMarker(const ALine: string): Boolean;
begin
  Result := False;
end;

function TIdReply.GetFormattedReply: TStrings;
var
  LReply: TIdReply;
begin
  FFormattedReply.Clear;
  // Update the text
  if FReplyTexts <> nil then begin
    LReply := FReplyTexts.Find(Code);
    if LReply <> nil then begin
      Text.Assign(LReply.Text);
    end;
  end;
  Result := FFormattedReply;
end;

constructor TIdReply.Create(ACollection: TCollection);
begin
  inherited;
  FFormattedReply := TStringList.Create;
  FText := TStringList.Create;
end;

{ TIdReplies }

function TIdReplies.Add: TIdReply;
begin
  Result := TIdReply(inherited Add);
end;

function TIdReplies.Add(ANumericCode: Integer; AText: string): TIdReply;
begin
  Result := nil;
  if Find(IntToStr(ANumericCode)) = nil then begin
    Result := Add;
    Result.SetReply(ANumericCode, AText);
  end;
end;

constructor TIdReplies.Create(AOwner: TPersistent);
begin
  inherited Create(AOwner, TIdReply);
end;

function TIdReplies.Find(
  ACode: string
  ): TIdReply;
var
  i: Integer;
begin
  Result := nil;
  for i := 0 to Count - 1 do begin
    if Items[i].Code = ACode then begin
      Result := Items[i];
      Break;
    end;
  end;
end;

function TIdReplies.GetItem(Index: Integer): TIdReply;
begin
  Result := TIdReply(inherited Items[Index]);
end;

procedure TIdReplies.SetItem(Index: Integer; const Value: TIdReply);
begin
  inherited SetItem(Index, Value);
end;

procedure TIdReplies.UpdateText(AReply: TIdReply);
var
  LReply: TIdReply;
begin
  // Reply text is blank, get it from the ReplyTexts
  if AReply.Text.Count = 0 then begin
    LReply := Find(AReply.Code);
    if LReply <> nil then begin
      AReply.Text.Assign(LReply.Text);
    end;
  end;
end;

end.
