using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using EARTHLib;
using TUIO;
namespace google_earth_test1
{
    public partial class Form1 : Form, TuioListener
    {
        int viewHeight, viewWidth;
        bool isReady = false;                       //googleearth是否准备好

        ArrayList buttonIfoList = new ArrayList();  //按钮
        ArrayList buttons = new ArrayList();
        private double GRAD_PI = 180.0 / Math.PI;


        //初始camera位置
        private CameraInfoGE curCameraInfo = new CameraInfoGE();
        private IntPtr hGE;
        //实时更新此camera位置
        private CameraInfoGE camera = new CameraInfoGE();

        private TuioClient client;
        private Dictionary<long, TuioCursor> cursorList = new Dictionary<long, TuioCursor>(32);
        private object cursorSync = new object();
        private TuioCursor blob1, blob2, blob3, blob4, blob5;
        private List<long> aliveObjectList = new List<long>(32);

        private int indexpath1 = -1, indexpath2 = -1, indexpath3 = -1, indexpath4 = -1, indexpath5 = -1;     //当确定两个基准点时，此时的path位置
       
        private Thread newThread;
        private bool threadflag = false;
        private bool threadexit = false;
        private bool cameraChangeflag = false;
        //private bool flag = false;
        private bool angleChange = true;

        ArrayList labelList = new ArrayList();
        bool IsMeasure; //测距模式
        bool IsMsgUpdata = false;   //是否有消息
        string state = "none";

        static readonly Int32 WM_QUIT = 0x0012;

        struct bts                                      //设置按钮的结构体
        {
            public bts(string name, string locality, string audio, double speed)
            {
                Name = name;
                Locality = locality;
                Audio = audio;
                Speed = speed;
            }
            public string Name;
            public string Locality;
            public string Audio;
            public double Speed;
        }



        public Form1()
        {
            this.Width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
            this.Height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;

            loadXml(System.Windows.Forms.Application.StartupPath + "\\config.xml");
            InitializeComponent();
            
            this.SetVisibleCore(false);
            this.FormBorderStyle = FormBorderStyle.None;
            this.WindowState = FormWindowState.Maximized;
            this.SetVisibleCore(true);

            IsMeasure = false;
            newThread = new Thread(DoWork);
            newThread.Start();

        }
        /**
        * This callback method is invoked by the TuioClient when a new TuioObject is added to the session.
        *
        * @param  tobj  the TuioObject reference associated to the addTuioObject event
        */
        public void addTuioObject(TuioObject tobj)
        {
        }
        /**
         * This callback method is invoked by the TuioClient when an existing TuioObject is updated during the session.
         *
         * @param  tobj  the TuioObject reference associated to the updateTuioObject event
         */
        public void updateTuioObject(TuioObject tobj)
        {
        }

        /**
         * This callback method is invoked by the TuioClient when an existing TuioObject is removed from the session.
         *
         * @param  tobj  the TuioObject reference associated to the removeTuioObject event
         */
        public void removeTuioObject(TuioObject tobj)
        {
        }

        /**
         * This callback method is invoked by the TuioClient when a new TuioCursor is added to the session.
         *
         * @param  tcur  the TuioCursor reference associated to the addTuioCursor event
         */
        public void addTuioCursor(TuioCursor c)
        {
            lock (cursorSync)
            {
                cursorList.Add(c.getSessionID(), c);
                aliveObjectList.Add(c.getSessionID());
                if (aliveObjectList.Count >= 2 && aliveObjectList.Count != 3)
                {
                    indexpath1 = cursorList[aliveObjectList[aliveObjectList.Count - 2]].getPath().Count - 1;
                    indexpath2 = cursorList[aliveObjectList[aliveObjectList.Count - 1]].getPath().Count - 1;
                    state = "rotate";
                }
                else if (aliveObjectList.Count == 3)
                {
                    indexpath1 = cursorList[aliveObjectList[aliveObjectList.Count - 3]].getPath().Count - 1;
                    indexpath2 = cursorList[aliveObjectList[aliveObjectList.Count - 2]].getPath().Count - 1;
                    indexpath3 = cursorList[aliveObjectList[aliveObjectList.Count - 3]].getPath().Count - 1;
                    indexpath4 = cursorList[aliveObjectList[aliveObjectList.Count - 2]].getPath().Count - 1;
                    indexpath5 = cursorList[aliveObjectList[aliveObjectList.Count - 1]].getPath().Count - 1;
                    state = "rotate";
                }


                resetCamera();
            }
        }

        /**
         * This callback method is invoked by the TuioClient when an existing TuioCursor is updated during the session.
         *
         * @param  tcur  the TuioCursor reference associated to the updateTuioCursor event
         */
        public void updateTuioCursor(TuioCursor tcur)
        {
        }

