//
//  stMobileDetected.m
//  particle
//
//  Created by Meicam on 2017/11/21.
//  Copyright © 2017年 NewAuto video team. All rights reserved.
//

#import "stMobileDetected.h"
#import <AVKit/AVKit.h>
#import <CommonCrypto/CommonDigest.h>
#import "NvsAssetPackageParticleDescParser.h"
#import "NvsParticleSystemContext.h"


@implementation stMobileDetected
{
    st_handle_t _hDetector; // detector句柄
    BOOL    _isCreateHumanActionHandleSucceeded;
    int     _detectConfig;
    int     _noActionProidTime;
    NvsParticleSystemContext* _particleContext;
    NSArray*           _emitterNameList0;
    NSArray*           _emitterNameList1;
    NvsEmitterPlace    _emitterPlace0;
    NvsEmitterPlace    _emitterPlace1;
    
    uint64_t           _allHumanAction;
    uint64_t           _noActionStart;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.curentTirection = [NSMutableArray array];
        _hDetector = nil;
        _isCreateHumanActionHandleSucceeded = NO;
        _detectConfig = 0;
        _noActionProidTime = 0;
        _particleContext = nil;
        
        _allHumanAction = ST_MOBILE_EYE_BLINK | ST_MOBILE_MOUTH_AH |
        ST_MOBILE_HEAD_YAW | ST_MOBILE_HEAD_PITCH |
        ST_MOBILE_BROW_JUMP | ST_MOBILE_HAND_GOOD |
        ST_MOBILE_HAND_PALM | ST_MOBILE_HAND_LOVE |
        ST_MOBILE_HAND_HOLDUP | ST_MOBILE_HAND_CONGRATULATE |
        ST_MOBILE_HAND_FINGER_HEART | ST_MOBILE_HAND_FINGER_HEART |
        ST_MOBILE_HAND_OK | ST_MOBILE_HAND_SCISSOR |
        ST_MOBILE_HAND_PISTOL | ST_MOBILE_HAND_FINGER_INDEX ;
     }
    
    return self;
}

- (void)dealloc
{
    if (_hDetector) {
        
        st_mobile_human_action_destroy(_hDetector);
        _hDetector = NULL;
        _particleContext = nil;
        _emitterNameList0 = nil;
        _emitterNameList1 = nil;
    }
}

//验证license
- (BOOL)checkActiveCode
{
    NSString *strLicensePath = [[NSBundle mainBundle] pathForResource:@"SENSEME" ofType:@"lic"];
    NSData *dataLicense = [NSData dataWithContentsOfFile:strLicensePath];
    
    NSString *strKeySHA1 = @"SENSEME";
    NSString *strKeyActiveCode = @"ACTIVE_CODE";
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    NSString *strStoredSHA1 = [userDefaults objectForKey:strKeySHA1];
    NSString *strLicenseSHA1 = [self getSHA1StringWithData:dataLicense];
    
    st_result_t iRet = ST_OK;
    
    
    if (strStoredSHA1.length > 0 && [strLicenseSHA1 isEqualToString:strStoredSHA1]) {
        
        // Get current active code
        // In this app active code was stored in NSUserDefaults
        // It also can be stored in other places
        NSData *activeCodeData = [userDefaults objectForKey:strKeyActiveCode];
        
        // Check if current active code is available

        // use buffer
        
        iRet = st_mobile_check_activecode_from_buffer(
                                                      [dataLicense bytes],
                                                      (int)[dataLicense length],
                                                      [activeCodeData bytes],
                                                      (int)[activeCodeData length]
                                                      );
      
        
        if (ST_OK == iRet) {
            
            // check success
            return YES;
        }
    }
    
    /*
     1. check fail
     2. new one
     3. update
     */
    
    char active_code[1024];
    int active_code_len = 1024;
    
    // generate one

    
    // use buffer
    
    iRet = st_mobile_generate_activecode_from_buffer(
                                                     [dataLicense bytes],
                                                     (int)[dataLicense length],
                                                     active_code,
                                                     &active_code_len
                                                     );

    if (ST_OK != iRet) {
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"错误提示" message:@"使用 license 文件生成激活码时失败，可能是授权文件过期。" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
        
        [alert show];
        
        return NO;
        
    } else {
        
        // Store active code
        NSData *activeCodeData = [NSData dataWithBytes:active_code length:active_code_len];
        
        [userDefaults setObject:activeCodeData forKey:strKeyActiveCode];
        [userDefaults setObject:strLicenseSHA1 forKey:strKeySHA1];
        
        [userDefaults synchronize];
    }
    
    return YES;
}

