#import "TGPasscodeEntryController.h"

#import "TGPasswordEntryView.h"

#import "TGStringUtils.h"

#import "TGTimerTarget.h"

#import <AudioToolbox/AudioToolbox.h>
#import <LocalAuthentication/LocalAuthentication.h>

typedef enum {
    TGPasscodeEntryControllerSubmodeEnteringCurrent,
    TGPasscodeEntryControllerSubmodeEnteringNew,
    TGPasscodeEntryControllerSubmodeReenteringNew
} TGPasscodeEntryControllerSubmode;

@interface TGPasscodeEntryController ()
{
    TGPasscodeEntryControllerStyle _style;
    TGPasscodeEntryControllerMode _mode;
    TGPasscodeEntryControllerSubmode _submode;
    bool _cancelEnabled;
    
    NSString *_candidatePasscode;
    
    TGPasswordEntryView *_view;
    
    UIBarButtonItem *_nextItem;
    bool _usingTouchId;
    bool _alternativeMethodSelected;
    
    NSTimer *_shouldWaitTimer;
    bool _keepStatusBarStyle;
}

@end

@implementation TGPasscodeEntryController

- (instancetype)initWithStyle:(TGPasscodeEntryControllerStyle)style mode:(TGPasscodeEntryControllerMode)mode cancelEnabled:(bool)cancelEnabled allowTouchId:(bool)allowTouchId completion:(void (^)(NSString *))completion
{
    self = [super init];
    if (self != nil)
    {
        _style = style;
        _mode = mode;
        _completion = [completion copy];
        _allowTouchId = allowTouchId;
        _cancelEnabled = cancelEnabled;
        _keepStatusBarStyle = true;
        
        switch (_mode)
        {
            case TGPasscodeEntryControllerModeVerifySimple:
            case TGPasscodeEntryControllerModeVerifyComplex:
            {
                break;
            }
            case TGPasscodeEntryControllerModeSetupSimple:
            case TGPasscodeEntryControllerModeSetupComplex:
            {
                _submode = TGPasscodeEntryControllerSubmodeEnteringNew;
                
                break;
            }
            case TGPasscodeEntryControllerModeChangeSimpleToSimple:
            case TGPasscodeEntryControllerModeChangeSimpleToComplex:
            case TGPasscodeEntryControllerModeChangeComplexToSimple:
            case TGPasscodeEntryControllerModeChangeComplexToComplex:
            {
                _submode = TGPasscodeEntryControllerSubmodeEnteringCurrent;
                
                break;
            }
        }
        
        self.navigationBarShouldBeHidden = true;
        
        if ([self invalidPasscodeAttempts] >= 6 && ![self shouldWaitBeforeAttempting])
        {
            [self resetInvalidPasscodeAttempts];
        }
        
        [self resetMode:_mode];
        
        _shouldWaitTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(checkShouldWait) interval:1.0 repeat:true];
    }
    return self;
}

- (void)dealloc
{
    [_shouldWaitTimer invalidate];
    _shouldWaitTimer = nil;
}

- (void)cancelPressed
{
    if (_completion)
        _completion(nil);
    
    [_view resignFirstResponder];
}

- (void)nextPressed
{
    [self passcodeEntered:[_view passcode]];
}

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return _keepStatusBarStyle ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
}

- (TGPasswordEntryViewStyle)_passwordEntryViewStyleForStyle:(TGPasscodeEntryControllerStyle)style
{
    switch (style)
    {
        case TGPasscodeEntryControllerStyleDefault:
            return TGPasswordEntryViewStyleDefault;
        case TGPasscodeEntryControllerStyleTranslucent:
            return TGPasswordEntryViewStyleTranslucent;
    }
}

