//
//  UniWebView.cs
//  Created by Wang Wei (@onevcat) on 2017-04-11.
//
//  This file is a part of UniWebView Project (https://uniwebview.com)
//  By purchasing the asset, you are allowed to use this code in as many as projects 
//  you want, only if you publish the final products under the name of the same account
//  used for the purchase. 
//
//  This asset and all corresponding files (such as source code) are provided on an 
//  “as is” basis, without warranty of any kind, express of 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 action of contract, tort or otherwise, 
//  arising from, out of or in connection with the software or the use of other dealing in the software.
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
/// 
/// Main class of UniWebView. Any `GameObject` instance with this script can represent a webview object in the scene. 
/// Use this class to create, load, show and interact with a general-purpose web view.
/// 
public class UniWebView: MonoBehaviour {
    /// 
    /// Delegate for page started event.
    /// 
    /// The web view component which raises this event.
    /// The url which the web view is about to load.
    public delegate void PageStartedDelegate(UniWebView webView, string url);
    
    /// 
    /// Raised when the web view starts loading a url.
    /// 
    /// This event will be invoked for both url loading with `Load` method or by a link navigating from page.
    /// 
    public event PageStartedDelegate OnPageStarted;
    /// 
    /// Delegate for page finished event.
    /// 
    /// The web view component which raises this event.
    /// HTTP status code received from response.
    /// The url which the web view loaded.
    public delegate void PageFinishedDelegate(UniWebView webView, int statusCode, string url);
    /// 
    /// Raised when the web view finished to load a url successfully.
    /// 
    /// This method will be invoked when a valid response received from the url, regardless of the response status.
    /// If a url loading fails before reaching to the server and getting a response, `OnPageErrorReceived` will be 
    /// raised instead.
    /// 
    public event PageFinishedDelegate OnPageFinished;
    /// 
    /// Delegate for page error received event.
    /// 
    /// The web view component which raises this event.
    /// 
    /// The error code which indicates the error type. 
    /// It can be different from systems and platforms.
    /// 
    /// The error message which indicates the error.
    public delegate void PageErrorReceivedDelegate(UniWebView webView, int errorCode, string errorMessage);
    /// 
    /// Raised when an error encountered during the loading process. 
    /// Such as the "host not found" error or "no Internet connection" error will raise this event.
    /// 
    public event PageErrorReceivedDelegate OnPageErrorReceived;
    /// 
    /// Delegate for page progress changed event.
    /// 
    /// The web view component which raises this event.
    /// A value indicates the loading progress of current page. It is a value between 0.0f and 1.0f.
    public delegate void PageProgressChangedDelegate(UniWebView webView, float progress);
    /// 
    /// Raised when the loading progress value changes in current web view.
    /// 
    public event PageProgressChangedDelegate OnPageProgressChanged;
    /// 
    /// Delegate for message received event.
    /// 
    /// The web view component which raises this event.
    /// Message received from web view.
    public delegate void MessageReceivedDelegate(UniWebView webView, UniWebViewMessage message);
    /// 
    /// Raised when a message from web view is received. 
    /// 
    /// Generally, the message comes from a navigation to 
    /// a scheme which is observed by current web view. You could use `AddUrlScheme` and 
    /// `RemoveUrlScheme` to manipulate the scheme list.
    /// 
    /// "uniwebview://" scheme is default in the list, so a clicking on link starting with "uniwebview://"
    /// will raise this event, if it is not removed.
    /// 
    public event MessageReceivedDelegate OnMessageReceived;
    /// 
    /// Delegate for should close event.
    /// 
    /// The web view component which raises this event.
    /// Whether the web view should be closed and destroyed.
    public delegate bool ShouldCloseDelegate(UniWebView webView);
    /// 
    /// Raised when the web view is about to close itself.
    /// 
    /// This event is raised when the users close the web view by Back button on Android, Done button on iOS,
    /// or Close button on Unity Editor. It gives a chance to make final decision whether the web view should 
    /// be closed and destroyed. You can also clean all related resources you created (such as a reference to
    /// the web view) in this event.
    /// 
    public event ShouldCloseDelegate OnShouldClose;
    /// 
    /// Delegate for orientation changed event.
    /// 
    /// The web view component which raises this event.
    /// The screen orientation for current state.
    public delegate void OrientationChangedDelegate(UniWebView webView, ScreenOrientation orientation);
    /// 
    /// Raised when the screen orientation is changed. It is a good time to set the web view frame if you 
    /// need to support multiple orientations in your game.
    /// 
    public event OrientationChangedDelegate OnOrientationChanged;
    /// 
    /// Delegate for content loading terminated event.
    /// 
    /// The web view component which raises this event.
    public delegate void OnWebContentProcessTerminatedDelegate(UniWebView webView);
    /// 
    /// Raised when on iOS, when system calls `webViewWebContentProcessDidTerminate` method. 
    /// It is usually due to a low memory when loading the web content and leave you a blank white screen. 
    /// You need to free as much as memory you could and then do a page reload.
    /// 
    public event OnWebContentProcessTerminatedDelegate OnWebContentProcessTerminated;
    /// 
    /// Delegate for file download task starting event.
    /// 
    /// The web view component which raises this event.
    /// The remote URL of this download task. This is also the download URL for the task.
    /// The file name which user chooses to use.
    public delegate void FileDownloadStarted(UniWebView webView, string remoteUrl, string fileName);
    /// 
    /// Raised when a file download task starts.
    /// 
    public event FileDownloadStarted OnFileDownloadStarted;
    /// 
    /// Delegate for file download task finishing event.
    /// 
    /// The web view component which raises this event.
    /// 
    /// The error code of the download task result. Value `0` means the download finishes without a problem. 
    /// Any other non-`0` value indicates an issue. The detail meaning of the error code depends on system. 
    /// On iOS, it is usually the `errorCode` of the received `NSURLError`. On Android, the value usually represents
    /// an `ERROR_*` value in `DownloadManager`.
    /// 
    /// The remote URL of this download task.
    /// 
    /// The file path of the downloaded file. On iOS, the downloader file is in a temporary folder of your app sandbox.
    /// On Android, it is in the "Download" folder of your app.
    /// 
    public delegate void FileDownloadFinished(UniWebView webView, int errorCode, string remoteUrl, string diskPath);
    /// 
    /// Raised when a file download task finishes with either an error or success.
    /// 
    public event FileDownloadFinished OnFileDownloadFinished;
    /// 
    /// Delegate for capturing snapshot finished event.
    /// 
    /// The web view component which raises this event.
    /// 
    /// The error code of the event. If the snapshot is captured and stored without a problem, the error code is 0.
    /// Any other number indicates an error happened. In most cases, the screenshot capturing only fails due to lack
    /// of disk storage.
    /// 
    /// 
    /// An accessible disk path to the captured snapshot image. If an error happens, it is an empty string.
    /// 
    public delegate void CaptureSnapshotFinished(UniWebView webView, int errorCode, string diskPath);
    /// 
    /// Raised when an image captured and stored in a cache path on disk.
    /// 
    public event CaptureSnapshotFinished OnCaptureSnapshotFinished;
    /// 
    /// Delegate for multiple window opening event.
    /// 
    /// The web view component which opens the new multiple (pop-up) window.
    /// The identifier of the opened new window.
    public delegate void MultipleWindowOpenedDelegate(UniWebView webView, string multipleWindowId);
    /// 
    /// Raised when a new window is opened. This happens when you enable the `SetSupportMultipleWindows` and open a
    /// new pop-up window.
    /// 
    public event MultipleWindowOpenedDelegate OnMultipleWindowOpened;
    /// 
    /// Delegate for multiple window closing event.
    /// 
    /// The web view component which closes the multiple window.
    /// The identifier of the closed window.
    public delegate void MultipleWindowClosedDelegate(UniWebView webView, string multipleWindowId);
    /// 
    /// Raised when the multiple window is closed. This happens when the pop-up window is closed by navigation operation
    /// or by a invocation of `close()` on the page.
    /// 
    public event MultipleWindowClosedDelegate OnMultipleWindowClosed;
    private string id = Guid.NewGuid().ToString();
    private UniWebViewNativeListener listener;
    