        /**
         * This callback method is invoked by the TuioClient when an existing TuioCursor is removed from the session.
         *
         * @param  tcur  the TuioCursor reference associated to the removeTuioCursor event
         */
        public void removeTuioCursor(TuioCursor c)
        {
            lock (cursorSync)
            {
                /*if (aliveObjectList.Count == 3)
                {
                    flag = true;
                }*/
                cursorList.Remove(c.getSessionID());
                aliveObjectList.Remove(c.getSessionID());
                resetCamera();
            }
        }

        public void refresh(TuioTime frameTime)
        {
            Invalidate();
        }

        private void loadXml(string path)
        {
            XmlDocument configXml = new XmlDocument();
            try
            {
                configXml.Load(path);
            }
            catch (Exception e)
            {
                MessageBox.Show("Xml文件加载错误！自动加载默认配置文件");
                configXml.Load(System.Windows.Forms.Application.StartupPath + "\\config_backup.xml");
                //configXml.Load("config_backup.xml");
            }
            XmlNode eleNode = configXml.SelectSingleNode("buttons");

            XmlNodeList eleNodes = eleNode.SelectNodes("button");
            int buttonCount = int.Parse(((XmlElement)eleNode).GetAttribute("count"));

            for (int i = 0; i < buttonCount && i < 10; i++)
            {
                bts bts1 = new bts();
                XmlNode eleNode1 = eleNodes[i];
                bts1.Name = ((XmlElement)eleNode1).GetAttribute("name");
                //bts1.Name = eleNode1["name"].ToString() ;
                bts1.Locality = ((XmlElement)eleNode1).GetAttribute("locality");
                bts1.Audio = ((XmlElement)eleNode1).GetAttribute("audio");
                if (((XmlElement)eleNode1).GetAttribute("speed") != "")
                    bts1.Speed = double.Parse(((XmlElement)eleNode1).GetAttribute("speed"));
                buttonIfoList.Add(bts1);
                Button City = new Button();

                City.Size = new Size(50, 50);

                this.Controls.Add(City);
                City.Left = Width - 80;
                City.Top = i * 60 + 150 + 30;
                City.Text = bts1.Name;
                City.Click += new System.EventHandler(this.buttons_Click);

            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            GE.Width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width + 20;
            GE.Height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height + 70;

            viewHeight = Height;              //减去上下边缘
            viewWidth = Width;               //减去右边缘
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            GE.Width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width + 20;
            GE.Height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height + 70;
            viewHeight = Height;            //减去上下边缘
            viewWidth = Width;             //减去右边缘
        }
        /// <summary>
        /// 按钮的单击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttons_Click(object sender, EventArgs e)
        {
            if (((Button)sender).Text == "退出")
            {
                threadflag = false;
                threadexit = true;

                //当Google Earth 正在运算的时候（比如正在搜索某个地点的），PostMessage有可能结束不了Google Earth
                try
                {
                    
                    client.removeTuioListener(this);
                    client.disconnect();
                    RunCmd("taskkill /im googleearth.exe /f");
                    Environment.Exit(0);
                }
                catch (Exception ex)
                {
                }
                
                return;
            }
            if (((Button)sender).Text == "倾斜75度")
            {
                try
                {
                    if (angleChange)
                    {
                        CameraInfoGE cameraInfo = GE.ApplicationGE.GetCamera(0);
                        cameraInfo.Tilt = 75;
                        GE.ApplicationGE.SetCamera(cameraInfo, 4.99);
                        angleChange = false;
                    }
                    else
                    {
                        CameraInfoGE cameraInfo = GE.ApplicationGE.GetCamera(0);
                        cameraInfo.Tilt = 0;
                        GE.ApplicationGE.SetCamera(cameraInfo, 4.99);
                        angleChange = true;
                    }
                }
                catch (Exception ex)
                {
                }
                return;
            }
            if (((Button)sender).Text == "最小化")
            {
                this.WindowState = FormWindowState.Minimized;
                return;
            }
            IntPtr hWindow = WinApi.FindWindow(null, "OSCCORE");
            if (((Button)sender).Text == "放弃测距")
            {

                IsMeasure = false;
                ((Button)sender).Text = "测距";
                return;
            }
            if (((Button)sender).Text == "测距")
            {

                IsMeasure = true;
                ((Button)sender).Text = "放弃测距";
                camera = GE.ApplicationGE.GetCamera(0);

                System.Threading.Thread.Sleep(100);

                return;
            }

            for (int i = 0; i < buttonIfoList.Count && i < 10; i++)
            {
                if (((bts)buttonIfoList[i]).Name == ((Button)sender).Text)
                {
                    // WinApi.PlaySound(((bts)buttonIfoList[i]).Audio, IntPtr.Zero, WinApi.PlaySoundFlags.SND_ASYNC);
                    GE.setValue("speed", ((bts)buttonIfoList[i]).Speed);
                    GE.ApplicationGE.SearchController.Search(((bts)buttonIfoList[i]).Locality);
                    return;
                }
            }
        }
        
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!isReady)
            {
                try
                {
                    GE.ApplicationGE.GetCamera(1);
                    hGE = (IntPtr)GE.ApplicationGE.GetRenderHwnd();
                }
                catch (Exception ex)
                {
                    isReady = false;
                    return;
                }
                isReady = true;
                timer3.Enabled = true;
                timer1.Enabled = false;
                threadflag = true;
                TUIO_ini(3333);
                //---设定初始值----------------------------------------------
                camera = GE.ApplicationGE.GetCamera(1);
                camera.Range = 20000000;
                camera.FocusPointLatitude = 34.904491;
                camera.FocusPointLongitude = 106.391466;
                camera.Azimuth = 0;
                camera.Tilt = 0;
                cameraChangeflag = true;
            }
        }