- (void)loadView
{
    [super loadView];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    CGSize screenSize = self.view.frame.size;
    if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
    {
        screenSize = [UIScreen mainScreen].bounds.size;
        if (screenSize.width > screenSize.height)
        {
            CGFloat tmp = screenSize.width;
            screenSize.width = screenSize.height;
            screenSize.height = tmp;
        }
    }
    
    _view = [[TGPasswordEntryView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, screenSize.width, screenSize.height) style:[self _passwordEntryViewStyleForStyle:_style]];
    if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
        _view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    __weak TGPasscodeEntryController *weakSelf = self;
    if (_cancelEnabled)
    {
        _view.cancel = ^
        {
            __strong TGPasscodeEntryController *strongSelf = weakSelf;
            if (strongSelf != nil)
                [strongSelf cancelPressed];
        };
    }
    _view.simplePasscodeEntered = ^
    {
        __strong TGPasscodeEntryController *strongSelf = weakSelf;
        if (strongSelf != nil)
            [strongSelf passcodeEntered:[strongSelf->_view passcode]];
    };
    _view.complexPasscodeEntered = ^
    {
        __strong TGPasscodeEntryController *strongSelf = weakSelf;
        if (strongSelf != nil)
            [strongSelf passcodeEntered:[strongSelf->_view passcode]];
    };
    _view.passcodeChanged = ^(NSString *passcode)
    {
        __strong TGPasscodeEntryController *strongSelf = weakSelf;
        if (strongSelf != nil)
            strongSelf->_nextItem.enabled = passcode.length != 0;
    };
    [self.view addSubview:_view];
    
    [self resetMode:_mode];
    
    [_view becomeFirstResponder];
}

- (bool)supportsTouchId
{
    if (iosMajorVersion() >= 8)
    {
        if ([[[LAContext alloc] init] canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil])
            return true;
    }
    
    return false;
}

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    
    /*if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
    {
        UIView *snapshotView = [_view snapshotViewAfterScreenUpdates:false];
        snapshotView.frame = _view.frame;
        [self.view addSubview:snapshotView];
        [UIView animateWithDuration:duration animations:^
        {
            snapshotView.alpha = 0.0f;
            snapshotView.frame = (CGRect){CGPointZero, [self referenceViewSizeForOrientation:toInterfaceOrientation]};
        } completion:^(__unused BOOL finished)
        {
            [snapshotView removeFromSuperview];
        }];
    }*/
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}

- (void)prepareForAppear
{
    [_view updateBackgroundIfNeeded];
    
    _keepStatusBarStyle = true;
    if (iosMajorVersion() >= 7)
        [self setNeedsStatusBarAppearanceUpdate];
}

- (void)prepareForDisappear
{
    _keepStatusBarStyle = false;
    if (iosMajorVersion() >= 7)
        [self setNeedsStatusBarAppearanceUpdate];
}

- (void)refreshTouchId
{
    [_view resignFirstResponder];
    [_view becomeFirstResponder];
    
    if (!_usingTouchId && !_alternativeMethodSelected && _allowTouchId && [self supportsTouchId] && _touchIdCompletion)
    {
        LAContext *context = [[LAContext alloc] init];
        
        NSError *error = nil;
        if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error])
        {
            _usingTouchId = true;
            [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:TGLocalized(@"EnterPasscode.TouchId") reply:^(BOOL success, NSError *error)
            {
                if (error != nil)
                {
                    TGDispatchOnMainThread(^
                    {
                        _usingTouchId = false;
                        _alternativeMethodSelected = true;
                    });
                }
                else
                {
                    if (success)
                    {
                        TGDispatchOnMainThread(^
                        {
                            _usingTouchId = false;
                            if (_touchIdCompletion)
                                _touchIdCompletion();
                        });
                    }
                    else
                    {
                        TGDispatchOnMainThread(^
                        {
                            _usingTouchId = false;
                        });
                    }
                }
            }];
        }
    }
}

