/*
 * Copyright (c) 2022-2026 Huawei Device Co., Ltd.
 * 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.
 */

#import "AceVideo.h"
#import <AVFoundation/AVFoundation.h>
#include <cmath>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

#import "AceSurfaceHolder.h"
#import "AceSurfaceView.h"
#import "StageAssetManager.h"
#import "AceTextureHolder.h"
#include "base/log/log.h"

#define VIDEO_FLAG      @"video@"
#define PARAM_AND       @"#HWJS-&-#"
#define PARAM_EQUALS    @"#HWJS-=-#"
#define PARAM_BEGIN     @"#HWJS-?-#"
#define METHOD          @"method"
#define EVENT           @"event"

#define SUCCESS         @"success"
#define FAIL            @"fail"
#define KEY_SOURCE      @"src"
#define KEY_VALUE       @"value"
#define KEY_ISTEXTURE   @"isTexture"
#define FILE_SCHEME     @"file://"
#define HAP_SCHEME      @"/"
#define SECOND_TO_MSEC  (1000)

typedef enum : NSUInteger {
    IDLE,
    PREPARED,
    STARTED,
    PAUSED,
    STOPPED,
    PLAYBACK_COMPLETE
} PlayState;

// Supported playback speeds defined by AVPlayer or system standards
static const float DEFAULT_SPEED = 1.0f;
static const float SPEED_COMPARE_EPSILON = 0.00001f;
static const float SUPPORTED_SPEEDS[] = {
    0.125f, 0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 3.0f };

@interface AceVideo()
{
    BOOL _isAddedLisenten;
}
@property (nonatomic, assign) int64_t incId;
@property (nonatomic, assign) int32_t instanceId;
@property (nonatomic, assign) long surfaceId;

@property (nonatomic, copy) IAceOnResourceEvent onEvent;
@property (nonatomic, assign) BOOL isAutoPlay;
@property (nonatomic, assign) BOOL isMute;
@property (nonatomic, assign) BOOL isLoop;
@property (nonatomic, assign) float speed;
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, copy) NSString *rawSrc;

@property (nonatomic, copy) NSString *moudleName;
@property (nonatomic, strong) NSMutableDictionary<NSString *, IAceOnCallSyncResourceMethod> *callSyncMethodMap;

@property (nonatomic, strong) AVPlayer *player_;

@property (nonatomic, strong) AceTexture *renderTexture;
@property (nonatomic, strong) CADisplayLink *displayLink;

@property (nonatomic, assign) BOOL isTexture;
@property (nonatomic, assign) BOOL backgroundPause;
@property (nonatomic, assign) PlayState state;
@property (nonatomic, assign) BOOL showFirstFrame;
@property (nonatomic, assign) BOOL pendingPlayAfterPrepare;
@property (nonatomic, assign) BOOL seekedAfterPrepare;
@end

@implementation AceVideo
- (CGSize)getDisplaySizeForPlayerItem:(AVPlayerItem *)playerItem
{
    if (!playerItem) {
        return CGSizeZero;
    }

    CGSize size = playerItem.presentationSize;
    if (size.width > 0.0 && size.height > 0.0) {
        return size;
    }

    NSArray<AVAssetTrack *> *videoTracks = [playerItem.asset tracksWithMediaType:AVMediaTypeVideo];
    AVAssetTrack *videoTrack = videoTracks.firstObject;
    if (!videoTrack) {
        return CGSizeZero;
    }

    CGSize naturalSize = videoTrack.naturalSize;
    if (naturalSize.width <= 0.0 || naturalSize.height <= 0.0) {
        return CGSizeZero;
    }
    CGAffineTransform transform = videoTrack.preferredTransform;
    CGRect displayRect = CGRectApplyAffineTransform(
        CGRectMake(0.0, 0.0, naturalSize.width, naturalSize.height), transform);
    return CGSizeMake(fabs(CGRectGetWidth(displayRect)), fabs(CGRectGetHeight(displayRect)));
}

- (void)updateFirstFrameVisibilityAfterPrepared
{
    __weak __typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        __strong __typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        [strongSelf updateFirstFrameVisibility];
    });
}