    /// 
    /// Represents the embedded toolbar in the current web view.
    /// 
    public UniWebViewEmbeddedToolbar EmbeddedToolbar { get; private set; }
    private bool isPortrait;
    [SerializeField]
    #pragma warning disable 0649 
    private string urlOnStart;
    [SerializeField]
    private bool showOnStart = false;
    [SerializeField]
    private bool fullScreen;
    [Obsolete("Use Toolbar is deprecated. Use the embedded toolbar instead.", false)]
    [SerializeField]
    private bool useToolbar;
    [Obsolete("Use Toolbar is deprecated. Use the embedded toolbar instead.", false)]
    [SerializeField]
    private UniWebViewToolbarPosition toolbarPosition;
    [SerializeField]
    private bool useEmbeddedToolbar;
    
    [SerializeField]
    private UniWebViewToolbarPosition embeddedToolbarPosition;
    #pragma warning restore 0649
    // Action callback holders
    private Dictionary actions = new Dictionary();
    private Dictionary> payloadActions = new Dictionary>();
    [SerializeField]
    private Rect frame;
    /// 
    /// Gets or sets the frame of current web view. The value is based on current `Screen.width` and `Screen.height`.
    /// The first two values of `Rect` is `x` and `y` position and the followed two `width` and `height`.
    /// 
    public Rect Frame {
        get { return frame; }
        set {
            frame = value;
            UpdateFrame();
        }
    }
    [SerializeField]
    private RectTransform referenceRectTransform;
    /// 
    /// A reference rect transform which the web view should change its position and size to.
    /// Set it to a Unity UI element (which contains a `RectTransform`) under a canvas to determine 
    /// the web view frame by a certain UI element. 
    /// 
    /// By using this, you could get benefit from [Multiple Resolutions UI](https://docs.unity3d.com/Manual/HOWTO-UIMultiResolution.html).
    /// 
    /// 
    public RectTransform ReferenceRectTransform {
        get {
            return referenceRectTransform;
        }
        set {
            referenceRectTransform = value;
            UpdateFrame();
        }
    }
    private bool started;
    private bool backButtonEnabled = true;
    /// 
    /// The url of current loaded web page.
    /// 
    public string Url {
        get { return UniWebViewInterface.GetUrl(listener.Name); } 
    }
    /// 
    /// Updates and sets current frame of web view to match the setting.
    /// 
    /// This is useful if the `referenceRectTransform` is changed and you need to sync the frame change
    /// to the web view. This method follows the frame determining rules.
    /// 
    public void UpdateFrame() {
        Rect rect = NextFrameRect();
        UniWebViewInterface.SetFrame(listener.Name, (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
    }
    Rect NextFrameRect() {
        if (referenceRectTransform == null) {
            UniWebViewLogger.Instance.Info("Using Frame setting to determine web view frame.");
            return frame;
        } else {
            UniWebViewLogger.Instance.Info("Using reference RectTransform to determine web view frame.");
            var worldCorners = new Vector3[4];
            
            referenceRectTransform.GetWorldCorners(worldCorners);
            
            var bottomLeft = worldCorners[0];
            var topLeft = worldCorners[1];
            var topRight = worldCorners[2];
            var bottomRight = worldCorners[3];
            var canvas = referenceRectTransform.GetComponentInParent