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

interface

{$I DPF.iOS.Defs.inc}

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

  System.TypInfo,
  DPF.iOS.BaseControl,
  DPF.iOS.Classes,
{$IFDEF IOS}
  iOSapi.QuartzCore,
  Macapi.ObjectiveC,
  Macapi.ObjCRuntime,
  iOSapi.CocoaTypes,
  iOSapi.Foundation,
  iOSapi.Uikit,
  iOSapi.CoreGraphics,
  iOSapi.CoreImage,
  iOSapi.MediaPlayer,
  iOSapi.CoreMedia,
  iOSapi.AVFoundation,
  FMX.Platform.iOS,
  DPF.iOS.Common,
{$ENDIF}
  DPF.iOS.UIView,
  FMX.Layouts, FMX.Memo,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type
{$M+}
  TDPFMPMoviePlayerViewController = class;

{$IFDEF IOS}

  MPMoviePlayerControllerClass = interface( NSObjectClass )
    ['{A2DA29E8-CAB5-4941-A0BA-2CE545D4A1EC}']
  end;

  MPMoviePlayerController = interface( NSObject )
    ['{CAFE46BB-1A89-4350-BE1C-3FA8336D5CE2}']
    // function accessLog: MPMovieAccessLog; cdecl;
    // function allowsAirPlay: Boolean; cdecl;
    // function backgroundColor: UIColor; cdecl;
    // function backgroundView: UIView; cdecl;
    // procedure cancelAllThumbnailImageRequests; cdecl;
    function contentURL: NSURL; cdecl;
    // function controlStyle: MPMovieControlStyle; cdecl;
    // function duration: NSTimeInterval; cdecl;
    // function endPlaybackTime: NSTimeInterval; cdecl;
    // function errorLog: MPMovieErrorLog; cdecl;
    // function initWithContentURL( url: NSURL ): Pointer; cdecl;
    // function initialPlaybackTime: NSTimeInterval; cdecl;
    // function isAirPlayVideoActive: Boolean; cdecl;
    // function isFullscreen: Boolean; cdecl;
    // function loadState: MPMovieLoadState; cdecl;
    // function movieControlMode: MPMovieControlMode; cdecl; //deprecated
    // function movieMediaTypes: MPMovieMediaTypeMask; cdecl;
    // function movieSourceType: MPMovieSourceType; cdecl;
    // function naturalSize: CGSize; cdecl;
    // function playableDuration: NSTimeInterval; cdecl;
    // function playbackState: MPMoviePlaybackState; cdecl;
    // function repeatMode: MPMovieRepeatMode; cdecl;
    // procedure requestThumbnailImagesAtTimes( playbackTimes: NSArray; timeOption: MPMovieTimeOption ); cdecl;
    // function scalingMode: MPMovieScalingMode; cdecl;
    // procedure setAllowsAirPlay( allowsAirPlay: Boolean ); cdecl;
    procedure setBackgroundColor( backgroundColor: UIColor ); cdecl;
    procedure setContentURL( contentURL: NSURL ); cdecl;
    procedure setControlStyle( controlStyle: MPMovieControlStyle ); cdecl;
    // procedure setEndPlaybackTime( endPlaybackTime: NSTimeInterval ); cdecl;
    // procedure setFullscreen( fullscreen: Boolean ); cdecl; overload;
    procedure setFullscreen( fullscreen: Boolean; animated: Boolean ); cdecl; overload;
    procedure setInitialPlaybackTime( initialPlaybackTime: NSTimeInterval ); cdecl;

    procedure setMovieSourceType( movieSourceType: MPMovieSourceType ); cdecl;
    // procedure setRepeatMode( repeatMode: MPMovieRepeatMode ); cdecl;
    procedure setScalingMode( scalingMode: MPMovieScalingMode ); cdecl;
    // procedure setShouldAutoplay( shouldAutoplay: Boolean ); cdecl;
    // procedure setUseApplicationAudioSession( useApplicationAudioSession: Boolean ); cdecl;
    // function shouldAutoplay: Boolean; cdecl;
    // function thumbnailImageAtTime( playbackTime: NSTimeInterval; timeOption: MPMovieTimeOption ): UIImage; cdecl;
    // function timedMetadata: NSArray; cdecl;
    // function useApplicationAudioSession: Boolean; cdecl;
    // function view: UIView; cdecl;
    procedure pause; cdecl;
    procedure stop; cdecl;
    procedure play; cdecl;
  end;

  TMPMoviePlayerController = class( TOCGenericImport<MPMoviePlayerControllerClass, MPMoviePlayerController> )
  end;

  MPMoviePlayerViewController = interface( UIViewController )
    ['{2FDE5404-2531-411A-85B7-F1564BEC445C}']
    function initWithContentURL( contentURL: NSURL ): Pointer; cdecl;
    function moviePlayer: MPMoviePlayerController; cdecl;
    // function shouldAutorotateToInterfaceOrientation( toInterfaceOrientation: UIInterfaceOrientation ): Boolean; cdecl; //  Available in iOS 3.2 through iOS 6.1
  end;

  TMPMoviePlayerViewController = class( TOCGenericImport<MPMoviePlayerViewControllerClass, MPMoviePlayerViewController> )
  end;

  // ------------------------------------------------------------------------------
  IMoviePlayerNotificationHandler = interface( NSObject )
    ['{AA17AFA5-A1BB-462E-9379-223748513DF3}']
    procedure doneClicked( notification: NSNotification ); cdecl;
  end;

  TMoviePlayerNotificationHandler = class( TOCLocal )
  private
    FDPFMPPlayer: TDPFMPMoviePlayerViewController;
  public
    function GetObjectiveCClass: PTypeInfo; override;
    constructor create( Owner: TDPFMPMoviePlayerViewController );
    procedure doneClicked( notification: NSNotification ); cdecl;
  end;
{$ENDIF}

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

  TDPMoviePathType        = ( mptFileName, mptURL );
  TDPMovieSourceType      = ( mstUnknown, mstFile, mstStreaming );
  TDPFMovieScalingMode    = ( msmNone = 0, msmAspectFit = 1, msmAspectFill = 2, msmFill = 3 );
  TDPFMPMovieControlStyle = ( csStyleNone = 0, csEmbedded = 1, csFullscreen = 2 );

  TDPFMoviePlayerOnError         = procedure( Sender: TObject; Error: string ) of object;
  TDPFMoviePlayerOnUserExited    = procedure( Sender: TObject ) of object;
  TDPFMoviePlayerOnPlaybackEnded = procedure( Sender: TObject; var CanClose: Boolean ) of object;

  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFMPMoviePlayerViewController = class( TComponent )
  private
{$IFDEF IOS}
    FMoviePlayer                   : MPMoviePlayerViewController;
    FMovieURL                      : NSURL;
    FMoviePlayerNotificationHandler: TMoviePlayerNotificationHandler;
{$ENDIF}
    FMoviePath        : string;
    FMoviePathType    : TDPMoviePathType;
    FMovieSourceType  : TDPMovieSourceType;
    FBackgroundColor  : TAlphaColor;
    FOnUserExited     : TDPFMoviePlayerOnUserExited;
    FOnError          : TDPFMoviePlayerOnError;
    FOnPlaybackEnded  : TDPFMoviePlayerOnPlaybackEnded;
    FMovieControlStyle: TDPFMPMovieControlStyle;
    FShowInView       : TDPFUIView;
    FMovieScalingMode : TDPFMovieScalingMode;
    procedure setBackgroundColor( const Value: TAlphaColor );
  protected
{$IFDEF IOS}
    procedure Loaded; override;
    procedure dealocatePlayer;
{$ELSE}
{$ENDIF}
  public
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
    procedure Close;
    procedure Play;
    procedure Stop;
    procedure Pause;
  published
    property MoviePath        : string read FMoviePath write FMoviePath;
    property MovieSourceType  : TDPMovieSourceType read FMovieSourceType write FMovieSourceType default mstFile;
    property MoviePathType    : TDPMoviePathType read FMoviePathType write FMoviePathType default mptFileName;
    property BackgroundColor  : TAlphaColor read FBackgroundColor write setBackgroundColor default TAlphaColors.Black;
    property MovieControlStyle: TDPFMPMovieControlStyle read FMovieControlStyle write FMovieControlStyle default TDPFMPMovieControlStyle.csFullscreen;
    property MovieScalingMode : TDPFMovieScalingMode read FMovieScalingMode write FMovieScalingMode default TDPFMovieScalingMode.msmNone;

    property ShowInView: TDPFUIView read FShowInView write FShowInView;

    property OnError        : TDPFMoviePlayerOnError read FOnError write FOnError;
    property OnUserExited   : TDPFMoviePlayerOnUserExited read FOnUserExited write FOnUserExited;
    property OnPlaybackEnded: TDPFMoviePlayerOnPlaybackEnded read FOnPlaybackEnded write FOnPlaybackEnded;
  end;

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