- (instancetype)init:(int64_t)incId
    moudleName:(NSString*)moudleName
    onEvent:(IAceOnResourceEvent)callback
    texture:(AceTexture *)texture
    abilityInstanceId:(int32_t)abilityInstanceId
{
    if (self = [super init]) {
        LOGI("AceVideo: init moudleName: %{public}s  incId: %{public}lld", moudleName.UTF8String, incId);
        self.incId = incId;
        self.instanceId = abilityInstanceId;
        self.onEvent = callback;
        self.state = IDLE;
        self.moudleName = moudleName;
        self.speed = DEFAULT_SPEED;
        self.isMute = false;
        self.isAutoPlay = false;
        self.isLoop = false;
        self.pendingPlayAfterPrepare = false;

        _callSyncMethodMap = [[NSMutableDictionary alloc] init];
        [self initEventCallback];
    }
    return self;
}

- (void)initEventCallback
{
    LOGI("AceVideo: initEventCallback");
    __weak __typeof(self)weakSelf = self;
    //init callback
    NSString *init_method_hash = [self method_hashFormat:@"init"];
    IAceOnCallSyncResourceMethod init_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: init");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            return [strongSelf initMediaPlayer:param] ? SUCCESS : FAIL;
        } else {
            LOGE("AceVideo: init fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[init_callback copy] forKey:init_method_hash];

    // start callback
    IAceOnCallSyncResourceMethod start_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: startPlay");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf startPlay];
            return SUCCESS;
        } else {
            LOGE("AceVideo: startPlay fail");
            return FAIL;
        }
    };

    NSString *start_method_hash = [self method_hashFormat:@"start"];
    [self.callSyncMethodMap setObject:[start_callback copy] forKey:start_method_hash];

    // pause callback 
    NSString *pause_method_hash = [self method_hashFormat:@"pause"];
    IAceOnCallSyncResourceMethod pause_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: pause");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf pause];
            return SUCCESS;
        } else {
            LOGE("AceVideo: pause fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[pause_callback copy] forKey:pause_method_hash];
    // stop callback
    NSString *stop_method_hash =  [self method_hashFormat:@"stop"];
    IAceOnCallSyncResourceMethod stop_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: stop");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf stop];
            return SUCCESS;
        } else {
            LOGE("AceVideo: stop fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[stop_callback copy] forKey:stop_method_hash];

    // getposition callback 
    NSString *getposition_method_hash = [self method_hashFormat:@"getposition"];
    IAceOnCallSyncResourceMethod getposition_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: currentpos");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            int64_t position = [strongSelf getPosition];
            if (strongSelf.state == STARTED) {
                [strongSelf fireCallback:@"ongetcurrenttime"
                    params:[NSString stringWithFormat:@"currentpos=%lld", position]];
            }
            return [NSString stringWithFormat:@"%@%lld",@"currentpos=", position];
        } else {
            LOGE("AceVideo: currentpos fail");
            return FAIL;
        }
    };

    [self.callSyncMethodMap setObject:[getposition_callback copy] forKey:getposition_method_hash];
    // seekto callback 
    NSString *seekto_method_hash = [self method_hashFormat:@"seekto"];
    IAceOnCallSyncResourceMethod seekto_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: seekto");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            if (!param) {
                return FAIL;
            }
            int64_t msec = [[param objectForKey:KEY_VALUE] longLongValue];
            CMTime time = CMTimeMake(msec/1000, 1);
            [strongSelf seekTo:time];
            return SUCCESS;
        } else {
            LOGE("AceVideo: seekto fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[seekto_callback copy] forKey:seekto_method_hash];

    // setvolume callback 
    NSString *setvolume_method_hash = [self method_hashFormat:@"setvolume"];
    IAceOnCallSyncResourceMethod setvolume_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: setVolume");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            if (!param) {
                return FAIL;
            }
            LOGI("%{public}s", [[param objectForKey:KEY_VALUE] description].UTF8String);
            float volumn = [[param objectForKey:KEY_VALUE] floatValue];
            [strongSelf setVolume:volumn];
            return SUCCESS;
        } else {
            LOGE("AceVideo: setVolume fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[setvolume_callback copy] forKey:setvolume_method_hash];

    // enablelooping callback
    NSString *enablelooping_method_hash = [self method_hashFormat:@"enablelooping"];
    IAceOnCallSyncResourceMethod enablelooping_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: enablelooping");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            if (!param) {
                return FAIL;
            }
            BOOL loop = [[param objectForKey:@"loop"] boolValue];
            [strongSelf enableLooping:loop];
            return SUCCESS;
        } else {
            LOGE("AceVideo: enablelooping fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[enablelooping_callback copy] forKey:enablelooping_method_hash];

    // setspeed callback  
    NSString *setspeed_method_hash = [self method_hashFormat:@"setspeed"];
    IAceOnCallSyncResourceMethod setspeed_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: player_ setspeed");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            if (!param) {
                return FAIL;
            }
            float speed = [[param objectForKey:KEY_VALUE] floatValue];
            [strongSelf updateSpeed:speed];
            return SUCCESS;
        } else {
            LOGE("AceVideo: setspeed fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[setspeed_callback copy] forKey:setspeed_method_hash];

    // setdirection callback 
    NSString *setdirection_method_hash = [self method_hashFormat:@"setdirection"];
    IAceOnCallSyncResourceMethod setdirection_callback = ^NSString *(NSDictionary * param){
        return SUCCESS;
    };
    [self.callSyncMethodMap setObject:[setdirection_callback copy] forKey:setdirection_method_hash];

    // start callback 
    NSString *setlandscape_method_hash = [self method_hashFormat:@"setlandscape"];
    IAceOnCallSyncResourceMethod setlandscape_callback = ^NSString *(NSDictionary * param){
        return SUCCESS;
    };
    [self.callSyncMethodMap setObject:[setlandscape_callback copy] forKey:setlandscape_method_hash];
    
    // setLayer callback 
    NSString *setsurface_method_hash = [self method_hashFormat:@"setsurface"];
    IAceOnCallSyncResourceMethod setsurface_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: setsurface");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            return [strongSelf setSuerface:param];
        } else {
            LOGE("AceVideo: setsurface fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[setsurface_callback copy] forKey:setsurface_method_hash];

    // setupdateResource callback 
    NSString *updateResource_method_hash = [self method_hashFormat:@"updateresource"];
    IAceOnCallSyncResourceMethod setupdateResource_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: updateresource");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
             return [strongSelf setUpdateResource:param];
        } else {
            LOGE("AceVideo: updateresource fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[setupdateResource_callback copy] forKey:updateResource_method_hash];

    // setfullscreen callback
    NSString *fullscreen_method_hash = [self method_hashFormat:@"fullscreen"];
    IAceOnCallSyncResourceMethod setfullscreen_callback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: fullscreen");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
             return [strongSelf setFullscreen:param];
        } else {
            LOGE("AceVideo: fullscreen fail");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[setfullscreen_callback copy] forKey:fullscreen_method_hash];

    NSString *setRenderFirstFrameMethodHash = [self method_hashFormat:@"setRenderFirstFrame"];
    IAceOnCallSyncResourceMethod setRenderFirstFrameCallback = ^NSString *(NSDictionary * param){
        LOGI("AceVideo: setRenderFirstFrame");
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            return [strongSelf setRenderFirstFrame:param];
        } else {
            LOGE("AceVideo: strongSelf is nil");
            return FAIL;
        }
    };
    [self.callSyncMethodMap setObject:[setRenderFirstFrameCallback copy] forKey:setRenderFirstFrameMethodHash];
}

- (NSDictionary<NSString *, IAceOnCallSyncResourceMethod> *)getSyncCallMethod
{
    return self.callSyncMethodMap;
}

- (void)startPlay
{
    LOGI("AceVideo: player_ startPlay");
    if (self.player_) {
        if (self.state == STOPPED || self.state == PLAYBACK_COMPLETE) {
            CMTime time = CMTimeMake(0, 1);
            __weak __typeof(self)weakSelf = self;
            [self.player_
            seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
                __strong __typeof(weakSelf)strongSelf = weakSelf;
                if (finished && strongSelf) {
                    strongSelf.state = STARTED;
                    [strongSelf fireCallback:@"ongetcurrenttime" params:@"currentpos=0"];
                    [strongSelf firePreparedEventWithCurrentItem:strongSelf.player_.currentItem isPlaying:1];
                    [strongSelf showAvPlayerlayer];
                    [strongSelf.player_ play];
                    if (strongSelf.player_.rate != strongSelf.speed) {
                        [strongSelf updateSpeed:strongSelf.speed];
                    }
                }
            }];
            return;
        } else if (self.state == PREPARED) {
            if (self.seekedAfterPrepare) {
                self.state = STARTED;
                [self showAvPlayerlayer];
                [self.player_ play];
                if (self.player_.rate != self.speed) {
                    [self updateSpeed:self.speed];
                }
            } else {
                CMTime time = CMTimeMake(0, 1);
                __weak __typeof(self)weakSelf = self;
                [self.player_ seekToTime:time
                toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
                    __strong __typeof(weakSelf)strongSelf = weakSelf;
                    if (finished && strongSelf) {
                        strongSelf.state = STARTED;
                        [strongSelf showAvPlayerlayer];
                        [strongSelf.player_ play];
                        if (strongSelf.player_.rate != strongSelf.speed) {
                            [strongSelf updateSpeed:strongSelf.speed];
                        }
                    }
                }];
            }
            return;
        } else {
            CMTime currentTime = self.player_.currentTime;
            int64_t duration = [self getMediaDuration] / 1000;
            if (currentTime.timescale > 0 && currentTime.value / currentTime.timescale == duration) {
                CMTime time = CMTimeMake(0, currentTime.timescale);
                [self seekTo:time];
            }
        }
        [self showAvPlayerlayer];
        [self.player_ play];
        self.state = STARTED;

        if (self.player_.rate != self.speed) {
            [self updateSpeed:self.speed];
        }
    }
}

