package com.mobify.astro.plugins.webviewplugin;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Build;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.mobify.astro.BuildConfig;

public class AstroWebViewClient extends WebViewClient {
    private static final String TAG = AstroWebViewPlugin.class.getName();

    // Investigate changing the url type to URL instead of String
    // https://developer.android.com/reference/java/net/URL.html
    interface WebClientListener {
        void webClientPageStarted(WebView view, String url);
        void webClientPageFinished(WebView view, String url);
        void webClientPageFailed(WebView view, String url);
        boolean webClientNavigate(WebView view, String url, boolean isCurrentlyLoading);
    }

    private WebClientListener webClientListener;

    protected boolean isCurrentlyLoading = false;
    protected boolean ignoreNextPageLifecycle = false; // Only ever set to true for Android versions below 5

    public AstroWebViewClient(WebClientListener webClientListener) {
        super();
        this.webClientListener = webClientListener;
    }

    public void ignoreNextPageLifecycle() {
        // Chrome 63 introduced a change that caused
        // onPageStarted and onPageFinished lifecycle functions to be called only once
        // We want to make sure not to ignore them
        // On Android versions below 5, the version of Chrome in the webview is locked to 30
        // so support ignoring them there
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            ignoreNextPageLifecycle = true;
        }
    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);

        // Ignores an entire page lifecycle.
        if (ignoreNextPageLifecycle) {
            return;
        }

        // Ignore excessive calls.
        if (isCurrentlyLoading) {
            return;
        }
        isCurrentlyLoading = true;

        webClientListener.webClientPageStarted(view, url);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);

        // Ignores an entire page lifecycle.
        if (ignoreNextPageLifecycle) {
            ignoreNextPageLifecycle = false;
            return;
        }

        // Ignore excessive calls.
        if (!isCurrentlyLoading) {
            return;
        }
        isCurrentlyLoading = false;

        webClientListener.webClientPageFinished(view, url);
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return webClientListener.webClientNavigate(view, url, isCurrentlyLoading);
    }

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        if (BuildConfig.DEBUG) {
            handler.proceed();
        } else {
            super.onReceivedSslError(view, handler, error);
        }
    }

    private void onReceivedErrorHandler(WebView view, String url) {
        webClientListener.webClientPageFailed(view, url);

        // Prevent onPageFinished after error was triggered
        isCurrentlyLoading = false;
    }

    @SuppressWarnings("deprecation")
    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        // onReceivedError is called only for host resource in deprecated API
        onReceivedErrorHandler(view, failingUrl);

        super.onReceivedError(view, errorCode, description, failingUrl);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        // onReceivedError is called for all resources in newer API, ensure only the main resource is handled
        if (request.isForMainFrame()) {
            onReceivedErrorHandler(view, request.getUrl().toString());
        }

        super.onReceivedError(view, request, error);
    }
}