implementation

// ------------------------------------------------------------------------------
{ TDPFMPMoviePlayerViewController }
// ------------------------------------------------------------------------------
constructor TDPFMPMoviePlayerViewController.create( AOwner: TComponent );
begin
  inherited create( AOwner );
  FMoviePathType     := mptFileName;
  FMovieSourceType   := mstFile;
  FBackgroundColor   := TAlphaColors.Black;
  FMovieControlStyle := TDPFMPMovieControlStyle.csFullscreen;
  FMovieScalingMode  := TDPFMovieScalingMode.msmNone;
end;

// ------------------------------------------------------------------------------
destructor TDPFMPMoviePlayerViewController.Destroy;
begin
{$IFDEF IOS}
  if Assigned( FMoviePlayer ) then
  begin
    FMoviePlayer.view.removeFromSuperview;
    FMoviePlayer := nil;
  end;

  if Assigned( FMovieURL ) and ( FMoviePathType = mptFileName ) then
    FMovieURL.release;
  FMovieURL := nil;

  if Assigned( FMoviePlayerNotificationHandler ) then
    FMoviePlayerNotificationHandler.DisposeOf;

{$ENDIF}
  inherited;
end;

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

procedure TDPFMPMoviePlayerViewController.Loaded;
begin
  // ----------------------------
  // Important
  inherited;
