// ------------------------------------------------------------------------------
// DPF.iOS.GameKit Class
//
// 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.GameKit;

interface

{$I DPF.iOS.Defs.inc}

uses
{$IFDEF IOS}
  Macapi.ObjectiveC,
  Macapi.CoreFoundation,
  Macapi.ObjCRuntime,
  Posix.SysSocket,
  Macapi.Dispatch,
  iOSapi.Foundation,
  iOSapi.CocoaTypes,
  Macapi.Mach,
  iOSapi.UIKit,
  DPF.iOS.Common,
  DPF.iOS.UIView,
{$ENDIF}
  System.SysUtils,
  System.Classes,
  System.Types,
  System.UITypes,
  System.Variants,
  System.TypInfo,
  System.Math,
  FMX.Dialogs;

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

{$IFDEF IOS}

const
  GameKitFwk = '/System/Library/Frameworks/GameKit.framework/GameKit';

{$ENDIF}

type

  TDPGKLeaderboardViewController = class;
{$IFDEF IOS}
  GKLocalPlayer                   = interface;
  id                              = pointer;
  int64_t                         = Int64;
  GKGameCenterControllerViewState = Integer;

  GKPhotoSize = ( GKPhotoSizeSmall = 0, GKPhotoSizeNormal = 1 );

  GKLeaderboardTimeScope = ( GKLeaderboardTimeScopeToday = 0, GKLeaderboardTimeScopeWeek = 1, GKLeaderboardTimeScopeAllTime = 2 );

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

  TGKPlayerCompletionHandler          = procedure( players: NSArray; error: NSError ) of object;
  TGKPlayerLoadPhotoCompletionHandler = procedure( photo: UIImage; error: NSError ) of object;

  GKPlayerClass = interface( NSObjectClass )
    ['{1CDC14B3-3EEC-4DA6-BF5E-055D54029BD0}']
    procedure loadPlayersForIdentifiers( identifiers: NSArray; withCompletionHandler: TGKPlayerCompletionHandler ); cdecl;
  end;

  GKPlayer = interface( NSObject )
    ['{4F9B7E4F-7EFF-4C5A-A902-8E27C6FA1596}']

    function playerID: NSString; cdecl;

    function alias: NSString; cdecl;
    function displayName: NSString; cdecl;
    function isFriend: Boolean; cdecl;
    procedure loadPhotoForSize( size: GKPhotoSize; withCompletionHandler: TGKPlayerLoadPhotoCompletionHandler ); cdecl;
  end;

  TGKPlayer = class( TOCGenericImport<GKPlayerClass, GKPlayer> )
  end;


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

  TGKLocalPlayerGenerateIdentityCompletionHandler       = procedure( publicKeyUrl: NSURL; signature: NSData; salt: NSData; timestamp: uint64_t; error: NSError ) of object;
  TGKLocalPlayerLoadFriendsCompletionHandler            = procedure( players: NSArray; error: NSError ) of object;
  TGKLocalPlayerLoadDefaultLeaderboardCompletionHandler = procedure( leaderboardIdentifier: NSString; error: NSError ) of object;
  TGKLocalPlayerSetDefaultLeaderboardCompletionHandler  = procedure( error: NSError ) of object;
  TGKLocalPlayerAuthenticateHandler                     = procedure( viewController: UIViewController; error: NSError ) of object; cdecl;

  GKLocalPlayerClass = interface( GKPlayerClass )
    ['{F051209B-83AD-4808-8E72-81D76742F738}']
    function localPlayer: id { GKLocalPlayer }; cdecl;
  end;

  GKLocalPlayer = interface( GKPlayer )
    ['{5150248E-A1FC-46FE-897D-EACC5C9022CA}']

    procedure setAuthenticateHandler( authenticateHandler: TGKLocalPlayerAuthenticateHandler ); cdecl;
    function authenticateHandler: TGKLocalPlayerAuthenticateHandler; cdecl;
    function isAuthenticated: Boolean; cdecl;
    procedure generateIdentityVerificationSignatureWithCompletionHandler( completionHandler: TGKLocalPlayerGenerateIdentityCompletionHandler ); cdecl; // Available in iOS 7.0 and later.
    procedure loadFriendsWithCompletionHandler( completionHandler: TGKLocalPlayerLoadFriendsCompletionHandler ); cdecl; // Available in iOS 7.0 and later.
    function friends: NSArray; cdecl;
    function underage: Boolean; cdecl;
    procedure loadDefaultLeaderboardIdentifierWithCompletionHandler( completionHandler: TGKLocalPlayerLoadDefaultLeaderboardCompletionHandler ); cdecl;
    procedure setDefaultLeaderboardIdentifier( leaderboardIdentifier: NSString; completionHandler: TGKLocalPlayerSetDefaultLeaderboardCompletionHandler ); cdecl;
    procedure registerListener( listener: id ); cdecl;   // Available in iOS 7.0 and later.
    procedure unregisterAllListeners; cdecl;             // Available in iOS 7.0 and later.
    procedure unregisterListener( listener: id ); cdecl; // Available in iOS 7.0 and later.
  end;

  TGKLocalPlayer = class( TOCGenericImport<GKLocalPlayerClass, GKLocalPlayer> )
  end;

  // ----------------------------------------------------------------------------
  TGKLocalPlayerReportScoresCompletionHandler = procedure( error: NSError ) of object;

  TGKLocalPlayerGKChallengeComposeCompletionBlock = procedure( composeController: UIViewController; didIssueChallenge: Boolean; sentPlayerIDs: NSArray ); cdecl;

  GKScoreClass = interface( NSObjectClass )
    ['{4D2CE113-30FF-413F-81F1-5A16630087DC}']
    procedure reportScores( scores: NSArray; completionHandler: TGKLocalPlayerReportScoresCompletionHandler ); cdecl; overload;
    procedure reportScores( scores: NSArray; challenges: NSArray; completionHandler: TGKLocalPlayerReportScoresCompletionHandler ); cdecl; overload;
    procedure reportScoreWithCompletionHandler( completionHandler: TGKLocalPlayerReportScoresCompletionHandler ); cdecl; // Deprecated in iOS 7.0
  end;

  GKScore = interface( NSObject )
    ['{EFE445C2-FF78-4EBA-8F71-4058A9F92012}']

    function initWithCategory( category: NSString ): id; cdecl; // Deprecated in iOS 7.0

    function playerID: NSString; cdecl;
    function date: NSDate; cdecl;
    function value: int64_t; cdecl;
    function context: uint64_t; cdecl;
    function formattedValue: NSString; cdecl;
    function rank: NSInteger; cdecl;
    function category: NSString; cdecl; // Deprecated in iOS 7.0
    function shouldSetDefaultLeaderboard: Boolean; cdecl;
    function challengeComposeControllerWithPlayers( playerIDs: NSArray; message: NSString; completionHandler: TGKLocalPlayerGKChallengeComposeCompletionBlock ): UIViewController; cdecl;
    procedure issueChallengeToPlayers( playerIDs: NSArray; message: NSString ); cdecl; // Deprecated in iOS 7.0

    function initWithLeaderboardIdentifier( identifier: NSString ): id; cdecl; overload;
    function initWithLeaderboardIdentifier( identifier: NSString; playerID: NSString ): id; cdecl; overload;
    function leaderboardIdentifier: NSString; cdecl; // Available in iOS 7.0 and later
  end;

  TGKScore = class( TOCGenericImport<GKScoreClass, GKScore> )
  end;

  // ----------------------------------------------------------------------------
  GKGameCenterViewControllerClass = interface( UINavigationControllerClass )
    ['{F3E6AA49-F5F2-4025-9180-0A7DDE45BF4F}']
  end;

  GKGameCenterViewController = interface( UINavigationController )
    ['{1856D0F5-FB20-4A50-945F-2DB01DCBD11D}']

    function gameCenterDelegate: id; cdecl;
    procedure setGameCenterDelegate( gameCenterDelegate: id ); cdecl;

    function viewState: GKGameCenterControllerViewState; cdecl;

    function leaderboardCategory: NSString; cdecl; // Deprecated in iOS 7.0
    function leaderboardTimeScope: GKLeaderboardTimeScope; cdecl; // Deprecated in iOS 7.0

  end;

  TGKGameCenterViewController = class( TOCGenericImport<GKGameCenterViewControllerClass, GKGameCenterViewController> )
  end;

  // ----------------------------------------------------------------------------
  GKLeaderboardViewControllerClass = interface( GKGameCenterViewControllerClass )
    ['{0E772FC5-BFFC-4F5F-9950-88D0ACAE8935}']
  end;

  GKLeaderboardViewController = interface( GKGameCenterViewController )
    ['{398EF0D0-FA65-4A3C-A595-34718D3EF22B}']

    function category: NSString; cdecl;
    procedure setCategory( category: NSString ); cdecl;

    function leaderboardDelegate: id; cdecl;
    procedure setLeaderboardDelegate( leaderboardDelegate: id ); cdecl;

    function timeScope: GKLeaderboardTimeScope; cdecl;
    procedure setTimeScope( timeScope: GKLeaderboardTimeScope ); cdecl;

  end;

  TGKLeaderboardViewController = class( TOCGenericImport<GKLeaderboardViewControllerClass, GKLeaderboardViewController> )
  end;

  // ----------------------------------------------------------------------------
  GKLeaderboardViewControllerDelegate = interface( IObjectiveC )
    ['{AECEA52C-C717-48D8-AFF1-2391C19F6057}']

    procedure leaderboardViewControllerDidFinish( viewController: GKLeaderboardViewController ); cdecl;
  end;

  TGKLeaderboardViewControllerDelegate = class( TOCLocal, GKLeaderboardViewControllerDelegate )
  private
    FDPGKLeaderboardViewController: TDPGKLeaderboardViewController;
  public
    constructor Create( ADPGKLeaderboardViewController: TDPGKLeaderboardViewController );
    procedure leaderboardViewControllerDidFinish( viewController: GKLeaderboardViewController ); cdecl;
  end;

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

