/*
 Copyright 2015 OpenMarket 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 "MXKAuthenticationFallbackWebView.h"

// Generic method to make a bridge between JS and the WKWebView
NSString *kMXKJavascriptSendObjectMessage = @"window.sendObjectMessage = function(parameters) {   \
var iframe = document.createElement('iframe');                              \
iframe.setAttribute('src', 'js:' + JSON.stringify(parameters));             \
\
document.documentElement.appendChild(iframe);                               \
iframe.parentNode.removeChild(iframe);                                      \
iframe = null;                                                              \
};";

// The function the fallback page calls when the registration is complete
NSString *kMXKJavascriptOnRegistered = @"window.matrixRegistration.onRegistered = function(homeserverUrl, userId, accessToken) {   \
sendObjectMessage({  \
'action': 'onRegistered',           \
'homeServer': homeserverUrl,        \
'userId': userId,                   \
'accessToken': accessToken          \
});                                     \
};";

// The function the fallback page calls when the login is complete
NSString *kMXKJavascriptOnLogin = @"window.matrixLogin.onLogin = function(response) {   \
sendObjectMessage({  \
'action': 'onLogin',           \
'response': response        \
});                                     \
};";

@interface MXKAuthenticationFallbackWebView ()
{
    // The block called when the login or the registration is successful
    void (^onSuccess)(MXLoginResponse *);
    
    // Activity indicator
    UIActivityIndicatorView *activityIndicator;
}
@end

@implementation MXKAuthenticationFallbackWebView

- (void)dealloc
{
    if (activityIndicator)
    {
        [activityIndicator removeFromSuperview];
        activityIndicator = nil;
    }
}

- (void)openFallbackPage:(NSString *)fallbackPage success:(void (^)(MXLoginResponse *))success
{
    self.navigationDelegate = self;
    
    onSuccess = success;
    
    // Add activity indicator
    activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    activityIndicator.center = self.center;
    [self addSubview:activityIndicator];
    [activityIndicator startAnimating];

    // Delete cookies to launch login process from scratch
    for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])
    {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }
    
    [self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:fallbackPage]]];
}

#pragma mark - WKNavigationDelegate

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    if (activityIndicator)
    {
        [activityIndicator stopAnimating];
        [activityIndicator removeFromSuperview];
        activityIndicator = nil;
    }
    
    [self evaluateJavaScript:kMXKJavascriptSendObjectMessage completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        
    }];
    [self evaluateJavaScript:kMXKJavascriptOnRegistered completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        
    }];
    [self evaluateJavaScript:kMXKJavascriptOnLogin completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        
    }];
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    MXLogDebug(@"[MXKAuthenticationFallbackWebView] decidePolicyForNavigationAction");
    
    NSString *urlString = navigationAction.request.URL.absoluteString;
    
    if ([urlString hasPrefix:@"js:"])
    {
        //  do not log urlString, it may have an access token
        MXLogDebug(@"[MXKAuthenticationFallbackWebView] URL has js: prefix");
        
        // Listen only to scheme of the JS-WKWebView bridge
        NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject]  stringByRemovingPercentEncoding];
        NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
        
        NSError *error;
        NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
                                                                     error:&error];
        
        if (error)
        {
            MXLogDebug(@"[MXKAuthenticationFallbackWebView] Error when parsing json: %@", error);
        }
        else
        {
            if ([@"onRegistered" isEqualToString:parameters[@"action"]])
            {
                // Translate the JS registration event to MXLoginResponse
                // We cannot use [MXLoginResponse modelFromJSON:] because of https://github.com/matrix-org/synapse/issues/4756
                // Because of this issue, we cannot get the device_id allocated by the homeserver
                // TODO: Fix it once the homeserver issue is fixed (filed at https://github.com/vector-im/riot-meta/issues/273).
                MXLoginResponse *loginResponse = [MXLoginResponse new];
                loginResponse.homeserver = parameters[@"homeServer"];
                loginResponse.userId = parameters[@"userId"];
                loginResponse.accessToken = parameters[@"accessToken"];
                
                MXLogDebug(@"[MXKAuthenticationFallbackWebView] Registered on homeserver: %@", loginResponse.homeserver);

                // Sanity check
                if (loginResponse.homeserver.length && loginResponse.userId.length && loginResponse.accessToken.length)
                {
                    MXLogDebug(@"[MXKAuthenticationFallbackWebView] Call success block");
                    // And inform the client
                    onSuccess(loginResponse);
                }
            }
            else if ([@"onLogin" isEqualToString:parameters[@"action"]])
            {
                // Translate the JS login event to MXLoginResponse
                MXLoginResponse *loginResponse;
                MXJSONModelSetMXJSONModel(loginResponse, MXLoginResponse, parameters[@"response"]);

                MXLogDebug(@"[MXKAuthenticationFallbackWebView] Logged in on homeserver: %@", loginResponse.homeserver);
                
                // Sanity check
                if (loginResponse.homeserver.length && loginResponse.userId.length && loginResponse.accessToken.length)
                {
                    MXLogDebug(@"[MXKAuthenticationFallbackWebView] Call success block");
                    // And inform the client
                    onSuccess(loginResponse);
                }
            }
        }
        
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

@end
