// ------------------------------------------------------------------------------
// DPF.iOS.UIActionSheet Component
//
// Dadeh Pardazane Faragir ( DPF ) Co.
//
// Web: http://www.dpfaragir.com
//
// Developed By: Babak Yaghoobi
//
// Email #1: yaghoobi@dpfaragir.com
// Email #2: b_yaghobi@yahoo.com
//
// ------------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ------------------------------------------------------------------------------
unit DPF.iOS.UIActionSheet;

interface

{$I DPF.iOS.Defs.inc}

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  System.UITypes,
  System.Variants,

  System.TypInfo,
  DPF.iOS.BaseControl,
  DPF.iOS.UIFont,
{$IFDEF IOS}
  Macapi.ObjectiveC,
  Macapi.ObjCRuntime,
  IOSapi.CocoaTypes,
  IOSapi.Foundation,
  IOSapi.Uikit,
  IOSapi.CoreGraphics,
  IOSapi.CoreImage,
  FMX.Platform.iOS,
  DPF.iOS.Common,
{$ENDIF}
  FMX.Layouts, FMX.Memo,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type

{$IFDEF IOS}
  TDPFUIActionSheet = class;

  // ------------------------------------------------------------------------------
  TDPFActionSheetDelegate = class( TOCLocal, UIActionSheetDelegate )
  private
    FDPFUIActionSheet: TDPFUIActionSheet;
  public
    constructor Create( ADPFUIActionSheet: TDPFUIActionSheet );

    procedure actionSheet( actionSheet: UIActionSheet; clickedButtonAtIndex: NSInteger ); cdecl; // overload;
    procedure actionSheetCancel( actionSheet: UIActionSheet ); cdecl;
    procedure didPresentActionSheet( actionSheet: UIActionSheet ); cdecl;
    procedure willPresentActionSheet( actionSheet: UIActionSheet ); cdecl;
  end;

  // ------------------------------------------------------------------------------
  IActionSheetGestureRecognizerDelegate = interface( IObjectiveC )
    ['{37FFBFC1-9BDB-4457-A98C-DC44C633DB2B}']
    procedure handleSingleTap( gr: UITapGestureRecognizer ); cdecl;
  end;

  TDPFActionSheetGestureRecognizerDelegate = class( TOCLocal, IActionSheetGestureRecognizerDelegate )
  private
    FDPFUIActionSheet: TDPFUIActionSheet;
  public
    constructor Create( ADPFUIActionSheet: TDPFUIActionSheet );
    procedure handleSingleTap( gr: UITapGestureRecognizer ); cdecl;
  end;