{$ENDIF}

  // ----------------------------------------------------------------------------
  // TDPGKLeaderboardViewController Component
  // ----------------------------------------------------------------------------
  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPGKLeaderboardViewController = class( TComponent )
  private
{$IFDEF IOS}
    FLeaderboardViewController          : GKLeaderboardViewController;
    FGKLeaderboardViewControllerDelegate: TGKLeaderboardViewControllerDelegate;
{$ENDIF}
  public
    { Public declarations }
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;

    procedure ShowLeaderBoard( ID: string );
    procedure InitPlayer( ID: string );

{$IFDEF IOS}
    procedure AuthenticateHandlerComp( viewController: UIViewController; error: NSError ); cdecl;
{$ENDIF}
  published
  end;

  // ----------------------------------------------------------------------------
implementation

{$IFDEF IOS}
{$IF Not defined(CPUARM)}

uses Posix.Dlfcn;

var
  GameKitModule: THandle;
{$ENDIF}
{$ENDIF}

  // ------------------------------------------------------------------------------
  { TDPFInAppPurchase }
constructor TDPGKLeaderboardViewController.Create( AOwner: TComponent );
begin
  inherited;
{$IFDEF IOS}
  FGKLeaderboardViewControllerDelegate := TGKLeaderboardViewControllerDelegate.Create( self );

  FLeaderboardViewController := TGKLeaderboardViewController.Wrap( TGKLeaderboardViewController.Alloc.init );
  FLeaderboardViewController.setLeaderboardDelegate( FGKLeaderboardViewControllerDelegate.GetObjectID );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