- (void)replay {
    CMTime time = CMTimeMake(0, 1);
    [self seekTo:time];
    [self startPlay];
}

- (void)pause
{
    if(self.state == STOPPED){
        return;
    }
    if (self.player_) {
        [self.player_ pause];
        self.state = PAUSED;
    }
}

- (void)stop
{
    if (self.player_) {
        [self.player_ pause];
        self.state = STOPPED;
        [self fireCallback:@"stop" params:@""];
    }
}

- (void)seekTo:(CMTime)time
{
    if (self.player_) {
        if (self.state == STOPPED) {
            return;
        }
        if (self.state == PREPARED) {
            self.seekedAfterPrepare = YES;
        }
        __weak __typeof(self)weakSelf = self;
        [self.player_ seekToTime:time
            toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if (finished && strongSelf) {
                if (strongSelf.state != STARTED) {
                    if (strongSelf.isTexture && strongSelf.renderTexture) {
                        [strongSelf.renderTexture refreshPixelBuffer];
                    } else {
                        [strongSelf showAvPlayerlayer];
                    }
                }
                int64_t posInSec = 0;
                if (time.timescale > 0) {
                    posInSec = time.value / time.timescale;
                }
                NSString *timeParam = [NSString stringWithFormat:@"currentpos=%lld", posInSec];
                [strongSelf fireCallback:@"ongetcurrenttime" params:timeParam];

                NSString *param = [NSString stringWithFormat:@"currentpos=%f", (float)time.value];
                [strongSelf fireCallback:@"seekcomplete" params:param];
            }
        }];
    }
}