{$ENDIF}

  // ------------------------------------------------------------------------------
  // TDPFActionSheetButtonItem

  TDPFActionSheetButtonKind = ( bkOther, bkDestructive, bkCancel, bkCustom );

  TDPFActionSheetButtonItem = class( TCollectionItem )
  private
    FOwner                   : TCollection;
    FTitle                   : string;
    FBackgroundImageNormal   : string;
    FBackgroundImageHighlight: string;
    FBackgroundImageDisable  : string;
    FBackgroundColor         : TAlphaColor;
    FBorderColor             : TAlphaColor;
    FCornerRadius            : Single;
    FBorderWidth             : Integer;
    FImageHighlight          : string;
    FImageDisable            : string;
    FImageNormal             : string;
    FFont                    : TDPFFont;
    FTitleColorNormal        : TAlphaColor;
    FTitleColorDisable       : TAlphaColor;
    FTitleColorHighlight     : TAlphaColor;
    FButtonKind              : TDPFActionSheetButtonKind;
    procedure SetFont( const Value: TDPFFont );
  protected
    function GetDisplayName: string; override;
  public
    constructor Create( AOwner: TCollection ); override;
    destructor Destroy; override;
  published
    property Font               : TDPFFont read FFont write SetFont;
    property Title              : string read FTitle write FTitle;
    property TitleColorNormal   : TAlphaColor read FTitleColorNormal write FTitleColorNormal default TAlphaColors.Black;
    property TitleColorHighlight: TAlphaColor read FTitleColorHighlight write FTitleColorHighlight default TAlphaColors.White;
    property TitleColorDisable  : TAlphaColor read FTitleColorDisable write FTitleColorDisable default TAlphaColors.Gray;
    property ButtonKind         : TDPFActionSheetButtonKind read FButtonKind write FButtonKind default TDPFActionSheetButtonKind.bkOther;

    property BackgroundColor: TAlphaColor read FBackgroundColor write FBackgroundColor default TAlphaColors.Null;

    property BackgroundImageNormal   : string read FBackgroundImageNormal write FBackgroundImageNormal;
    property BackgroundImageHighlight: string read FBackgroundImageHighlight write FBackgroundImageHighlight;
    property BackgroundImageDisable  : string read FBackgroundImageDisable write FBackgroundImageDisable;

    property ImageNormal   : string read FImageNormal write FImageNormal;
    property ImageHighlight: string read FImageHighlight write FImageHighlight;
    property ImageDisable  : string read FImageDisable write FImageDisable;

    property BorderWidth : Integer read FBorderWidth write FBorderWidth default 0;
    property BorderColor : TAlphaColor read FBorderColor write FBorderColor default TAlphaColors.Null;
    property CornerRadius: Single read FCornerRadius write FCornerRadius;

  end;

  // ----------------------------------------------------------------------------
  TDPFActionSheetButtonsCollection = class( TCollection )
  private
    FOwner: TComponent;
  protected
    function GetOwner: TPersistent; override;
    function GetItem( Index: Integer ): TDPFActionSheetButtonItem;
    procedure SetItem( Index: Integer; Value: TDPFActionSheetButtonItem );

  public
    constructor Create( AOwner: TComponent );
    destructor Destroy; override;

    function Add: TDPFActionSheetButtonItem;
    function Insert( Index: Integer ): TDPFActionSheetButtonItem;

    property Items[index: Integer]: TDPFActionSheetButtonItem read GetItem write SetItem; default;
  end;

  // ----------------------------------------------------------------------------
  TDPFActionSheetOnClick = procedure( Sender: TObject; ButtonIndex: Integer ) of object;

  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFUIActionSheet = class( TComponent )
  private
    FOnClick             : TDPFActionSheetOnClick;
    FButtons             : TDPFActionSheetButtonsCollection;
    FTitle               : string;
    FTitleColor          : TAlphaColor;
    FTitleBackGroundColor: TAlphaColor;
    FTitleFont           : TDPFFont;

{$IFDEF IOS}
    FMessageView                            : UIActionSheet;
    FUITapGestureRecognizer                 : UITapGestureRecognizer;
    DPFActionSheetDelegate                  : TDPFActionSheetDelegate;
    FDPFActionSheetGestureRecognizerDelegate: TDPFActionSheetGestureRecognizerDelegate;
    procedure SetBkImage( FUIButton: UIButton; ImageName: string; ImageState: Integer );
    procedure SetFgImage( FUIButton: UIButton; ImageName: string; ImageState: Integer );
    procedure SetBkColor( FUIButton: UIButton; Color: TAlphaColor );
    procedure SetTitleColor( FUIButton: UIButton; Color: TAlphaColor; ColorState: Integer );

    procedure SetLabelTextColor( FUILabel: UILabel; Color: TAlphaColor );
    procedure SetLabelBackColor( FUIlabel: UILabel; Color: TAlphaColor );
{$ENDIF}
    procedure SetButtons( const Value: TDPFActionSheetButtonsCollection );
    procedure SetTitleFont( const Value: TDPFFont );
  protected
    function GetButtonItem( Title: string ): TDPFActionSheetButtonItem;
  public
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
    procedure ShowMessage; overload;
    procedure ShowMessage( AMessageTitle: string; const AButtons: array of string ); overload;
  published
    property OnClick             : TDPFActionSheetOnClick read FOnClick write FOnClick;
    property Title               : string read FTitle write FTitle;
    property TitleColor          : TAlphaColor read FTitleColor write FTitleColor default TAlphaColors.White;
    property TitleBackGroundColor: TAlphaColor read FTitleBackGroundColor write FTitleBackGroundColor default TAlphaColors.Null;
    property TitleFont           : TDPFFont read FTitleFont write SetTitleFont;
    property Buttons             : TDPFActionSheetButtonsCollection read FButtons write SetButtons;
  end;

  // ------------------------------------------------------------------------------

implementation

// ------------------------------------------------------------------------------
{ TDPFUIActionSheet }
// ------------------------------------------------------------------------------
constructor TDPFUIActionSheet.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
{$IFDEF IOS}
  DPFActionSheetDelegate                   := TDPFActionSheetDelegate.Create( Self );
  FDPFActionSheetGestureRecognizerDelegate := TDPFActionSheetGestureRecognizerDelegate.Create( Self );
  FUITapGestureRecognizer                  := TUITapGestureRecognizer.Wrap( TUITapGestureRecognizer.Alloc.initWithTarget( FDPFActionSheetGestureRecognizerDelegate.GetObjectID, Sel_getUid( 'handleSingleTap:' ) ) );
{$ENDIF}
  FButtons   := TDPFActionSheetButtonsCollection.Create( Self );
  FTitleFont := TDPFFont.create;