- (NSString *)getSHA1StringWithData:(NSData *)data
{
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    
    CC_SHA1(data.bytes, (unsigned int)data.length, digest);
    
    NSMutableString *strSHA1 = [NSMutableString string];
    
    for (int i = 0 ; i < CC_SHA1_DIGEST_LENGTH ; i ++) {
        
        [strSHA1 appendFormat:@"%02x" , digest[i]];
    }
    
    return strSHA1;
}


- (BOOL)setupHandle {
    
    st_result_t iRet = ST_OK;
    
    
    //初始化检测模块句柄
    NSString *strModelPath = [[NSBundle mainBundle] pathForResource:@"action5.0.0" ofType:@"model"];
    
    uint32_t config = 0;
    
    config = ST_MOBILE_HUMAN_ACTION_DEFAULT_CONFIG_VIDEO;
    

    iRet = st_mobile_human_action_create(strModelPath.UTF8String,
                                         config,
                                         &_hDetector);
    
    
    if (ST_OK != iRet || !_hDetector) {
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"错误提示" message:@"算法SDK初始化失败，可能是模型路径错误，SDK权限过期，与绑定包名不符" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
        
        [alert show];
        
        return NO;
    }
    
    //设置张嘴动作的阈值,置信度为[0,1], 默认阈值为0.5
    st_mobile_set_mouthah_threshold(0.4f);
    return YES;
}

- (BOOL)initSTMobileDetected
{
    if(![self checkActiveCode])
      return NO;
    
    if(![self setupHandle])
        return NO;
    
    _noActionStart = -1;
    _isCreateHumanActionHandleSucceeded = YES;
    return YES;
    
}

- (void)setDetectedConfig:(NvsParticleType) type
{
    if(type == NvsParticleType_Mouth){
        _noActionProidTime = 100;
        _detectConfig =  ST_MOBILE_FACE_DETECT | ST_MOBILE_MOUTH_AH ;  ///<  嘴巴大张
    }else if(type == NvsParticleType_Eye){
        _noActionProidTime = 5000;
        _detectConfig =  ST_MOBILE_FACE_DETECT | ST_MOBILE_EYE_BLINK;///<  眨眼
    }else if(type == NvsParticleType_Gesture){
        _noActionProidTime = 2000;
        _detectConfig = ST_MOBILE_HAND_FINGER_INDEX;///<  食指指尖
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_PALM; ///<  手掌 4096
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_LOVE;///<  爱心 16384
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_HOLDUP; ///<  托手 32768
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_CONGRATULATE;///<  恭贺（抱拳） 131072
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_FINGER_HEART;///<  单手比爱心 262144
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_TWO_INDEX_FINGER;///< 平行手指 524288
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_OK;///< OK手势
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_SCISSOR;///< 剪刀手
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_PISTOL;///< 手枪手势
        //   _detectConfig = m_detectConfig | ST_MOBILE_HAND_GOOD;///< 大拇哥 2048
    }else{
        _detectConfig = 0;
        _noActionProidTime = 2000;
    }
}

//设置正在使用的粒子效果特技
- (void) setCurrentParticleEffect:(NvsParticleSystemContext*) fx effectDesc: (NvsAssetPackageParticleDescParser*)parxml {
    
    @synchronized(self){
        _particleContext = fx;
        if(parxml != nil){
            _emitterNameList0 = nil;
            _emitterNameList1 = nil;
            NvsParticleType particleType = [parxml getParticleType];
            if(particleType == NvsParticleType_Eye){
                _emitterPlace0 = [parxml getLeftEyePlace];
                _emitterPlace1 = [parxml getRightEyePlace];
                _emitterNameList0 = [parxml getLeftEyeEmitter];
                _emitterNameList1 = [parxml getRightEyeEmitter];
            }else{
                _emitterPlace0 = [parxml getParticlePartitionPlace:0];
                _emitterNameList0 = [parxml getParticlePartitionEmitter:0];
            }
            [self enableParticle:NO];
            [self setDetectedConfig:particleType];
        }else{
            [self setDetectedConfig:0];
        }
        
    }
    
    
}

- (void)enableParticle:(BOOL)enable
{
    @synchronized(self) {
        if (!_particleContext)
            return;
        
        if(_emitterNameList0) {
            for (int i = 0; i < _emitterNameList0.count; i++)
                [_particleContext setEmitterEnabled:_emitterNameList0[i] emitterEnable:enable];
            
        }
        
        if(_emitterNameList1) {
            for (int i = 0; i < _emitterNameList1.count; i++)
                [_particleContext setEmitterEnabled:_emitterNameList1[i] emitterEnable:enable];
        }
    }
}