end;

// ------------------------------------------------------------------------------
{ TIMoviePlayerNotificationHandler }
constructor TMoviePlayerNotificationHandler.create( Owner: TDPFMPMoviePlayerViewController );
begin
  inherited create;
  FDPFMPPlayer := Owner;
end;

// ------------------------------------------------------------------------------
function TMoviePlayerNotificationHandler.GetObjectiveCClass: PTypeInfo;
begin
  Result := TypeInfo( IMoviePlayerNotificationHandler );
end;

// ------------------------------------------------------------------------------
procedure TMoviePlayerNotificationHandler.doneClicked( notification: NSNotification );
var
  Val             : NSNumber;
  CanClose        : Boolean;
  mediaPlayerError: NSError;
begin

  CanClose := true;
  Val      := TNSNumber.Wrap( notification.UserInfo.valueForKey( NSSTR( 'MPMoviePlayerPlaybackDidFinishReasonUserInfoKey' ) ) );

  if Val.intValue = MPMovieFinishReasonPlaybackEnded then
  begin
    // movie finished playin
    if Assigned( FDPFMPPlayer.FOnPlaybackEnded ) then
    begin
      FDPFMPPlayer.FOnPlaybackEnded( FDPFMPPlayer, CanClose );
      if CanClose then
        FDPFMPPlayer.dealocatePlayer;
    end;
  end
  else if ( Val.intValue = MPMovieFinishReasonUserExited ) or ( Val.intValue = MPMovieFinishReasonPlaybackError ) and ( FDPFMPPlayer <> nil ) then
  begin
    // user hit the done button
    if ( Val.intValue = MPMovieFinishReasonUserExited ) and Assigned( FDPFMPPlayer.FOnUserExited ) then
      FDPFMPPlayer.FOnUserExited( FDPFMPPlayer )
    else if ( Val.intValue = MPMovieFinishReasonPlaybackError ) and Assigned( FDPFMPPlayer.FOnError ) then
    begin
      mediaPlayerError := TNSError.Wrap( notification.UserInfo.valueForKey( NSSTR( 'error' ) ) );
      FDPFMPPlayer.FOnError( FDPFMPPlayer, UTF8ToString( mediaPlayerError.localizedDescription.UTF8String ) );
    end;
    FDPFMPPlayer.dealocatePlayer;
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFMPMoviePlayerViewController.dealocatePlayer;
begin
  if Assigned( FMoviePlayerNotificationHandler ) then
    TNSNotificationCenter.Wrap( TNSNotificationCenter.OCClass.defaultCenter ).removeObserver( FMoviePlayerNotificationHandler.GetObjectID );

  if Assigned( FMoviePlayer ) then
  begin
    if FMoviePlayer.moviePlayer <> nil then
    begin
      FMoviePlayer.moviePlayer.setInitialPlaybackTime( -1 );
      FMoviePlayer.moviePlayer.stop;
    end;
    FMoviePlayer.view.removeFromSuperview;
    FMoviePlayer.moviePlayer.setContentURL( nil );
    FMoviePlayer.release;
  end;
  FMoviePlayer := nil;

  if Assigned( FMovieURL ) and ( FMoviePathType = mptFileName ) then
    FMovieURL.release;
  FMovieURL := nil;

  if Assigned( FMoviePlayerNotificationHandler ) then
    FMoviePlayerNotificationHandler.DisposeOf;