- (int64_t)getPosition
{
    if (self.player_) {
        CMTime time = self.player_.currentTime;
        return time.value / time.timescale;
    }
    return 0;
}

- (void)setVolume:(float)volume
{
    if (self.player_) {
        [self.player_ setVolume:volume];
    }
}

- (void)enableLooping:(BOOL)enable
{
    if (self.player_) {
        self.isLoop = enable;
    }
}

- (float)normalizeSpeed:(float)speed
{
    for (float supportedSpeed : SUPPORTED_SPEEDS) {
        if (std::fabs(supportedSpeed - speed) <= SPEED_COMPARE_EPSILON) {
            return supportedSpeed;
        }
    }
    LOGW("AceVideo: Unsupported speed %{public}f, fallback to default speed.", speed);
    return DEFAULT_SPEED;
}

- (void)updateSpeed:(float)speed
{
    float normalizedSpeed = [self normalizeSpeed:speed];
    self.speed = normalizedSpeed;
    if (self.player_) {
        AVPlayerTimeControlStatus status = self.player_.timeControlStatus;
        if (status == AVPlayerTimeControlStatusPlaying || self.isAutoPlay || self.state == STARTED) {
            LOGI("AceVideo: setspeed %{public}f", normalizedSpeed);
            [self.player_ setRate:normalizedSpeed];
        } else {
            LOGI("AceVideo: If the speed is greater than 0, the video will start playing.  setspeed");
        }
    }
}