destructor TDPGKLeaderboardViewController.Destroy;
begin
{$IFDEF IOS}
  FGKLeaderboardViewControllerDelegate.DisposeOf;
  FLeaderboardViewController.release;
{$ENDIF}
  inherited;
end;

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

procedure TDPGKLeaderboardViewController.AuthenticateHandlerComp( viewController: UIViewController; error: NSError ); cdecl;
begin
  if assigned( error ) then
    ShowMessage( NSStrToStr( error.localizedDescription ) )
  else if assigned( viewController ) then
    GetSharedApplication.keyWindow.rootViewController.presentModalViewController( viewController, true );
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPGKLeaderboardViewController.InitPlayer( ID: string );
{$IFDEF IOS}
var
  LPlayer: GKLocalPlayer;
{$ENDIF}
begin
{$IFDEF IOS}
  FLeaderboardViewController.setCategory( NSStr( ID ) );
  FLeaderboardViewController.setTimeScope( GKLeaderboardTimeScopeToday );
  LPlayer := TGKLocalPlayer.Wrap( TGKLocalPlayer.OCClass.localPlayer );
  if not LPlayer.isAuthenticated then
    LPlayer.setAuthenticateHandler( AuthenticateHandlerComp );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPGKLeaderboardViewController.ShowLeaderBoard( ID: string );
begin
{$IFDEF IOS}
  if assigned( FLeaderboardViewController ) then
  begin
    FLeaderboardViewController.setCategory( NSStr( ID ) );
    FLeaderboardViewController.setTimeScope( GKLeaderboardTimeScopeToday );
    GetSharedApplication.keyWindow.rootViewController.presentModalViewController( FLeaderboardViewController, true );
  end;
{$ENDIF}
end;

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

constructor TGKLeaderboardViewControllerDelegate.Create( ADPGKLeaderboardViewController: TDPGKLeaderboardViewController );
begin
  inherited create;
  FDPGKLeaderboardViewController := ADPGKLeaderboardViewController;
end;

// ------------------------------------------------------------------------------
procedure TGKLeaderboardViewControllerDelegate.leaderboardViewControllerDidFinish( viewController: GKLeaderboardViewController ); cdecl;
begin
  FDPGKLeaderboardViewController.FLeaderboardViewController.dismissModalViewControllerAnimated( true );
end;
// ------------------------------------------------------------------------------

{$ENDIF}
// ------------------------------------------------------------------------------
{$IFDEF IOS}
{$IF defined(CPUARM)}
procedure GameKitLoaderLoader; cdecl; external GameKitFwk;
{$ELSE}

initialization

GameKitModule := dlopen( MarshaledAString( GameKitFwk ), RTLD_LAZY );

finalization

dlclose( GameKitModule );
{$ENDIF}
{$ENDIF}

end.