end;

// ------------------------------------------------------------------------------
destructor TDPFUIActionSheet.Destroy;
begin
{$IFDEF IOS}
  DPFActionSheetDelegate.DisposeOf;
  FDPFActionSheetGestureRecognizerDelegate.DisposeOf;
  FUITapGestureRecognizer.release;
{$ENDIF}
  Buttons.DisposeOf;
  inherited;
end;

// ------------------------------------------------------------------------------
function TDPFUIActionSheet.GetButtonItem( Title: string ): TDPFActionSheetButtonItem;
var
  I: Integer;
begin
  Result := nil;
  for I  := 0 to Buttons.Count - 1 do
    if SameText( Buttons[I].Title, Title ) then
    begin
      Result := Buttons[I];
      break;
    end;
end;

// ------------------------------------------------------------------------------
{$IFDEF IOS}

procedure TDPFUIActionSheet.SetBkColor( FUIButton: UIButton; Color: TAlphaColor );
begin
  if Assigned( FUIButton ) then
  begin
    if Color <> TAlphaColors.Null then
      FUIButton.SetBackgroundColor( TColorToUIColor( Color ) )
    else
      FUIButton.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetTitleColor( FUIButton: UIButton; Color: TAlphaColor; ColorState: Integer );
begin
  if Assigned( FUIButton ) then
  begin
    if Color <> TAlphaColors.Null then
      FUIButton.setTitleColor( TColorToUIColor( Color ), ColorState )
    else
      FUIButton.setTitleColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ), ColorState );
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetLabelBackColor( FUIlabel: UILabel; Color: TAlphaColor );
begin
  if Assigned( FUIlabel ) then
  begin
    if Color <> TAlphaColors.Null then
      FUIlabel.SetBackgroundColor( TColorToUIColor( Color ) )
    else
      FUIlabel.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetLabelTextColor( FUILabel: UILabel; Color: TAlphaColor );
begin
  if Assigned( FUILabel ) then
  begin
    if Color <> TAlphaColors.Null then
      FUILabel.setTextColor( TColorToUIColor( Color ) )
    else
      FUILabel.setTextColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetBkImage( FUIButton: UIButton; ImageName: string; ImageState: Integer );
var
  Image: UIImage;
begin
  if Assigned( FUIButton ) then
  begin
    if ImageName <> '' then
    begin
      Image := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( ImageName ) ) );
      FUIButton.SetBackgroundImage( Image, ImageState );
    end
    else if Assigned( FUIButton.imageView ) then
      FUIButton.SetBackgroundImage( nil, ImageState );
  end

end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetFgImage( FUIButton: UIButton; ImageName: string; ImageState: Integer );
var
  Image: UIImage;
begin
  if Assigned( FUIButton ) then
  begin
    if ImageName <> '' then
    begin
      Image := TUIImage.Wrap( TUIImage.OCClass.ImageNamed( NSStr( ImageName ) ) );
      FUIButton.setImage( Image, ImageState );
      // Image.release;
      Image := nil;
    end
    else if ( FUIButton.imageView <> nil ) then
      FUIButton.setImage( nil, ImageState );
  end;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetButtons( const Value: TDPFActionSheetButtonsCollection );
begin
  FButtons.Assign( Value );
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.SetTitleFont( const Value: TDPFFont );
begin
  FTitleFont.Assign( Value );
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.ShowMessage( AMessageTitle: string; const AButtons: array of string );
var
  I: Integer;
begin
  Buttons.Clear;
  for I               := 0 to high( AButtons ) do
    Buttons.Add.Title := AButtons[i];
  Title               := AMessageTitle;
  ShowMessage;
end;