- (void)firePreparedEventWithCurrentItem:(AVPlayerItem *)playerItem isPlaying:(int)isPlaying
{
    if (!playerItem) {
        return;
    }
    CGSize size = [self getDisplaySizeForPlayerItem:playerItem];
    int64_t duration = [self getMediaDuration];
    NSString *param = [NSString stringWithFormat:
        @"width=%f&height=%f&duration=%lld&isplaying=%d&needRefreshForce=%d",
        size.width, size.height, duration, isPlaying, 1];
    [self fireCallback:@"prepared" params:param];
}

- (void)firePlayStatusEvent:(int)isPlaying
{
    NSString *param = [NSString stringWithFormat:@"isplaying=%d", isPlaying];
    [self fireCallback:@"onplaystatus" params:param];
}

- (void)handleSameSourceReset
{
    if (!self.player_ || !self.player_.currentItem) {
        return;
    }

    BOOL wasStopped = (self.state == STOPPED);
    [self.player_ pause];

    if (!wasStopped) {
        [self fireCallback:@"ongetcurrenttime" params:@"currentpos=0"];
    }

    self.state = PREPARED;
    self.seekedAfterPrepare = NO;

    if (!self.showFirstFrame) {
        [self firePreparedEventWithCurrentItem:self.player_.currentItem isPlaying:0];
        [self updateFirstFrameVisibilityAfterPrepared];
        return;
    }

    CMTime time = CMTimeMake(0, 1);
    __weak __typeof(self)weakSelf = self;
    [self.player_ seekToTime:time
    toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        if (!finished || !strongSelf) {
            return;
        }
        [strongSelf firePreparedEventWithCurrentItem:strongSelf.player_.currentItem isPlaying:0];
        [strongSelf updateFirstFrameVisibilityAfterPrepared];
    }];
}

- (void)setReset
{
    [self resetPlayerToPrepare:self.player_.currentItem withIsSetAutoPlay:false];
}

- (void)setPrepare:(id)object
{
    [self resetPlayerToPrepare:object withIsSetAutoPlay:true];
}

- (NSString *)setSuerface:(NSDictionary *)params
{
    if (!params) {
        LOGE("AceVideo: setSurface failed: params is null");
        return FAIL;
    }
    if (!params[KEY_VALUE]) {
        LOGE("AceVideo: setSurface failed: value is illegal");
        return FAIL;
    }
    @try {
        self.surfaceId = [params[KEY_VALUE] longLongValue];
        if ([params[KEY_ISTEXTURE] boolValue]){
            self.isTexture = YES;
            LOGI("AceVideo:isTexture Ture");
            AceTexture *texture = (AceTexture*)[AceTextureHolder getTextureWithId:self.surfaceId
                inceId:self.instanceId];
            self.renderTexture = texture;
        } else{
            LOGI("AceVideo: setSurface id:%{public}lld", static_cast<long long>(self.surfaceId));
            AceSurfaceView * surfaceView = (AceSurfaceView *)[AceSurfaceHolder getLayerWithId:self.surfaceId
                inceId:self.instanceId].delegate;
            if (surfaceView && self.player_) {
                LOGI("AceVideo: MediaPlayer SetSurface");
                AVPlayerLayer * playerLayer = (AVPlayerLayer *)surfaceView.layer;
                playerLayer.player = self.player_;
            }
            [self updateFirstFrameVisibility];
        }

    } @catch (NSException *exception) {
        LOGE("AceVideo: IOException, setSuerface failed");
        return FAIL;
    }
    return SUCCESS;
}

