/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2019 Alibaba Group
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#import "FLBFlutterViewContainer.h"
#import "FLBFlutterApplication.h"
#import "FLBStackCache.h"
#import "FLBStackCacheObjectImg.h"
#import "FLBMemoryInspector.h"
#import "Service_NavigationService.h"
#import "FlutterBoostConfig.h"

#define FLUTTER_VIEW [FLBFlutterApplication sharedApplication].flutterViewController.view
#define FLUTTER_VC [FLBFlutterApplication sharedApplication].flutterViewController
#define FLUTTER_APP [FLBFlutterApplication sharedApplication]

@interface FLBFlutterViewContainer  ()
@property (nonatomic,copy,readwrite) NSString *name;
@property (nonatomic,strong,readwrite) NSDictionary *params;
@property (nonatomic,strong) UIImageView *screenShotView;
@property (nonatomic,assign) long long identifier;
@property (nonatomic,assign) BOOL interactiveGestureActive;
@end

@implementation FLBFlutterViewContainer

- (void)setName:(NSString *)name params:(NSDictionary *)params
{
    if(!_name && name){
        _name = name;
        _params = params;
        [Service_NavigationService didInitPageContainer:^(NSNumber *r) {}
                                               pageName:name
                                                 params:params
                                               uniqueId:[self uniqueIDString]];
    }
}


static NSUInteger kInstanceCounter = 0;

+ (NSUInteger)instanceCounter
{
    return kInstanceCounter;
}

+ (void)instanceCounterIncrease
{
    kInstanceCounter++;
    if(kInstanceCounter == 1){
        [FLUTTER_APP resume];
    }
}

+ (void)instanceCounterDecrease
{
    kInstanceCounter--;
    if([self.class instanceCounter] == 0){
        [[FLBStackCache sharedInstance] clear];
        [[FLBFlutterApplication sharedApplication] pause];
    }
}

- (NSString *)uniqueIDString
{
    return @(_identifier).stringValue;
}

- (instancetype)init
{
    if(self = [super init]){
        static long long sCounter = 0;
        _identifier = sCounter++;
        [self.class instanceCounterIncrease];
        [NSNotificationCenter.defaultCenter addObserver:self
                                               selector:@selector(flutterViewDidShow:) name:@"flutter_boost_container_showed"
                                                 object:nil];
    }
    return self;
}

- (void)flutterViewDidAppear:(NSDictionary *)params
{
    //Notify flutter view appeared.
}

- (void)flutterViewDidShow:(NSNotification *)notification
{
     __weak typeof(self) weakSelf = self;
    if([notification.object isEqual: self.uniqueIDString]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf showFlutterView];
        });
    }
}

- (void)dealloc
{
    [self notifyWillDealloc];
    [NSNotificationCenter.defaultCenter removeObserver:self];
}

- (void)notifyWillDealloc
{
    [Service_NavigationService willDeallocPageContainer:^(NSNumber *r) {}
                                               pageName:_name params:_params
                                               uniqueId:[self uniqueIDString]];

    [[FLBStackCache sharedInstance] remove:self.uniqueIDString];
    [[FLBFlutterApplication sharedApplication] removeViewController:self];
    
    [self.class instanceCounterDecrease];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = UIColor.whiteColor;
    
    self.screenShotView = [[UIImageView alloc] initWithFrame:self.view.bounds];
    self.screenShotView.backgroundColor = [UIColor whiteColor];
    
    [self.view addSubview:self.screenShotView];
}

#pragma mark - ScreenShots
- (UIImage *)takeScreenShot
{
    CGFloat scale = 1;
    switch ([FLBMemoryInspector.sharedInstance currentCondition]) {
        case FLBMemoryConditionNormal:
            scale = 2;
            break;
        case FLBMemoryConditionLowMemory:
            scale = 1;
            break;
        case FLBMemoryConditionExtremelyLow:
            scale = 0.75;
            break;
        case FLBMemoryConditionAboutToDie:
            return [UIImage new];
            break;
        case FLBMemoryConditionUnknown:
            if([[FLBMemoryInspector sharedInstance] smallMemoryDevice]){
                scale = 1;
            }else{
                scale = 2;
            }
            break;
    }
    
    self.screenShotView.opaque = YES;
    
    CGRect flutterBounds = self.view.bounds;
    CGSize snapshotSize = CGSizeMake(flutterBounds.size.width ,
                                     flutterBounds.size.height);
    UIGraphicsBeginImageContextWithOptions(snapshotSize, NO, scale);
    
    [self.view drawViewHierarchyInRect:flutterBounds
                    afterScreenUpdates:NO];
    
    UIImage *snapImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return snapImage;
}

- (void)saveScreenShot
{
    UIImage *snapImage = [self takeScreenShot];
    if(snapImage){
        FLBStackCacheObjectImg *cImg = [[FLBStackCacheObjectImg alloc] initWithImage:snapImage];
        [[FLBStackCache sharedInstance] pushObject:cImg key:self.uniqueIDString];
    }
}

- (void)clearCurrentScreenShotImage
{
    self.screenShotView.image = nil;
}

- (UIImage *)getSavedScreenShot
{
    FLBStackCacheObjectImg *cImg = [[FLBStackCache sharedInstance] objectForKey:self.uniqueIDString];
    return [cImg image];
}

- (BOOL)isFlutterViewAttatched
{
    return FLUTTER_VIEW.superview == self.view;
}

