using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;

using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using CommonAIEditor;
using CommonAI.Zone.ZoneEditor.Plugin.EditorToScene;
using CommonAI.Zone.ZoneEditor.Plugin;
using CommonAI.Zone.ZoneEditor.Plugin.SceneToEditor;
using System.IO;
using System.Runtime.InteropServices;
using CommonLang.File;
using GameEditorPlugin.Utils;
using CommonAI.Zone;
using CommonLang.Property;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Threading;
using CommonFroms;
using GameEditorPlugin.Win32.Runtime;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Serialization.Formatters;
using System.Collections;
using System.Runtime.Remoting.Channels.Tcp;
using System.Net.NetworkInformation;
using System.Net;

namespace GameEditorUnity3D
{


    public partial class Unity3dControl : UserControl, ISceneEditorPlugin
    {
        public Unity3dControl()
        {
            InitializeComponent();
        }

        #region IEditorPlugin

        public event PluginMessageHandler OnGetPluginMessage;
        public event CallAddSceneObject CallAddObject;
        public event CallResetSceneObject CallResetObject;

        public Control AsControl()
        {
            return this;
        }

        public bool EnableRight { get { return true; } }

        private void Response(object resp)
        {
            this.Invoke(new System.Action(() =>
            {
                if (resp is RspOnObjectSelected)
                {
                    RspOnObjectSelected rsp = resp as RspOnObjectSelected;
                    rsp.Selected = true;
                }
                if (OnGetPluginMessage != null)
                {
                    OnGetPluginMessage.Invoke(resp);
                    if (resp is RspEditorState)
                    {
                        if ((resp as RspEditorState).State == RspEditorState.STATE_SUCCEED)
                        {
                            OnEditorStatusOK();
                        }
                    }
                }
            }));
        }

        public void SendMessage(object data)
        {
            string output = EditorMessageDecoder.EncodeMessage(data);

            Send(output);

            return;
        }
        
        public static bool PortInUse(int port)
        {
            bool inUse = false;

            IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();

            foreach (IPEndPoint endPoint in ipEndPoints)
            {
                if (endPoint.Port == port)
                {
                    inUse = true;
                    break;
                }
            }

            return inUse;
        }

        private void OnEditorStatusOK()
        {
            var timer = new System.Timers.Timer(1);
            timer.Elapsed += (sender, e) =>
            {
                timer.Close();
                timer.Dispose();
                this.Invoke(new System.Action(() =>
                {
                    ActivateUnityWindow();
                }));
            };
            timer.Start();
        }

        //------------------------------------------------------------------------------

        #endregion


        #region 组件设计器生成的代码

        private System.ComponentModel.IContainer components = null;

        [DllImport("user32.dll")]
        static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);

        internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
        [DllImport("user32.dll")]
        internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

        [DllImport("user32.dll")]
        internal static extern IntPtr SetFocus(IntPtr hWnd);


        [DllImport("user32.dll")]
        static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        private Process process;
        private IntPtr unityHWND = IntPtr.Zero;

        private const int WM_ACTIVATE = 0x0006;
        private readonly IntPtr WA_ACTIVE = new IntPtr(1);
        private readonly IntPtr WA_INACTIVE = new IntPtr(0);

        public IpcRemoteObject RemoteObject { get; set; }
        private ObjRef RemoteObjectRef;
        private int MessageProxyServerPort = 9900;
        private string MessageProxyServerIP = @"127.0.0.1";
        private string MessageProxyServerName = @"GameEditorUnity3D.Server";
        private string MessageProxyURLName = @"GameEditorUnity3D";
        private string MessageProxyURL;

        /// <summary>
        /// 初始化进程间消息代理
        /// </summary>
        public void InitializeIPCMessageProxy()
        {
            while (PortInUse(MessageProxyServerPort))
            {
                MessageProxyServerPort++;
            }
            MessageProxyURLName += System.Guid.NewGuid();
            MessageProxyURL = string.Format(@"tcp://{0}:{1}/{2}", MessageProxyServerIP, MessageProxyServerPort, MessageProxyURLName);
            BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
            serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
            BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
            IDictionary props = new Hashtable();
            props["name"] = MessageProxyServerName;
            props["port"] = MessageProxyServerPort;
            props["bindTo"] = MessageProxyServerIP;
            TcpChannel channel = new TcpChannel(props, clientProvider, serverProvider);
            ChannelServices.RegisterChannel(channel, false);
            RemoteObject = new IpcRemoteObject();
            RemoteObjectRef = RemotingServices.Marshal(RemoteObject, MessageProxyURLName);
            //RemoteObject = Activator.GetObject(typeof(IpcRemoteObject), MessageProxyURLName) as IpcRemoteObject;
            RemoteObject.ServerCallback = Recv;
        }