        //计算两点距离
        //x1:A点经度
        //y1:A点北纬
        //x2:B点经度
        //y2:B点北纬
        double TwoPointDistance(double x1, double y1, double x2, double y2)
        {
            if (x1 == x2 && y1 == y2)
            {
                return 0;
            }
            double earthR = 6371004;
            //D= arc cos((sin北纬A×sin北纬B)＋(cos北纬A×cos北纬B×cosAB两地经度差))×地球平均半径
            return Math.Acos(
                        (Math.Sin(y1 * Math.PI / 180) * Math.Sin(y2 * Math.PI / 180))
                        + (Math.Cos(y1 * Math.PI / 180) * Math.Cos(y2 * Math.PI / 180) * Math.Cos((x2 - x1) * Math.PI / 180))
                        ) * earthR;
        }

        //-------------------定时刷新---------------------------------------
        private void timer3_Tick(object sender, EventArgs e)
        {
            //GE.ApplicationGE.GetPointOnTerrainFromScreenCoords
            if (state == "drag")
            {


            }
            try
            {
                lock (cursorSync)
                {
                    if (aliveObjectList.Count >= 2 && aliveObjectList.Count != 3)
                    {
                        /*
                        if (flag)
                        {
                            flag = false;
                            return;
                        }*/
                        blob1 = cursorList[aliveObjectList[aliveObjectList.Count - 2]];
                        blob2 = cursorList[aliveObjectList[aliveObjectList.Count - 1]];
                        
                        double tempdang, tempdzoom;
                        tempdang = getAngleTrig(blob2.getX() - blob1.getX(), blob2.getY() - blob1.getY()) - getAngleTrig(blob2.getPath()[indexpath2].getX() - blob1.getPath()[indexpath1].getX(), blob2.getPath()[indexpath2].getY() - blob1.getPath()[indexpath1].getY());
                        tempdzoom = Math.Sqrt((
                                             (blob2.getX() - blob1.getX()) * (blob2.getX() - blob1.getX())
                                            + (blob2.getY() - blob1.getY()) * (blob2.getY() - blob1.getY())
                                            ) / 1.0 /
                                             ((blob2.getPath()[indexpath2].getX() - blob1.getPath()[indexpath1].getX()) * (blob2.getPath()[indexpath2].getX() - blob1.getPath()[indexpath1].getX())
                                            + (blob2.getPath()[indexpath2].getY() - blob1.getPath()[indexpath1].getY()) * (blob2.getPath()[indexpath2].getY() - blob1.getPath()[indexpath1].getY())
                                            ));

                        camera.Range = curCameraInfo.Range / tempdzoom;
                        camera.Azimuth = curCameraInfo.Azimuth - tempdang;

                        if (camera.Azimuth > 180) camera.Azimuth = camera.Azimuth - 360;
                        if (camera.Azimuth < -180) camera.Azimuth = camera.Azimuth + 360;
                        if (camera.Range >= 100000000) camera.Range = 100000000;
                        if (camera.Range <= 0) camera.Range = 0;
                        if (camera.Tilt < 0.0001) camera.Tilt = 0;
                        if (camera.Tilt > 89) camera.Tilt = 89;

                        cameraChangeflag = true;
                    }
                    else if (aliveObjectList.Count == 3)
                    {
                        blob3 = cursorList[aliveObjectList[aliveObjectList.Count - 3]];
                        blob4 = cursorList[aliveObjectList[aliveObjectList.Count - 2]];
                        blob5 = cursorList[aliveObjectList[aliveObjectList.Count - 1]];
                        double barycenter1X, barycenter1Y, barycenter2X, barycenter2Y;
                        barycenter1X = (blob3.getX() + blob4.getX() + blob5.getX()) / 3;
                        barycenter1Y = (blob3.getY() + blob4.getY() + blob5.getY()) / 3;
                        barycenter2X = (blob3.getPath()[indexpath3].getX() + blob4.getPath()[indexpath4].getX() + blob5.getPath()[indexpath5].getX()) / 3;
                        barycenter2Y = (blob3.getPath()[indexpath3].getY() + blob4.getPath()[indexpath4].getY() + blob5.getPath()[indexpath5].getY()) / 3;

                        if (barycenter1Y >= barycenter2Y)
                        {
                            camera.Tilt = curCameraInfo.Tilt - (Math.Sqrt(Math.Abs((barycenter1X - barycenter2X) * (barycenter1Y - barycenter2Y)))) * 1500;
                        }
                        else
                        {
                            camera.Tilt = curCameraInfo.Tilt + (Math.Sqrt(Math.Abs((barycenter1X - barycenter2X) * (barycenter1Y - barycenter2Y)))) * 1500;
                        }
                        if (camera.Tilt > 89)
                        {
                            camera.Tilt = 89;
                        }
                        else if (camera.Tilt < 0.0001)
                        {
                            camera.Tilt = 0;
                        }
                        cameraChangeflag = true;
                    }
                }
            }
            catch (Exception ex)
            {
            }
        }
        private void resetCamera()
        {
            curCameraInfo = GE.ApplicationGE.GetCamera(0);
            camera.Azimuth = curCameraInfo.Azimuth;
            camera.Range = curCameraInfo.Range;
            camera.Tilt = curCameraInfo.Tilt;
            camera.FocusPointLatitude = curCameraInfo.FocusPointLatitude;
            camera.FocusPointLongitude = curCameraInfo.FocusPointLongitude;
            camera.FocusPointAltitude = curCameraInfo.FocusPointAltitude;
        }
        private double getAngleTrig(double X, double Y)
        {
            if (X == 0.0)
            {
                if (Y < 0.0)
                    return 270;
                else
                    return 90;
            }
            else if (Y == 0)
            {
                if (X < 0)
                    return 180;
                else
                    return 0;
            }
            if (Y > 0.0)
                if (X > 0.0)
                    return Math.Atan(Y / X) * GRAD_PI;
                else
                    return 180.0 - Math.Atan(Y / -X) * GRAD_PI;
            else
                if (X > 0.0)
                    return 360.0 - Math.Atan(-Y / X) * GRAD_PI;
                else
                    return 180.0 + Math.Atan(-Y / -X) * GRAD_PI;
        }
        
