/**
 * @file    PlatformHelper.cs
 * 
 * @author  imsunghoon
 * @date    2017-2022
 * @copyright Copyright © Com2uS Platform Corporation. All Right Reserved.
 * @brief 플랫폼 사용 편의를 위한 헬퍼 기능들의 모음
 *
 */

using UnityEngine;
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using UnityEngine.UI;
using System.Linq;

/**
 * @defgroup PlatformHelper
 * @{
 * \~korean  플랫폼 사용 편의를 위한 기능 제공 <br/><br/>
 * \~english Provides features for ease of platform use <br/><br/>
 */

namespace hive
{
	/**
	 * @brief 플랫폼 사용 편의를 위한 헬퍼 기능들의 모음<br/><br/>
	 * 
	 * @ingroup PlatformHelper
	 * @author imsunghoon
	 */
	public class PlatformHelper {

		/**
		 * @brief Share 관련 동작이 완료되었을 때 호출됨. (share)
		 * 
		 * @param isSuccess Share 결과.
		 * 
		 * @ingroup PlatformHelper
		 */
		public delegate void onShare(ResultAPI result);

		/**
		 * @brief Android에서 재요청된 OS 권한동의에 대한 결과 값을 반환한다.
		 *
		 * @param granted 사용자가 수락한 권한 리스트, denied 사용자가 거부한 권한 리스트
		 *
		 * @ingroup PlatformHelper
		 */
        public delegate void onRequestUserPermissions(ResultAPI result, String[] granted, String[] denied);

		/**
		 * @brief 인 앱 브라우저 콜백
		 *
		 * @param result 인 앱 브라우저 호출 결과
		 *
		 * @ingroup PlatformHelper
		 */
		public delegate void onInAppBrowser(ResultAPI result);

		/**
		 * @brief 인 앱 웹뷰 콜백
		 *
		 * @param result 인 앱 웹뷰 호출 결과
		 *
		 * @ingroup PlatformHelper
		 */
		public delegate void onInAppWebView(ResultAPI result);

        /**
         * @brief 게임 실행 시 사용된 인수 정보를 반환하는 콜백 리스너
         *
         * @param result [ResultAPI] 처리 결과 정보
         * @param parameters [const char*] 게임 실행 시 사용된 인수 정보
         *
         */
        public delegate void onParameters(ResultAPI result, String parameters);

		public static void share(PlatformShare platformShare, onShare listener) {

			JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "share", listener);
			jsonParam.AddField (PlatformShare.SHARE_TYPE, (int)platformShare.getShareType());
			jsonParam.AddField (PlatformShare.DATA_SUBJECT, platformShare.getSubject());
			jsonParam.AddField (PlatformShare.DATA_TEXT, platformShare.getText());

			JSONObject jsonArray = new JSONObject();

			if (platformShare.getMedia() != null) {
				
				foreach(String path in platformShare.getMedia()) {
					jsonArray.Add(path);
				}
			}

			jsonParam.AddField(PlatformShare.DATA_MEDIA, jsonArray);

			HIVEUnityPlugin.callNative (jsonParam);
		}