- (void)attatchFlutterView
{
    if([self isFlutterViewAttatched]) return;
    
    [FLUTTER_VC willMoveToParentViewController:nil];
    [FLUTTER_VC removeFromParentViewController];
    [FLUTTER_VC didMoveToParentViewController:nil];
   
    [FLUTTER_VC willMoveToParentViewController:self];
    FLUTTER_VIEW.frame = self.view.bounds;
    
    if(!self.screenShotView.image){
         [self.view addSubview: FLUTTER_VIEW];
    }else{
        [self.view insertSubview:FLUTTER_VIEW belowSubview:self.screenShotView];
    }

    [self addChildViewController:FLUTTER_VC];
    [FLUTTER_VC didMoveToParentViewController:self];
}

- (BOOL)showSnapShotVew
{
    self.screenShotView.image = [self getSavedScreenShot];
   
    if([self isFlutterViewAttatched]){
        NSUInteger fIdx = [self.view.subviews indexOfObject:FLUTTER_VIEW];
        NSUInteger sIdx = [self.view.subviews indexOfObject:self.screenShotView];
        if(fIdx > sIdx){
            [self.view insertSubview:FLUTTER_VIEW
                             atIndex:0];
        }
    }else{
        
    }
    
    return self.screenShotView.image != nil;
}

- (void)showFlutterView
{
    if(FLUTTER_VIEW.superview != self.view) return;
    
    if([self isFlutterViewAttatched] ){
        NSUInteger fIdx = [self.view.subviews indexOfObject:FLUTTER_VIEW];
        NSUInteger sIdx = [self.view.subviews indexOfObject:self.screenShotView];
        self.screenShotView.backgroundColor = UIColor.clearColor;
        if(sIdx > fIdx){
            [self.view insertSubview:self.screenShotView belowSubview:FLUTTER_VIEW];
            [self flutterViewDidAppear:@{@"uid":self.uniqueIDString?:@""}];
        }
    }
    
    [self clearCurrentScreenShotImage];
    
    //Invalidate obsolete screenshot.
    [FLBStackCache.sharedInstance invalidate:self.uniqueIDString];
}

#pragma mark - Life circle methods

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [[FLBFlutterApplication sharedApplication] resume];
}

- (void)viewWillAppear:(BOOL)animated
{
    if(self.navigationController.interactivePopGestureRecognizer.state == UIGestureRecognizerStateBegan){
        self.interactiveGestureActive = true;
    }
    
    [[FLBFlutterApplication sharedApplication] resume];
    //For new page we should attach flutter view in view will appear
    //for better performance.
    if(![[FLBFlutterApplication sharedApplication] contains:self]){
         [self attatchFlutterView];
    }
    
    [self showSnapShotVew];
    
    [Service_NavigationService willShowPageContainer:^(NSNumber *result) {}
                                            pageName:_name
                                              params:_params
                                            uniqueId:self.uniqueIDString];
    //Save some first time page info.
    if(![FlutterBoostConfig sharedInstance].fPagename){
        [FlutterBoostConfig sharedInstance].fPagename = _name;
        [FlutterBoostConfig sharedInstance].fPageId = self.uniqueIDString;
        [FlutterBoostConfig sharedInstance].fParams = _params;
    }
    
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [[FLBFlutterApplication sharedApplication] resume];
   
    //Ensure flutter view is attached.
    [self attatchFlutterView];
    
    [Service_NavigationService didShowPageContainer:^(NSNumber *result) {}
                                           pageName:_name
                                             params:_params
                                           uniqueId:self.uniqueIDString];
    
    [[FLBFlutterApplication sharedApplication] addUniqueViewController:self];
    
    [super viewDidAppear:animated];
 
    __weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                 (int64_t)(2 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(),^{
                       if (weakSelf.isViewLoaded && weakSelf.view.window) {
                           // viewController is visible
                            [weakSelf showFlutterView];
                       }
                   });
    
    self.interactiveGestureActive = NO;
}

- (void)viewWillDisappear:(BOOL)animated
{
    //is top.
    if([FLUTTER_APP isTop:self.uniqueIDString]
       && self.navigationController.interactivePopGestureRecognizer.state != UIGestureRecognizerStateBegan
       && !self.interactiveGestureActive){
        [self saveScreenShot];
    }
    
    self.interactiveGestureActive = NO;
   
    self.screenShotView.image = [self getSavedScreenShot];
    if(self.screenShotView.image){
        [self.view bringSubviewToFront:self.screenShotView];
    }
   
    [Service_NavigationService willDisappearPageContainer:^(NSNumber *result) {}
                                                 pageName:_name
                                                   params:_params
                                                 uniqueId:self.uniqueIDString];
    [super viewWillDisappear:animated];
}


- (void)viewDidDisappear:(BOOL)animated
{
    [Service_NavigationService didDisappearPageContainer:^(NSNumber *result) {}
                                                pageName:_name
                                                  params:_params
                                                uniqueId:self.uniqueIDString];
    
    [self clearCurrentScreenShotImage];
    [super viewDidDisappear:animated];

    [FLUTTER_APP inactive];
    self.interactiveGestureActive = NO;
}

#pragma mark - FLBViewControllerResultHandler
- (void)onRecievedResult:(NSDictionary *)resultData forKey:(NSString *)key
{
    [Service_NavigationService onNativePageResult:^(NSNumber *finished) {}
                                         uniqueId:self.uniqueIDString
                                              key:key
                                       resultData:resultData
                                           params:@{}];
}


@end