/// ios Fix display order issues
- (void)showAvPlayerlayer
{
    if (self.surfaceId == 0) {
        return;
    }
    AceSurfaceView * surfaceView = (AceSurfaceView *)[AceSurfaceHolder getLayerWithId:self.surfaceId
        inceId:self.instanceId].delegate;
    if (!surfaceView) {
        return;
    }
    AVPlayerLayer * playerLayer = (AVPlayerLayer *)surfaceView.layer;
    if (playerLayer.isHidden) {
        playerLayer.hidden = false;
    }
}

- (NSString *)setUpdateResource:(NSDictionary *)params
{
    LOGI("AceVideo: setUpdateResource");
    if (!params) {
        LOGE("AceVideo: updateResource failed: params is null");
        return FAIL;
    }
    @try {
        if (!params[KEY_SOURCE]) {
            return FAIL;
        }

        NSString *src = [params objectForKey:KEY_SOURCE];
        if (![src isKindOfClass:[NSString class]] || src.length == 0 || [src isKindOfClass:[NSNull class]]) {
            LOGE("AceVideo: src param is null");
            return FAIL;
        }

        NSString *oldRawSrc = self.rawSrc;
        BOOL isSameSource = (oldRawSrc && [src isEqualToString:oldRawSrc]);
        if (isSameSource && self.player_.currentItem) {
            [self handleSameSourceReset];
            return SUCCESS;
        }

        if(![self setDataSource:src]) {
            return FAIL;
        }

        if (!self.url) {
            return FAIL;
        }

        [self pause];
        [self updatePalyerItem];
    } @catch (NSException *exception) {
        LOGE("AceVideo: IOException, setSuerface failed");
        return FAIL;
    }
    return SUCCESS;
}