- (NSInteger)invalidPasscodeAttempts
{
    return [[[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_invalidAttempts"] integerValue];
}

- (void)addInvalidPasscodeAttempt
{
    [[NSUserDefaults standardUserDefaults] setObject:@([self invalidPasscodeAttempts] + 1) forKey:@"Passcode_invalidAttempts"];
    [[NSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) forKey:@"Passcode_invalidAttemptDate"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (void)resetInvalidPasscodeAttempts
{
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"Passcode_invalidAttempts"];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"Passcode_invalidAttemptDate"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (bool)shouldWaitBeforeAttempting
{
    if ([self invalidPasscodeAttempts] < 6)
        return false;
    NSTimeInterval invalidAttemptDate = [[[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_invalidAttemptDate"] doubleValue];
    NSTimeInterval waitInterval = 60.0;
#ifdef DEBUG
    waitInterval = 5.0;
#endif
    return [[NSDate date] timeIntervalSince1970] - invalidAttemptDate < waitInterval;
}

- (NSTimeInterval)intervalSinceLastInvalidPasscodeAttempt
{
    NSTimeInterval invalidAttemptDate = [[[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_invalidAttemptDate"] doubleValue];
    return [[NSDate date] timeIntervalSince1970] - invalidAttemptDate;
}

- (void)checkShouldWait
{
    if ([self invalidPasscodeAttempts] >= 6 && ![self shouldWaitBeforeAttempting])
    {
        [self resetInvalidPasscodeAttempts];
    }
    [_view setErrorTitle:[self currentErrorText]];
}

- (NSString *)currentErrorText
{
    NSInteger attemptCount = [self invalidPasscodeAttempts];
    if (attemptCount == 0)
        return @"";
    else if (attemptCount < 6)
    {
        NSString *format = [TGStringUtils integerValueFormat:@"PasscodeSettings.FailedAttempts_" value:attemptCount];
        return [[NSString alloc] initWithFormat:TGLocalized(format), [[NSString alloc] initWithFormat:@"%d",(int)attemptCount]];
    }
    else
        return TGLocalized(@"PasscodeSettings.TryAgainIn1Minute");
}

- (void)resetMode:(TGPasscodeEntryControllerMode)mode
{
    _candidatePasscode = nil;
    _mode = mode;
    
    switch (_mode)
    {
        case TGPasscodeEntryControllerModeVerifySimple:
        case TGPasscodeEntryControllerModeVerifyComplex:
        {
            [_view setTitle:TGLocalized(@"EnterPasscode.EnterPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeVerifyComplex animated:false];
            
            if (_mode == TGPasscodeEntryControllerModeVerifySimple)
            {
                if (_cancelEnabled)
                {
                    [self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
                }
            }
            else if (_mode == TGPasscodeEntryControllerModeVerifyComplex)
            {
                if (_cancelEnabled)
                {
                    [self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
                }
                _nextItem = [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Next") style:UIBarButtonItemStyleDone target:self action:@selector(nextPressed)];
                [self setRightBarButtonItem:_nextItem];
                _nextItem.enabled = false;
            }
            break;
        }
        case TGPasscodeEntryControllerModeSetupSimple:
        case TGPasscodeEntryControllerModeSetupComplex:
        {
            [_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeNew") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeSetupComplex animated:false];
            
            [self setLeftBarButtonItem:nil];
            [self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
            break;
        }
        case TGPasscodeEntryControllerModeChangeComplexToSimple:
        case TGPasscodeEntryControllerModeChangeComplexToComplex:
        case TGPasscodeEntryControllerModeChangeSimpleToSimple:
        case TGPasscodeEntryControllerModeChangeSimpleToComplex:
        {
            bool isComplex = _mode == TGPasscodeEntryControllerModeChangeComplexToSimple || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex;
            [_view setTitle:TGLocalized(@"EnterPasscode.EnterCurrentPasscode") errorTitle:[self currentErrorText] isComplex:isComplex animated:false];
            
            if (_mode == TGPasscodeEntryControllerModeChangeSimpleToSimple)
            {
                [self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
            }
            else
            {
                [self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
            }
            
            _nextItem = [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Next") style:UIBarButtonItemStyleDone target:self action:@selector(nextPressed)];
            if (_mode == TGPasscodeEntryControllerModeChangeComplexToSimple || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex)
            {
                [self setRightBarButtonItem:_nextItem];
            }
            _nextItem.enabled = false;
            
            break;
        }
    }
    
    [_view becomeFirstResponder];
}

- (void)passcodeEntered:(NSString *)passcode
{
    if ([self shouldWaitBeforeAttempting])
    {
        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
        [_view resetPasscode];
        return;
    }
    else if ([self invalidPasscodeAttempts] >= 6)
    {
        [self resetInvalidPasscodeAttempts];
    }
    
    switch (_mode)
    {
        case TGPasscodeEntryControllerModeVerifySimple:
        case TGPasscodeEntryControllerModeVerifyComplex:
        {
            _alternativeMethodSelected = false;
            
            if (_checkCurrentPasscode)
            {
                if (_checkCurrentPasscode(passcode))
                {
                    [self resetInvalidPasscodeAttempts];
                    
                    if (_completion)
                        _completion(passcode);
                }
                else
                {
                    [self addInvalidPasscodeAttempt];
                    
                    [_view setTitle:TGLocalized(@"EnterPasscode.EnterPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeVerifyComplex animated:false];
                    
                    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
                }
            }
            else
            {
                if (_completion)
                    _completion(passcode);
            }
            
            break;
        }
        case TGPasscodeEntryControllerModeSetupSimple:
        case TGPasscodeEntryControllerModeSetupComplex:
        {
            switch (_submode)
            {
                case TGPasscodeEntryControllerSubmodeEnteringNew:
                {
                    _candidatePasscode = passcode;
                    _submode = TGPasscodeEntryControllerSubmodeReenteringNew;
                    [_view setTitle:TGLocalized(@"EnterPasscode.RepeatNewPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeSetupComplex animated:true];
                    break;
                }
                case TGPasscodeEntryControllerSubmodeReenteringNew:
                {
                    if ([passcode isEqualToString:_candidatePasscode])
                    {
                        if (_completion)
                            _completion(passcode);
                    }
                    else
                    {
                        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
                        _candidatePasscode = nil;
                        _submode = TGPasscodeEntryControllerSubmodeEnteringNew;
                        [_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeNew") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeSetupComplex animated:true];
                    }
                    break;
                }
                default:
                    break;
            }
            break;
        }
        case TGPasscodeEntryControllerModeChangeSimpleToSimple:
        case TGPasscodeEntryControllerModeChangeSimpleToComplex:
        case TGPasscodeEntryControllerModeChangeComplexToSimple:
        case TGPasscodeEntryControllerModeChangeComplexToComplex:
        {
            switch (_submode)
            {
                case TGPasscodeEntryControllerSubmodeEnteringCurrent:
                {
                    if (_checkCurrentPasscode && _checkCurrentPasscode(passcode))
                    {
                        [self resetInvalidPasscodeAttempts];
                        
                        _candidatePasscode = nil;
                        _submode = TGPasscodeEntryControllerSubmodeEnteringNew;
                        [_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeChange") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
                        
                        if (_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex)
                            [self setRightBarButtonItem:_nextItem];
                        else
                            [self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
                    }
                    else
                    {
                        [self addInvalidPasscodeAttempt];
                        
                        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
                        _candidatePasscode = nil;
                        _submode = TGPasscodeEntryControllerSubmodeEnteringCurrent;
                        [_view setTitle:TGLocalized(@"EnterPasscode.EnterCurrentPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeComplexToSimple || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
                    }
                    break;
                }
                case TGPasscodeEntryControllerSubmodeEnteringNew:
                {
                    _candidatePasscode = passcode;
                    _submode = TGPasscodeEntryControllerSubmodeReenteringNew;
                    [_view setTitle:TGLocalized(@"EnterPasscode.RepeatNewPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
                    break;
                }
                case TGPasscodeEntryControllerSubmodeReenteringNew:
                {
                    if ([_candidatePasscode isEqualToString:passcode])
                    {
                        if (_completion)
                            _completion(passcode);
                    }
                    else
                    {
                        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
                        _candidatePasscode = nil;
                        _submode = TGPasscodeEntryControllerSubmodeEnteringNew;
                        [_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeChange") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
                    }
                    break;
                }
            }
            
            break;
        }
    }
}

@end