// ------------------------------------------------------------------------------
procedure TDPFUIActionSheet.ShowMessage;
{$IFDEF IOS}
var
  I, idx: Integer;
{$ENDIF}
begin
{$IFDEF IOS}
  FMessageView := TUIActionSheet.Wrap( TUIActionSheet.Alloc.initWithTitle( NSStr( '' ), nil, nil, nil, nil ) );
  FMessageView.setDelegate( DPFActionSheetDelegate.GetObjectID );
  FMessageView.setActionSheetStyle( UIActionSheetStyleDefault );

  for I := 0 to Buttons.Count - 1 do
  begin
    idx := FMessageView.addButtonWithTitle( NSStr( Buttons[I].Title ) );
    if Buttons[I].ButtonKind = bkCancel then
      FMessageView.setCancelButtonIndex( idx )
    else if Buttons[I].ButtonKind = bkDestructive then
      FMessageView.setDestructiveButtonIndex( idx );

  end;

  if Buttons.Count = 0 then
  begin
    FUITapGestureRecognizer.setNumberOfTapsRequired( 1 );
    FUITapGestureRecognizer.setNumberOfTouchesRequired( 1 );
    FUITapGestureRecognizer.setDelaysTouchesBegan( true );
    FUITapGestureRecognizer.setDelaysTouchesEnded( false );
    FMessageView.addGestureRecognizer( FUITapGestureRecognizer );
  end;

  FMessageView.setTitle( NSSTR( Title ) );
  FMessageView.showInView( GetSharedApplication.keyWindow.rootViewController.view );

{$ENDIF}
end;

// ------------------------------------------------------------------------------
{ TDPFActionSheetDelegate }
{$IFDEF IOS}

procedure TDPFActionSheetDelegate.actionSheet( actionSheet: UIActionSheet; clickedButtonAtIndex: NSInteger );
begin
  if Assigned( FDPFUIActionSheet.FOnClick ) then
    FDPFUIActionSheet.FOnClick( FDPFUIActionSheet, clickedButtonAtIndex );
end;

// ------------------------------------------------------------------------------
procedure TDPFActionSheetDelegate.actionSheetCancel( actionSheet: UIActionSheet );
begin
  { }
end;

// ------------------------------------------------------------------------------
constructor TDPFActionSheetDelegate.Create( ADPFUIActionSheet: TDPFUIActionSheet );
begin
  inherited create;
  FDPFUIActionSheet := ADPFUIActionSheet;
end;

// ------------------------------------------------------------------------------
procedure TDPFActionSheetDelegate.didPresentActionSheet( actionSheet: UIActionSheet );
begin
end;

// ------------------------------------------------------------------------------
procedure TDPFActionSheetDelegate.willPresentActionSheet( actionSheet: UIActionSheet );
var
  I         : Integer;
  b         : UIButton;
  l         : UILabel;
  O         : NSObject;
  ButtonItem: TDPFActionSheetButtonItem;
begin

  for I := 0 to FDPFUIActionSheet.FMessageView.subviews.count - 1 do
  begin
    O := TNSObject.Wrap( FDPFUIActionSheet.FMessageView.subviews.objectAtIndex( i ) );
    if O.isKindOfClass( objc_getClass( 'UIButton' ) ) then
    begin
      b          := TUIButton.Wrap( FDPFUIActionSheet.FMessageView.subviews.objectAtIndex( i ) );
      ButtonItem := FDPFUIActionSheet.GetButtonItem( UTF8ToString( b.titleLabel.text.UTF8String ) );

      if Assigned( ButtonItem ) and ( ButtonItem.ButtonKind = TDPFActionSheetButtonKind.bkCustom ) then
      begin
        b.setFont( ButtonItem.Font._UIFont );

        FDPFUIActionSheet.SetTitleColor( b, ButtonItem.TitleColorNormal, UIControlStateNormal );
        FDPFUIActionSheet.SetTitleColor( b, ButtonItem.TitleColorHighlight, UIControlStateHighlighted );
        FDPFUIActionSheet.SetTitleColor( b, ButtonItem.TitleColorDisable, UIControlStateDisabled );

        FDPFUIActionSheet.SetBkColor( b, ButtonItem.BackgroundColor );

        FDPFUIActionSheet.SetBkImage( b, ButtonItem.BackgroundImageNormal, UIControlStateNormal );
        FDPFUIActionSheet.SetBkImage( b, ButtonItem.BackgroundImageHighlight, UIControlStateHighlighted );
        FDPFUIActionSheet.SetBkImage( b, ButtonItem.BackgroundImageDisable, UIControlStateDisabled );

        FDPFUIActionSheet.SetFgImage( b, ButtonItem.ImageNormal, UIControlStateNormal );
        FDPFUIActionSheet.SetFgImage( b, ButtonItem.ImageHighlight, UIControlStateHighlighted );
        FDPFUIActionSheet.SetFgImage( b, ButtonItem.ImageDisable, UIControlStateDisabled );

        b.layer.setCornerRadius( ButtonItem.CornerRadius );
        b.layer.setBorderColor( TColorToUIColor( ButtonItem.BorderColor ).CGColor );
        b.layer.setBorderWidth( ButtonItem.BorderWidth );
      end;

      b.setShowsTouchWhenHighlighted( True );
    end
    else if O.isKindOfClass( objc_getClass( 'UILabel' ) ) then
    begin
      l := TUILabel.Wrap( FDPFUIActionSheet.FMessageView.subviews.objectAtIndex( i ) );
      l.setFont( FDPFUIActionSheet.TitleFont._UIFont );
      l.setNumberOfLines( 0 );
      FDPFUIActionSheet.SetLabelTextColor( l, FDPFUIActionSheet.TitleColor );
      FDPFUIActionSheet.SetLabelBackColor( l, FDPFUIActionSheet.TitleBackGroundColor );
    end;

  end;