- (void) changeParticleParam:(float)rateGain posX:(float)x poxY:(float)y withWidth:(int)width withHeight:(int)height
{
    @synchronized(self) {
        if (!_particleContext)
            return;
        
        if(_emitterNameList0) {
            for (int i = 0; i < _emitterNameList0.count; i++){
                //转换坐标
                CGPoint mapPoint = [self.currentFx mapPointFromImageCoordToParticleSystemCoord:width withHeight:height withPtImage:CGPointMake(x, y)];
                [_particleContext setEmitterPosition:_emitterNameList0[i] emitterPositionX:mapPoint.x emitterPositionY:mapPoint.y];
                if(rateGain > 0)
                    [_particleContext setEmitterRateGain:_emitterNameList0[i] emitterGain:rateGain];
            }
            
        }
    }
}

- (void) changeParticleParamFroEye:(st_mobile_106_t*)facePoints imageWidth:(int)w ImageHeight:(int)h
{
    st_pointf_t leftEye = facePoints->points_array[74];
    if(_emitterPlace0 == NvsEmitterPlace_Left)
        leftEye = facePoints->points_array[52];
    else if(_emitterPlace0 == NvsEmitterPlace_Right)
        leftEye = facePoints->points_array[55];
    else if(_emitterPlace0 == NvsEmitterPlace_Top)
        leftEye = facePoints->points_array[72];
    else if(_emitterPlace0 == NvsEmitterPlace_Bottom)
        leftEye = facePoints->points_array[73];
    
    st_pointf_t rightEye = facePoints->points_array[77];
    if(_emitterPlace1 == NvsEmitterPlace_Left)
        rightEye = facePoints->points_array[58];
    else if(_emitterPlace1 == NvsEmitterPlace_Right)
        rightEye = facePoints->points_array[61];
    else if(_emitterPlace1 == NvsEmitterPlace_Top)
        rightEye = facePoints->points_array[75];
    else if(_emitterPlace1 == NvsEmitterPlace_Bottom)
        rightEye = facePoints->points_array[76];
    
    float LeftEyeX = leftEye.x - (w / 2);
    float LeftEyeY = (h / 2)- leftEye.y;
    
    float RightEyeX = rightEye.x - (w / 2);
    float RightEyeY = (h / 2)- rightEye.y;
    
    @synchronized(self) {
        if (!_particleContext)
            return;
        
        if(_emitterNameList0) {
            for (int i = 0; i < _emitterNameList0.count; i++){
                //转化坐标
                CGPoint mapPoint = [self.currentFx mapPointFromImageCoordToParticleSystemCoord:w withHeight:h withPtImage:CGPointMake(LeftEyeX, LeftEyeY)];
                [_particleContext setEmitterPosition:_emitterNameList0[i] emitterPositionX:mapPoint.x emitterPositionY:mapPoint.y];
            }
        }
        
        if(_emitterNameList1) {
            for (int i = 0; i < _emitterNameList1.count; i++){
                //转化坐标
                CGPoint mapPoint = [self.currentFx mapPointFromImageCoordToParticleSystemCoord:w withHeight:h withPtImage:CGPointMake(RightEyeX, RightEyeY)];
                [_particleContext setEmitterPosition:_emitterNameList1[i] emitterPositionX:mapPoint.x emitterPositionY:mapPoint.y];
            }
        }
    }
}

- (void) stMobileDetectedFaceInfo:(st_mobile_face_t*)face imageWidth:(int)w ImageHeight:(int)h
{
    if(!face)
        return;
    
    st_pointf_t currentPoint;
    float fRateGain = 1.0f;
    unsigned long long face_action = face->face_action;
    BOOL hasAction = NO;
    
    if(face_action & ST_MOBILE_MOUTH_AH){
        st_pointf_t point98 = face->face106.points_array[98];
        st_pointf_t point102 = face->face106.points_array[102];
        
        st_pointf_t point96 = face->face106.points_array[96];
        st_pointf_t point100 = face->face106.points_array[100];
        
        float distance = point102.y - point98.y;
        float mouthWidth = point100.x - point96.x;
        if(_emitterPlace0 == NvsEmitterPlace_Center){
            if (point102.y < point98.y) {
                currentPoint = point102;
            } else {
                currentPoint.x = point102.x;
                currentPoint.y = point98.y + distance / 2;
                
            }
        }else if(_emitterPlace0 == NvsEmitterPlace_Left){
            currentPoint = point96;
        }else if(_emitterPlace0 == NvsEmitterPlace_Right){
            currentPoint = point100;
        }else if(_emitterPlace0 == NvsEmitterPlace_Top){
            currentPoint = point98;
        }else if(_emitterPlace0 == NvsEmitterPlace_Bottom){
            currentPoint = point102;
        }
        
        if (mouthWidth > 0) {
            fRateGain = distance / (mouthWidth / 2);
            fRateGain = (float) exp(fRateGain * fRateGain / 2) - 1;
            if (fRateGain > 5.0f)
                fRateGain = 5.0f;
            if (fRateGain < 0.1f)
                fRateGain = 0.1f;
        }
        
        NSLog(@"face action mouthAH(嘴巴大张) rate:%f", fRateGain);
        hasAction = YES;
    }
    
    if(_particleContext && hasAction){
        float normalX = currentPoint.x - (w / 2);
        float normalY = (h / 2)- currentPoint.y;
        [self changeParticleParam:fRateGain posX:normalX poxY:normalY withWidth:w withHeight:h];
    }
}