- (BOOL)setDataSource:(NSString *)param
{
    LOGI("AceVideo: setDataSource");
    self.rawSrc = param;
    @try {
        param = [param
            stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
        NSURL *url_ = [NSURL URLWithString:param];
        if (url_.scheme.length != 0 || ![url_.absoluteString hasPrefix:HAP_SCHEME]) {
            self.url = url_;
            return true;
        }

        NSString * bundlePath = [[StageAssetManager assetManager] getBundlePath];
        if (!bundlePath) {
            LOGE("AceVideo: setDataSource null assetManager");
            return false;
        }
        @try {
            NSURL * filePath = [NSURL fileURLWithPathComponents:@[bundlePath,self.moudleName,@"ets",param]];
            LOGI("AceVideo: setDataSourc file hapPath:%{public}s",filePath.absoluteString.UTF8String);
            self.url = filePath;
        } @catch (NSException *exception) {
            LOGE("AceVideo: not found asset in instance path, now begin to search asset in share path");
        }
    } @catch (NSException *exception) {
        LOGE("AceVideo: IOException, setDataSource failed");
        return false;
    }

    return true;
}

- (NSString *)setFullscreen:(NSDictionary *)param
{
    LOGI("AceVideo: setFullscreen");
    if (!param[KEY_VALUE]) {
        LOGE("AceVideo: setFullscreen failed: value is illegal");
        return FAIL;
    }
    if (self.surfaceId == 0) {
        return FAIL;
    }
    BOOL isFullScreen = [[param objectForKey:KEY_VALUE] boolValue];
    if (isFullScreen) {
        AceSurfaceView * surfaceView = (AceSurfaceView *)[AceSurfaceHolder getLayerWithId:self.surfaceId
            inceId:self.instanceId].delegate;
        if (surfaceView) {
            [surfaceView bringSubviewToFront];
        }
    }
    return SUCCESS;
}

- (NSString *)setRenderFirstFrame:(NSDictionary *)param
{
    if (![param isKindOfClass:[NSDictionary class]]) {
        return FAIL;
    }
    NSNumber *showFirstFrameValue = [param valueForKey:@"showFirstFrame"];
    if (!showFirstFrameValue) {
        return FAIL;
    }
    self.showFirstFrame = [showFirstFrameValue boolValue];
    [self updateFirstFrameVisibility];
    return SUCCESS;
}

- (BOOL)initMediaPlayer:(NSDictionary *)param
{
    LOGI("AceVideo: initMediaPlayer");
    if (!param[KEY_SOURCE]) {
        return NO;
    }

    NSString *src = [param objectForKey:KEY_SOURCE];
    if (![src isKindOfClass:[NSString class]] || src.length == 0 || [src isKindOfClass:[NSNull class]]) {
        LOGE("AceVideo: src param is null");
        return NO;
    }
    if(![self setDataSource:src]) {
        return NO;
    }

    if (!self.url) {
        return NO;
    }

    self.isAutoPlay = [[param objectForKey:@"autoplay"] boolValue];
    self.isMute = [[param objectForKey:@"mute"] boolValue];
    self.isLoop = [[param objectForKey:@"loop"] boolValue];

    [self updatePalyerItem];
    [self.player_ setMuted:self.isMute];
    [self setPrepare:nil];
    return YES;
}

- (AVPlayerItem *)updatePalyerItem
{
    @try {
        AVPlayerItem * playerItem = [[AVPlayerItem alloc] initWithURL:self.url];
        if (self.player_.currentItem && _isAddedLisenten) {
            _isAddedLisenten = false;
            [self.player_.currentItem removeObserver:self forKeyPath:@"status"];
            [self.player_.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        }
        _isAddedLisenten = true;
        [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
        [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

        if (self.player_) {
            [self.player_ replaceCurrentItemWithPlayerItem:playerItem];
        } else {
            self.player_ = [[AVPlayer alloc] initWithPlayerItem:playerItem];
        }

        [[NSNotificationCenter defaultCenter] removeObserver:self];
        [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(playDidEndNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

        return playerItem;
    } @catch (NSException *exception) {
        LOGE("AceVideo: playerItem create failed");
    }

}

- (void)playDidEndNotification:(NSNotification *)notification
{
    AVPlayerItem *videoItem = (AVPlayerItem *)notification.object;
    if (![self.player_.currentItem isEqual:videoItem]) {
        return;
    }
    [self playDidEnd];
}

- (void)playDidEnd{
    if (self.player_ && self.isLoop) {
        [self replay];
    } else {
        self.state = PLAYBACK_COMPLETE;
        [self fireCallback:@"completion" params:@""];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
    change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    AVPlayerItem *playerItem = object;
    if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        if (self.player_ && self.player_.currentItem) {
            NSArray *loadedTimeRanges = [[self.player_ currentItem] loadedTimeRanges];
            CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
            float startSeconds = CMTimeGetSeconds(timeRange.start);
            float durationSeconds = CMTimeGetSeconds(timeRange.duration);
            NSTimeInterval timeInterval = startSeconds + durationSeconds;// 计算缓冲总进度
            CMTime duration = playerItem.duration;
            CGFloat totalDuration = CMTimeGetSeconds(duration);
            if (isnan(timeInterval)) {
                timeInterval = 0;
            }
            if (isnan(totalDuration)) {
                totalDuration = 0;
            }
            if (totalDuration > 0) {
                CGFloat percent = timeInterval / totalDuration;
                NSString *param = [NSString stringWithFormat:@"percent=%f", percent];
                [self fireCallback:@"bufferingupdate" params:param];
            }
        }
    } else if ([keyPath isEqualToString:@"status"]) {
        AVPlayerItemStatus status = playerItem.status;
        switch (status) {
            case AVPlayerItemStatusFailed:{
                LOGE("AceVideo: AVPlayerItemStatusFailed");
                [self fireCallback:@"error" params:@""];
            } break;
            case AVPlayerItemStatusReadyToPlay:
            {
                [self setPrepare:object];
            }
                break;
            default:
                break;
        }
    }
}

- (CADisplayLink *)displayLink
{
    if (!_displayLink) {
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkDidrefresh)];
    }
    return _displayLink;
}

- (void)displayLinkDidrefresh
{
    if (self.renderTexture && self.state == STARTED) {
        [self.renderTexture refreshPixelBuffer];
    }
}

- (void)onActivityResume
{
    if (self.player_ && self.backgroundPause && self.state == PAUSED) {
        [self startPlay];
        self.backgroundPause = false;
    }
}

- (void)onActivityPause
{
    if (self.player_ && self.player_.timeControlStatus == AVPlayerTimeControlStatusPlaying) {
        [self pause];
        self.backgroundPause = true;
    }
}

- (void)resetPlayerToPrepare:(id)object withIsSetAutoPlay:(BOOL)setAutoPlay
{
    if (self.player_ && self.player_.currentItem) {
        self.state = PREPARED;
        CGSize size = [self getDisplaySizeForPlayerItem:self.player_.currentItem];
        float width = size.width;
        float height = size.height;
        
        if (height == CGSizeZero.height && width == CGSizeZero.width) {
            return;
        }
        int64_t duration = [self getMediaDuration];
        if (duration == 0) {
            return;
        }
        if (_isTexture && self.renderTexture) {
            AVPlayerItem* item = (AVPlayerItem*)object;
            [item addOutput:self.renderTexture.videoOutput];
            [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        }
        BOOL shouldAutoPlay = (self.isAutoPlay && setAutoPlay) || self.pendingPlayAfterPrepare;
        int isPlaying = (self.player_.timeControlStatus == AVPlayerTimeControlStatusPlaying ||
                        self.isAutoPlay ||
                        shouldAutoPlay) ? 1 : 0;
        NSString *param = [NSString stringWithFormat:
        @"width=%f&height=%f&duration=%lld&isplaying=%d&needRefreshForce=%d", width, height, duration, isPlaying, 1];
        [self fireCallback:@"prepared" params:param];
        [self updateFirstFrameVisibilityAfterPrepared];

        if (shouldAutoPlay) {
            self.pendingPlayAfterPrepare = false;
            [self startPlay];
        }
    }
}

- (void)updateFirstFrameVisibility
{
    if (self.state != PREPARED) {
        return;
    }
    if (self.isTexture) {
        if (self.showFirstFrame && self.renderTexture) {
            [self.renderTexture refreshPixelBuffer];
        }
        return;
    }
    if (self.surfaceId == 0) {
        return;
    }
    AceSurfaceView * surfaceView = (AceSurfaceView *)[AceSurfaceHolder getLayerWithId:self.surfaceId
        inceId:self.instanceId].delegate;
    if (!surfaceView) {
        return;
    }
    AVPlayerLayer * playerLayer = (AVPlayerLayer *)surfaceView.layer;
    playerLayer.hidden = !self.showFirstFrame;
}

- (NSString *)method_hashFormat:(NSString *)method
{
    return [NSString stringWithFormat:@"%@%lld%@%@%@%@", VIDEO_FLAG, self.incId, METHOD, PARAM_EQUALS, method, PARAM_BEGIN];
}

- (void)fireCallback:(NSString *)method params:(NSString *)params
{
    NSString *method_hash = [NSString stringWithFormat:@"%@%lld%@%@%@%@", VIDEO_FLAG, 
        self.incId, EVENT, PARAM_EQUALS, method, PARAM_BEGIN];
    if (self.onEvent) {
        self.onEvent(method_hash, params);
    }
}

- (void)dealloc
{
    LOGI("AceVideo dealloc");
}

- (void)releaseObject
{
    LOGI("AceVideo releaseObject");
    if (self.player_.currentItem && _isAddedLisenten) {
        @try {
            _isAddedLisenten = false;
            [self.player_.currentItem removeObserver:self forKeyPath:@"status"];
            [self.player_.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        } @catch (NSException *exception) {}
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    if (_displayLink) {
        [self.displayLink invalidate];
    }
    if (self.player_) {
        if (self.player_.timeControlStatus == AVPlayerTimeControlStatusPlaying) {
            [self pause];
        }
    }
    self.renderTexture = nil;
    self.url = nil;
    self.rawSrc = nil;
    if (self.callSyncMethodMap) {
        for (id key in self.callSyncMethodMap) {
            IAceOnCallSyncResourceMethod block = [self.callSyncMethodMap objectForKey:key];
            block = nil;
        }
        [self.callSyncMethodMap removeAllObjects];
        self.callSyncMethodMap = nil;
    }
}

- (int64_t)getMediaDuration
{
   return [AceVideo convertCMTimetoMillis:[[self.player_ currentItem] duration]];
}

+ (int64_t)convertCMTimetoMillis:(CMTime)cmtime
{
    if (CMTIME_IS_INDEFINITE(cmtime)) {
        return -9223372036854775807;
    }
    if (cmtime.timescale == 0) {
        return 0;
    }
    return cmtime.value * 1000 / cmtime.timescale;
}
@end