        public void DoWork()
        {
            Thread.Sleep(2000);
            while (!threadexit)
            {
                if (threadflag)
                {
                    if (cameraChangeflag)
                    {
                        camera.FocusPointAltitudeMode = AltitudeModeGE.AbsoluteAltitudeGE;
                        GE.ApplicationGE.SetCamera(camera, 4.5);
                        Trace.Write(" ");
                        {
                            cameraChangeflag = false;
                        }
                    }
                    Thread.Sleep(5);
                }
            }
        }
        private void TUIO_ini(int port)
        {
            client = new TuioClient(port);
            client.addTuioListener(this);
            client.connect();
            if (!client.isConnected())
            {
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            threadexit = true;
            threadflag = false;
            

            try
            {
                client.removeTuioListener(this);
                client.disconnect();

                RunCmd("taskkill /im googleearth.exe /f");
                Environment.Exit(0);
            }
            catch (Exception ex)
            {
            }

        }

        /// 运行DOS命令   
        /// DOS关闭进程命令(ntsd -c q -p PID )PID为进程的ID   
        /// </summary>   
        /// <param name="command"></param>   
        /// <returns></returns>   
        public static string RunCmd(string command)
        {
            //實例一個Process類，啟動一個獨立進程   
            System.Diagnostics.Process p = new System.Diagnostics.Process();

            //Process類有一個StartInfo屬性，這個是ProcessStartInfo類，包括了一些屬性和方法，下面我們用到了他的幾個屬性：   

            p.StartInfo.FileName = "cmd.exe";           //設定程序名   
            p.StartInfo.Arguments = "/c " + command;    //設定程式執行參數   
            p.StartInfo.UseShellExecute = false;        //關閉Shell的使用   
            p.StartInfo.RedirectStandardInput = true;   //重定向標準輸入   
            p.StartInfo.RedirectStandardOutput = true;  //重定向標準輸出   
            p.StartInfo.RedirectStandardError = true;   //重定向錯誤輸出   
            p.StartInfo.CreateNoWindow = true;          //設置不顯示窗口   

            p.Start();   //啟動   

            //p.StandardInput.WriteLine(command);       //也可以用這種方式輸入要執行的命令   
            //p.StandardInput.WriteLine("exit");        //不過要記得加上Exit要不然下一行程式執行的時候會當機   

            return p.StandardOutput.ReadToEnd();        //從輸出流取得命令執行結果   

        }
       
    }
}