- (void) stMobileDetectedHandInfo:(st_mobile_hand_t*)hand imageWidth:(int)w ImageHeight:(int)h
{
    if(!hand)
        return;
    
    if(hand->key_points_count <= 0)
        return;
    
    if(_particleContext){
        st_pointf_t currentPoint = hand->p_key_points[0];
        float normalX = currentPoint.x - (w / 2);
        float normalY = (h / 2)- currentPoint.y;
        [self changeParticleParam:0 posX:normalX poxY:normalY withWidth:w withHeight:h];
    }
    
}

- (void)grabberVideoFrameArrived:(NvsVideoFrameInfo*)frameInfo
{
    if(!frameInfo)
        return;
    if(frameInfo->pixelFormat != NvsPixelFormat_Nv12)
        return;
    if(!_isCreateHumanActionHandleSucceeded)
        return;
    
    if(_detectConfig <= 0)
        return;
    
    st_mobile_human_action_t detectResult;

    st_result_t iRet = st_mobile_human_action_detect(_hDetector,
                                                 frameInfo->planePtr[0],
                                                 ST_PIX_FMT_NV12,
                                                 frameInfo->frameWidth,
                                                 frameInfo->frameHeight,
                                                 frameInfo->planeRowPitch[0],
                                                 self.rotate_type,
                                                 _detectConfig,
                                                 &detectResult);
    if(iRet == ST_OK) {
        if(detectResult.face_count > 0){
            NSLog(@"%lld",detectResult.p_faces[0].face_action);
            if(((detectResult.p_faces[0].face_action) & _allHumanAction) > 0){
                _noActionStart = [self getCurrentTim];
                for (NSString *tirection in self.curentTirection) {
                    [_particleContext setEmitterEnabled:tirection emitterEnable:YES];
                }
//                [self enableParticle:YES];
                [self stMobileDetectedFaceInfo:&detectResult.p_faces[0] imageWidth:frameInfo->frameWidth ImageHeight:frameInfo->frameHeight];
                
            }else{
                //如果是眨眼动作,继续跟随眼部运动
                BOOL isEyeBlinkActionDetected = (_detectConfig&ST_MOBILE_EYE_BLINK) > 0 ? YES : NO;
                if(_noActionStart > 0 && isEyeBlinkActionDetected){
                    for (NSString *tirection in self.curentTirection) {
                        [_particleContext setEmitterEnabled:tirection emitterEnable:YES];
                    }
                    [self changeParticleParamFroEye:&detectResult.p_faces[0].face106 imageWidth:frameInfo->frameWidth ImageHeight:frameInfo->frameHeight];
                }
            }
        }
        
        if(detectResult.hand_count > 0){
            if((detectResult.p_hands[0].hand_action & _allHumanAction) > 0){
                _noActionStart = [self getCurrentTim];
                [self enableParticle:YES];
                [self stMobileDetectedHandInfo:&detectResult.p_hands[0] imageWidth:frameInfo->frameWidth ImageHeight:frameInfo->frameHeight];
            }
        }
        
    }
    
    long currentTime = [self getCurrentTim];
    if(_noActionStart > 0){
        if((currentTime - _noActionStart) > _noActionProidTime){
            
            [self enableParticle:NO];
            _noActionStart = -1;
            NSLog(@"NO Action Detected");
        }
    }
}

- (uint64_t) getCurrentTim
{
    NSTimeInterval time = [[NSDate date] timeIntervalSince1970]*1000;
    uint64_t ui64Time = [[NSNumber numberWithDouble:time] longLongValue];
    
    return ui64Time;
}

@end