        /// <summary>
        /// 释放进程间消息代理
        /// </summary>
        public void DisposeIPCMessageProxy()
        {
            RemotingServices.Unmarshal(RemoteObjectRef, true);
            IChannel[] regChannels = ChannelServices.RegisteredChannels;
            foreach (IChannel item in regChannels)
            {
                if (item.ChannelName == MessageProxyServerName)
                {
                    TcpChannel channel = item as TcpChannel;
                    channel.StopListening(null);
                    ChannelServices.UnregisterChannel(channel);
                    break;
                }
            }
        }

        private void InitializeComponent()
        {
            try
            {
                InitializeIPCMessageProxy();
                System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Unity3dControl));
                this.SuspendLayout();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
            }
            {
                //-----------------------------------------
                try
                {
                    string unitysrc = Application.StartupPath + "\\U3DScene\\U3DEditor.exe";
                    string u3d_webplayer = ConfigurationManager.AppSettings["u3d_webplayer"];
                    if (u3d_webplayer != null)
                    {
                        unitysrc = Application.StartupPath + u3d_webplayer;
                    }
                    process = new Process();
                    process.StartInfo.FileName = unitysrc;
                    process.StartInfo.Arguments = "-parentHWND " + this.Handle.ToInt32() + " -ipcPort " + MessageProxyServerPort + " -serviceName " + MessageProxyURLName + " " + Environment.CommandLine;
                    process.StartInfo.UseShellExecute = true;
                    process.StartInfo.CreateNoWindow = true;
                    Application.ThreadExit += new System.EventHandler(this.App_Exit);
                    process.Start();
                    process.WaitForInputIdle();
                    EnumChildWindows(this.Handle, WindowEnum, IntPtr.Zero);
                    this.Resize += new System.EventHandler(this.Form_Resize);
                    this.VisibleChanged += new System.EventHandler(this.Form_Activated);
                    this.Disposed += new System.EventHandler(this.Form_FormClosed);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
                }
                //-----------------------------------------

            }
            // 
            // Unity3dControl
            // 
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
            //this.Controls.Add(this.axUnityWebPlayer1);
            this.Name = "Unity3dControl";
            this.Size = new System.Drawing.Size(915, 685);
            this.ResumeLayout(false);

        }

        public void ActivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
            SetFocus(unityHWND);
        }

        public void DeactivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
        }

        private int WindowEnum(IntPtr hwnd, IntPtr lparam)
        {
            unityHWND = hwnd;
            ActivateUnityWindow();
            return 0;
        }

        public void Form_Resize(object sender, EventArgs e)
        {
            MoveWindow(unityHWND, 0, 0, this.Width, this.Height, true);
            ActivateUnityWindow();
            //OnGetPluginMessage.Invoke(new RspEditorState());
        }

        // Close Unity application
        public void Form_FormClosed(object sender, EventArgs e)
        {
            try
            {
                process.CloseMainWindow();

                Thread.Sleep(1000);
                while (process.HasExited == false)
                    process.Kill();
            }
            catch (Exception)
            {

            }
        }

        public void App_Exit(object sender, EventArgs e)
        {
            try
            {
                process.Kill();
            }
            catch (Exception)
            {

            }
        }

        public void Form_Activated(object sender, EventArgs e)
        {
            if (this.Visible)
                ActivateUnityWindow();
            else
                DeactivateUnityWindow();
        }

        public void Form_Deactivate(object sender, EventArgs e)
        {
            DeactivateUnityWindow();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            if (disposing)
            {
                DisposeIPCMessageProxy();
            }
            base.Dispose(disposing);
        }


        #endregion 

        #region Unity3dControl

        const string Prefix = "OnExternalCall(\"";
        const string Endfix = "\");";

        string Recv(string s)
        {
            object data = EditorMessageDecoder.DecodeMessage(s);
            Response(data);
            return "";
        }

        void Send(string regionmsg)
        {
            RemoteObject.SendToClient(regionmsg);
        }
        #endregion

    }

    public class U3dGamePlugin : IGameEditorPlugin
    {
        private static string dataRoot;
        private static string pluginDll;

        [DllImport("user32.dll", SetLastError = true)]
        private static extern void SwitchToThisWindow(IntPtr hWnd, bool turnOn);

        private string U3DRuntimeEXE = "/U3DScene/U3DSceneRun.exe";

        public string Name { get { return "Unity"; } }
        public string DataRoot
        {
            get { return U3dGamePlugin.dataRoot; }
            set { U3dGamePlugin.dataRoot = value.Replace('\\', '/'); }
        }
        public string PluginDLL
        {
            get { return U3dGamePlugin.pluginDll; }
            set { U3dGamePlugin.pluginDll = value.Replace('\\', '/'); }
        }

        public U3dGamePlugin()
        {
            string u3d_runtime = ConfigurationManager.AppSettings["u3d_runtime"];
            if (u3d_runtime != null)
            {
                U3DRuntimeEXE = u3d_runtime;
            }
        }

        public ISceneEditorPlugin CreateScenePlugin(IGameEditorPlugin d)
        {
            return new Unity3dControl();
        }

        public void RunTest(IGameEditorPlugin d, string data_dir, int sceneID, int actorTemplateID)
        {
            Type plugintype = ReflectionUtil.GetType(TemplateManager.Factory.GetType().FullName);
            string args = string.Format(
                "-CustomArgs:RunPlatform={0};MapID={1};FolderPath={2};ResPath={3};UserID={4};Plugin={5};IsNet=0;NetDriver=0;IP=0;UUID=0;Force=0;RoomID=0",
                0,
                sceneID,
                dataRoot + "/data",
                dataRoot,
                actorTemplateID,
                PluginDLL
                );
            FileInfo exefile = new FileInfo(Application.StartupPath + U3DRuntimeEXE);
            ProcessStartInfo start = new ProcessStartInfo(exefile.FullName, args);
            start.WorkingDirectory = exefile.Directory.FullName;
            Process p = new Process();
            p.StartInfo = start;
            p.Start();
            new Thread(() =>
            {
                while (!p.HasExited)
                {
                    Thread.Sleep(1000);
                }
                Regex regex = new Regex(@"\d{4}-\d{2}-\d{2}_\d*");
                foreach (DirectoryInfo dir in exefile.Directory.GetDirectories())
                {
                    if (regex.IsMatch(dir.Name))
                    {
                        if (File.Exists(dir.FullName + Path.DirectorySeparatorChar + "crash.dmp"))
                        {
                            FileSystem.DeleteToRecycleBin(dir.FullName);
                            //dir.Delete(true);
                        }
                    }
                }
            }).Start();
        }

        public void RunModelView(IGameEditorPlugin d, string[] filepath)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < filepath.Length; i++)
            {
                sb.Append(filepath[i].Replace('\\', '/'));
                if (i < filepath.Length - 1)
                {
                    sb.Append(",");
                }
            }
            ProcessStartInfo start = new ProcessStartInfo(
                Application.StartupPath + "/U3DScene/U3DSceneRun.exe",
                "-CustomArgs:" +
                "RunPlatform=" + 1 + ";" +
                "ModelPath=" + sb.ToString() +
                "");
            start.WorkingDirectory = Application.StartupPath + "\\U3DScene";
            Process p = new Process();
            p.StartInfo = start;
            p.Start();
        }

        public bool AcceptResource(IGameEditorPlugin d, string filename)
        {
            return filename.ToLower().EndsWith(".assetbundles");
        }

        public void RunLocalPlay(IGameEditorPlugin d, DirectoryInfo data_dir, int sceneID, bool recorder)
        {
            d.RunLocalPlay(null, data_dir, sceneID, recorder);
        }

        public void RunServerPlay(IGameEditorPlugin d, DirectoryInfo data_dir, int sceneID)
        {
            d.RunServerPlay(null, data_dir, sceneID);
        }

    }
}