	   /**
		* @brief Android에서 사용자에게 OS 권한을 재요청.
		*
		* @param requests Android에서 요청할 권한들을 포함한 리스트.
		*
		*/
        public static void requestUserPermissions(List<String> requests, onRequestUserPermissions listener)
        {
            JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "requestUserPermissions", listener);
            JSONObject requestJson = new JSONObject();
            foreach (string name in requests)
            {
                requestJson.Add(name);
            }
            jsonParam.AddField("requests", requestJson);
            HIVEUnityPlugin.callNative(jsonParam);
        }

		/**
		 * @brief 업데이트 팝업 설정으로 백그라운드에서 앱 다운로드가 완료되면
		 *
		 * UE에서 [Promotion.EngagementEventType.APPUPDATE_DOWNLOADED] 로 신호를 보낸다.
		 *
		 * 이후 [completeUpdate] 를 호출하면 [completeState] 값에 따라 새 버전으로 설치한다.
		 *
		 * 호출 하지 않으면 기본 동작으로 재시작시 설치를 진행한다.
		 *
		 * @param completeState 1 ImmediateUpdate, 2 RestartUpdate, 3 LaterUpdate. otherwise 1.
		 */
		public static void completeUpdate(int completeState)
        {
			JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "completeUpdate", null);
			jsonParam.AddField("completeState", completeState);

			HIVEUnityPlugin.callNative(jsonParam);
		}

		/**
		 *  \~korean
		 * @brief 인 앱 브라우저를 오픈한다.
		 *
		 * @param param 인앱 브라우저 설정을 위한 파라미터
		 * @param listener onInAppBrowser 결과 콜백
		 *
		 *  \~english
		 * @brief Open the in-app browser.
		 *
		 * @param param Parameters for in-app browser settings
		 * @param listener onInAppBrowser result callback
		 *
		 *  \~
		 * @ingroup PlatformHelper
		 */
		public static void showInAppBrowser(InAppBrowserParam param, onInAppBrowser listener)
		{
			JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "showInAppBrowser", listener);
			jsonParam.AddField("param", param.toJsonString());

			HIVEUnityPlugin.callNative(jsonParam);
		}

		/**
		 *  \~korean
		 * @brief 인 앱 웹뷰를 오픈한다.
		 *
		 * @param param 인앱 웹뷰 설정을 위한 파라미터
		 * @param listener onInAppWebView 결과 콜백
		 *
		 *  \~english
		 * @brief Open the in-app webview.
		 *
		 * @param param Parameters for in-app webview settings
		 * @param listener onInAppWebView result callback
		 *
		 *  \~
		 * @ingroup PlatformHelper
		 */
		public static void showInAppWebView(InAppWebViewParam param, onInAppWebView listener)
		{
			JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "showInAppWebView", listener);
            jsonParam.AddField("param", param.toJsonString());

            HIVEUnityPlugin.callNative(jsonParam);
		}

        public static void openBrowser(OpenBrowserParam param)
        {
            JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "openBrowser", null);
            jsonParam.AddField("url", param.url);
            jsonParam.AddField("useIncognitoMode", param.useIncognitoMode);

            HIVEUnityPlugin.callNative(jsonParam);
        }

        public static void getLaunchParameters(onParameters listener)
        {
            JSONObject jsonParam = HIVEUnityPlugin.createParam("PlatformHelper", "getLaunchParameters", listener);
            HIVEUnityPlugin.callNative(jsonParam);
        }

        // Native 영역에서 호출된 요청을 처리하기 위한 플러그인 내부 코드
        public static void executeEngine(JSONObject resJsonObject) {

			String methodName = null;
			resJsonObject.GetField (ref methodName, "method");

			int handlerId = -1;
			resJsonObject.GetField (ref handlerId, "handler");
			object handler = (object)HIVEUnityPlugin.popHandler (handlerId);

			if (handler == null) return;

			if ("shareText".Equals(methodName) ||
				"shareMedia".Equals(methodName) ||
				"share".Equals(methodName)) {

				onShare listener = (onShare)handler;
				listener (new ResultAPI (resJsonObject.GetField ("resultAPI")));
			}
            else if ("requestUserPermissions".Equals(methodName))
            {
                onRequestUserPermissions listener = (onRequestUserPermissions)handler;

                JSONObject jsonArrayGranted = resJsonObject.GetField("granted");
				String[] granted = new String[]{};
                if (!jsonArrayGranted.isNull && jsonArrayGranted.count > 0)
                {
					ArrayList grantedList = new ArrayList();
                    List<JSONObject> jsonList = jsonArrayGranted.list;

                    foreach (JSONObject jsonItem in jsonList)
                    {
                        grantedList.Add(jsonItem.ToString());
                    }
					granted = (string[])grantedList.ToArray(typeof(string));
                }

				JSONObject jsonArrayDenied = resJsonObject.GetField("denied");
				String[] denied = new String[]{};
                if (!jsonArrayDenied.isNull && jsonArrayDenied.count > 0)
                {
					ArrayList deniedList = new ArrayList();
                    List<JSONObject> jsonList = jsonArrayDenied.list;

                    foreach (JSONObject jsonItem in jsonList)
                    {
                        deniedList.Add(jsonItem.ToString());
                    }
					denied = (string[])deniedList.ToArray(typeof(string));
                }

                listener(new ResultAPI(resJsonObject.GetField("resultAPI")), granted, denied);
            }

			else if ("showInAppBrowser".Equals(methodName))
			{
				onInAppBrowser listener = (onInAppBrowser)handler;
				listener (new ResultAPI (resJsonObject.GetField ("resultAPI")));
			}

			else if ("showInAppWebView".Equals(methodName))
			{
				onInAppWebView listener = (onInAppWebView)handler;
				listener (new ResultAPI (resJsonObject.GetField ("resultAPI")));
			}
            else if ("getLaunchParameters".Equals(methodName))
            {
                String parameters = "";
                resJsonObject.GetField(ref parameters, "parameters");

                onParameters listener = (onParameters)handler;
                listener(new ResultAPI(resJsonObject.GetField("resultAPI")), parameters);
            }

        }
			
		public static string saveQrcodeToPng(byte[] qrcodestring){

			#if UNITY_EDITOR
			return "";
			#elif (UNITY_IOS || UNITY_ANDROID)
			Texture2D texture = new Texture2D(1, 1);
			texture.LoadImage (qrcodestring);		
			texture.Apply ();

			byte[] bytes = texture.EncodeToPNG();
			string path = Application.persistentDataPath + "/ShareImage.png";
			File.WriteAllBytes (path, bytes);

			return path;
			#else
			return "";
			#endif
		}

		public static byte[] StringToByteArray(string hex) {
			return Enumerable.Range(0, hex.Length)
				.Where(x => x % 2 == 0)
				.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
				.ToArray();
		}
		
	}

	public class PlatformShare {
		
		public enum ShareType : int {

			TEXT = 1, 
			MEDIA = 2
		}

		public const String SHARE_TYPE = "shareType";
		public const String DATA_SUBJECT = "shareSubject";
		public const String DATA_TEXT = "shareText";
		public const String DATA_MEDIA = "shareMedia";

		private ShareType shareType;
		private Dictionary<string, object> shareData = null;

		public PlatformShare() {
			
			if (shareData == null)
				shareData = new Dictionary<string, object>();
		}
		public void setSharetype(ShareType shareType) {

			this.shareType = shareType;
		}

		public ShareType getShareType() {

			return this.shareType;
		}

		public void setSubject(String subject) {

			shareData.Add(DATA_SUBJECT, subject);
		}

		public String getSubject() {

			if (shareData.ContainsKey(DATA_SUBJECT) == true) {
				return shareData[DATA_SUBJECT] as string;
			}
			
			return null;
		}

		public void setText(String text) {

			shareData.Add(DATA_TEXT, text);
		}

		public String getText() {

			if (shareData.ContainsKey(DATA_TEXT) == true) {
				return shareData[DATA_TEXT] as string;
			}

			return null;
		}

		public void setMedia(String[] media) {

			shareData.Add(DATA_MEDIA, media);
		}

		public String[] getMedia() {

			if (shareData.ContainsKey(DATA_MEDIA) == true) {
				return shareData[DATA_MEDIA] as String[];
			}

			return null;
		}
	}

	/**
	 *  \~korean
	 * @brief 인앱 브라우저 설정을 위한 파라미터
	 *
	 * @param url 연결 URL
	 * @param navigationColor 네비게이션 바 색상 (16진수 컬러)
	 * @param buttonColor 버튼 색상 (16진수)(iOS 전용)
	 * @param urlBarHiding 사용자가 스크롤을 내릴 때, URL 주소 창의 가려짐에 대한 설정. 기본 값은 true 이다.
	 * @param autoRedirectToExternalBrowser 인 앱 브라우저가 지원되지 않는 경우, 외부 브라우저 리 다이렉팅 설정. 기본 값은 true 이다.
	 *
	 *  \~english
     * @brief Parameters for in-app browser settings
     *
     * @param url Connection URL
	 * @param navigationColor navigation bar color (hex color)
	 * @param buttonColor button color (hex color)(only for iOS)
     * @param urlBarHiding set whether the url bar should hide as the user scrolls down on the page. default is true.
     * @param autoRedirectToExternalBrowser If the in-app browser is not supported, external browser redirect is automatically supported. default is ture.
	 *
	 *  \~
	 * @ingroup PlatformHelper
     */
	public class InAppBrowserParam
	{
		public readonly String url;
		public readonly String navigationColor;
		public readonly String buttonColor;
		public readonly Boolean urlBarHiding;
		public readonly Boolean autoRedirectToExternalBrowser;

		private InAppBrowserParam (
			String url,
			String navigationColor,
			String buttonColor,
			Boolean urlBarHiding,
			Boolean autoRedirectToExternalBrowser
		) {
			this.url = url;
			this.navigationColor = navigationColor;
			this.buttonColor = buttonColor;
			this.urlBarHiding = urlBarHiding;
			this.autoRedirectToExternalBrowser = autoRedirectToExternalBrowser;
		}

		public String toJsonString() {
			JSONObject jsonObj = new JSONObject();

			jsonObj.AddField("url", url);
			jsonObj.AddField("navigationColor", navigationColor);
			jsonObj.AddField("buttonColor", buttonColor);
			jsonObj.AddField("urlBarHiding", urlBarHiding);
			jsonObj.AddField("autoRedirectToExternalBrowser", autoRedirectToExternalBrowser);

			return jsonObj.ToString();
		}

		public class Builder {
			private String url;
			private String navigationColor;
			private String buttonColor;
			private Boolean urlBarHiding;
			private Boolean autoRedirectToExternalBrowser;
			
			public Builder(String url) {
				this.url = url;
			}

			public Builder setNavigationColor(String color) { this.navigationColor = color; return this; }
			public Builder setButtonColor(String color) { this.buttonColor = color; return this; }
			public Builder setUrlBarHiding(Boolean enable) { this.urlBarHiding = enable; return this; }
			public Builder setAutoRedirectToExternalBrowser(Boolean enable) { this.autoRedirectToExternalBrowser = enable; return this; }

			public InAppBrowserParam build()
			{
				return new InAppBrowserParam(
					url,
					navigationColor,
					buttonColor,
					urlBarHiding,
					autoRedirectToExternalBrowser
				);
			}
		}
	}

	/**
	 *  \~korean
	 * @brief 인앱 웹뷰 설정을 위한 파라미터
	 *
	 * @param url 연결 URL
	 * @param postData 만약 useUserSession 옵션을 사용하는 경우, 이것은 반드시 json 형식이어야 한다.
	 * @param useUserSession 만약 true로 설정한 경우, player_id 와 player_token 은 json으로 post 데이터에 포함된다. 기본 값은 false 이다.
	 * @param useGameWindow 안드로이드에서만 동작한다. rue로 설정한 경우, 현재 window에 웹뷰를 노출한다.
     * pause 상태가 되지 않으며 HiveTheme와 HiveOrientation이 무시된다. 기본값은 false 이다.
	 *
	 *  \~english
     * @brief Parameters for in-app webview settings
     *
     * @param url Connection URL
     * @param postData If you use useUserSession, it must be in json format.
	 * @param useUserSession If true, player_id and player_token are included in postData as json. Default value is false.
	 * @param useGameWindow Android only. If true, Displays the WebView in the current window.
     * It does not pause, and HiveTheme and HiveOrientation settings are ignored. Default value is false.
	 *
	 *  \~
	 * @ingroup PlatformHelper
     */
	public class InAppWebViewParam
	{
		public readonly String url;
		public readonly String postData;
		public readonly Boolean useUserSession;
		public readonly Boolean useGameWindow;

		private InAppWebViewParam (
			String url,
			String postData,
			Boolean useUserSession,
			Boolean useGameWindow
		) {
			this.url = url;
			this.postData = postData;
			this.useUserSession = useUserSession;
			this.useGameWindow = useGameWindow;
		}

		public String toJsonString() {
			JSONObject jsonObj = new JSONObject();

			jsonObj.AddField("url", url);
			jsonObj.AddField("postData", postData);
			jsonObj.AddField("useUserSession", useUserSession);
			jsonObj.AddField("useGameWindow", useGameWindow);

			return jsonObj.ToString();
		}

		public class Builder {
			private String url;
			private String postData;
			private Boolean useUserSession;
			private Boolean useGameWindow;
			
			public Builder(String url) {
				this.url = url;
			}

			public Builder setPostData(String postData) { this.postData = postData; return this; }
			public Builder setUseUserSession(Boolean enable) { this.useUserSession = enable; return this; }
			public Builder setUseGameWindow(Boolean enable) { this.useGameWindow = enable; return this; }
			
			public InAppWebViewParam build()
			{
				return new InAppWebViewParam(
					url,
					postData,
					useUserSession,
					useGameWindow
				);
			}
		}
	}

    /**
     * @brief Parameters for openBrowser settings
     *
     * @param url Connection URL
     * @param useIncognito If true, the browser will open in incognito mode. Default value is false.
     */
    public class OpenBrowserParam
    {
        public readonly String url;
        public readonly Boolean useIncognitoMode;

        private OpenBrowserParam(
            String url,
            Boolean useIncognitoMode
        )
        {
            this.url = url;
            this.useIncognitoMode = useIncognitoMode;
        }

        public String toJsonString()
        {
            JSONObject jsonObj = new JSONObject();

            jsonObj.AddField("url", url);
            jsonObj.AddField("useIncognitoMode", useIncognitoMode);

            return jsonObj.ToString();
        }

        public JSONObject toJsonObject()
        {
            JSONObject jsonObj = new JSONObject();

            jsonObj.AddField("url", url);
            jsonObj.AddField("useIncognitoMode", useIncognitoMode);

            return jsonObj;
        }

        public class Builder
        {
            private String url;
            private Boolean useIncognitoMode;

            public Builder(String url)
            {
                this.url = url;
            }

            public Builder setUseIncognitoMode(Boolean enable) { this.useIncognitoMode = enable; return this; }

            public OpenBrowserParam build()
            {
                return new OpenBrowserParam(
                    url,
                    useIncognitoMode
                );
            }
        }
    }
}


/** @} */