end;

{$ENDIF }

// ------------------------------------------------------------------------------
procedure TDPFMPMoviePlayerViewController.Pause;
begin
{$IFDEF IOS}
  FMoviePlayer.moviePlayer.pause;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
// Notifications:
//
// MPMovieDurationAvailableNotification
// MPMovieMediaTypesAvailableNotification
// MPMovieNaturalSizeAvailableNotification
// MPMoviePlayerContentPreloadDidFinishNotification
// MPMoviePlayerDidEnterFullscreenNotification
// MPMoviePlayerDidExitFullscreenNotification
// MPMoviePlayerIsAirPlayVideoActiveDidChangeNotification
// MPMoviePlayerLoadStateDidChangeNotification
// MPMoviePlayerNowPlayingMovieDidChangeNotification
// MPMoviePlayerPlaybackDidFinishNotification
// MPMoviePlayerPlaybackStateDidChangeNotification
// MPMoviePlayerScalingModeDidChangeNotification
// MPMoviePlayerThumbnailImageRequestDidFinishNotification
// MPMoviePlayerWillEnterFullscreenNotification
// MPMoviePlayerWillExitFullscreenNotification
// MPMovieSourceTypeAvailableNotification
// MPMoviePlayerReadyForDisplayDidChangeNotification
// ------------------------------------------------------------------------------
procedure TDPFMPMoviePlayerViewController.Play;
{$IFDEF IOS}
var
  animation: DPF.iOS.Classes.CATransition;
{$ENDIF}
begin
{$IFDEF IOS}
  Close;
  if FMoviePath <> '' then
  begin
    if FMoviePathType = mptFileName then
      FMovieURL := TNSUrl.Wrap( TNSUrl.Alloc.initFileURLWithPath( NSSTR( FMoviePath ), False ) )
    else
      FMovieURL := TNSUrl.Wrap( TNSUrl.OCClass.URLWithString( NSSTR( FMoviePath ) ) );

    FMoviePlayer := TMPMoviePlayerViewController.Wrap( TMPMoviePlayerViewController.Alloc.initWithContentURL( FMovieURL ) );

    if FBackgroundColor <> TAlphaColors.Null then
    begin
      FMoviePlayer.moviePlayer.setBackgroundColor( TColorToUIColor( FBackgroundColor ) );
      FMoviePlayer.view.setBackgroundColor( TColorToUIColor( FBackgroundColor ) );
    end;

    FMoviePlayerNotificationHandler := TMoviePlayerNotificationHandler.create( Self );
    TNSNotificationCenter.Wrap( TNSNotificationCenter.OCClass.defaultCenter ).addObserver( FMoviePlayerNotificationHandler.GetObjectID, sel_getUid( 'doneClicked:' ), ( NSSTR( 'MPMoviePlayerPlaybackDidFinishNotification' ) as ILocalObject ).GetObjectID, nil );

    FMoviePlayer.moviePlayer.setMovieSourceType( Integer( FMovieSourceType ) );
  end;

  if not Assigned( FMoviePlayer ) then
    exit;

  FMoviePlayer.view.setFrame( CGRectMake( 0, 0, screen.Size.Width, screen.Size.Height ) );
  FMoviePlayer.moviePlayer.setControlStyle( Integer( FMovieControlStyle ) );
  FMoviePlayer.moviePlayer.setScalingMode( Integer( FMovieScalingMode ) );

  if FMovieControlStyle = TDPFMPMovieControlStyle.csFullscreen then
  begin
    FMoviePlayer.moviePlayer.setFullscreen( true, true );
    FMoviePlayer.setWantsFullScreenLayout( true );
    GetSharedApplication.setStatusBarHidden( true );
    GetSharedApplication.setStatusBarStyle( UIStatusBarStyleBlackOpaque );
  end;

  if Assigned( FShowInView ) then
  begin
    FShowInView.GetUIView.setAutoresizesSubviews( true );
    FShowInView.GetUIView.addSubview( FMoviePlayer.view );
    FMoviePlayer.view.setFrame( FShowInView.GetUIView.bounds );
  end
  else
  begin
    animation := DPF.iOS.Classes.TCATransition.Wrap( DPF.iOS.Classes.TCATransition.OCClass.animation );
    animation.setDuration( 0.5 );
    animation.setType( kCATransitionFade { kCATransitionPush } );
    animation.setSubtype( kCATransitionFromTop );
    animation.setTimingFunction( TCAMediaTimingFunction.Wrap( TCAMediaTimingFunction.OCClass.functionWithName( kCAMediaTimingFunctionEaseInEaseOut ) ) );
    FMoviePlayer.view.layer.addAnimation( CAAnimation( animation ), NSStr( 'SwitchToView' ) );
    GetSharedApplication.keyWindow.rootViewController.presentModalViewController( FMoviePlayer, false );
  end;

  // GetSharedApplication.keyWindow.rootViewController.view.addSubview( FMoviePlayer.view );
  // FMoviePlayer.setModalTransitionStyle( UIModalTransitionStyleFlipHorizontal );
  // WindowHandleToPlatform( TCommonCustomForm( Form ).Handle ).View.addSubview( FMoviePlayer.view )
  // FMoviePlayer.navigationItem.leftBarButtonItem.setTitle( NSStr( 'Close' ) );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMPMoviePlayerViewController.Close;
begin
{$IFDEF IOS}
  dealocatePlayer;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMPMoviePlayerViewController.setBackgroundColor( const Value: TAlphaColor );
begin
  FBackgroundColor := Value;
{$IFDEF IOS}
  if FMoviePlayer <> nil then
  begin
    if FBackgroundColor <> TAlphaColors.Null then
      FMoviePlayer.moviePlayer.setBackgroundColor( TColorToUIColor( FBackgroundColor ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMPMoviePlayerViewController.Stop;
begin
{$IFDEF IOS}
  if FMoviePlayer <> nil then
  begin
    FMoviePlayer.moviePlayer.setInitialPlaybackTime( -1 );
    FMoviePlayer.moviePlayer.stop;
  end
{$ENDIF}
end;

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