end;
// ------------------------------------------------------------------------------
{ TDPFActionSheetGestureRecognizerDelegate }

constructor TDPFActionSheetGestureRecognizerDelegate.Create( ADPFUIActionSheet: TDPFUIActionSheet );
begin
  inherited create;
  FDPFUIActionSheet := ADPFUIActionSheet;
end;

// ------------------------------------------------------------------------------
procedure TDPFActionSheetGestureRecognizerDelegate.handleSingleTap( gr: UITapGestureRecognizer );
begin
  FDPFUIActionSheet.FMessageView.removeGestureRecognizer( gr );
  FDPFUIActionSheet.FMessageView.dismissWithClickedButtonIndex( 0, true );
  FDPFUIActionSheet.FMessageView.release;
  FDPFUIActionSheet.FMessageView := nil;
end;
{$ENDIF}
// ------------------------------------------------------------------------------
{ TDPFActionSheetButtonItem }

constructor TDPFActionSheetButtonItem.Create( AOwner: TCollection );
begin
  inherited Create( AOwner );
  FOwner              := AOwner;
  FBorderWidth        := 0;
  FBorderColor        := TAlphaColors.Null;
  FCornerRadius       := 0;
  FFont               := TDPFFont.create;
  TitleColorNormal    := TAlphaColors.Black;
  TitleColorHighlight := TAlphaColors.White;
  TitleColorDisable   := TAlphaColors.Gray;
  FButtonKind         := TDPFActionSheetButtonKind.bkOther;
end;

// ------------------------------------------------------------------------------
destructor TDPFActionSheetButtonItem.Destroy;
begin
  FFont.DisposeOf;
  inherited;
end;

// ------------------------------------------------------------------------------
function TDPFActionSheetButtonItem.GetDisplayName: string;
begin
  Result := Format( 'Button %d', [index] );
end;

// ------------------------------------------------------------------------------
procedure TDPFActionSheetButtonItem.SetFont( const Value: TDPFFont );
begin
  FFont.Assign( Value );
end;

// ------------------------------------------------------------------------------
{ TDPFActionSheetButtonsCollection }

function TDPFActionSheetButtonsCollection.Add: TDPFActionSheetButtonItem;
begin
  Result := inherited Add as TDPFActionSheetButtonItem;
end;

// ------------------------------------------------------------------------------
constructor TDPFActionSheetButtonsCollection.Create( AOwner: TComponent );
begin
  inherited Create( TDPFActionSheetButtonItem );
  FOwner := AOwner;
end;

// ------------------------------------------------------------------------------
destructor TDPFActionSheetButtonsCollection.Destroy;
begin

  inherited;
end;

// ------------------------------------------------------------------------------
function TDPFActionSheetButtonsCollection.GetItem( Index: Integer ): TDPFActionSheetButtonItem;
begin
  Result := inherited Items[index] as TDPFActionSheetButtonItem;
end;

// ------------------------------------------------------------------------------
function TDPFActionSheetButtonsCollection.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

// ------------------------------------------------------------------------------
function TDPFActionSheetButtonsCollection.Insert( Index: Integer ): TDPFActionSheetButtonItem;
begin
  Result := inherited insert( index ) as TDPFActionSheetButtonItem;
end;

// ------------------------------------------------------------------------------
procedure TDPFActionSheetButtonsCollection.SetItem( Index: Integer; Value: TDPFActionSheetButtonItem );
begin
  inherited SetItem( index, Value );
end;

// ------------------------------------------------------------------------------
end.
