using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using IronPython;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython.Modules;
using System.Diagnostics;

using System.Security.AccessControl;
using System.Security.Principal;
using System.Xml.Serialization;

namespace share_memory_test
{
    public partial class Form1 : Form
    {

        [Serializable]
        public class WeatherDataInfo
        {
            //[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)]
            public byte[] fileName = new byte[512];

            public uint timeLayerNum;
            public uint latitudeNum;
            public uint longitudeNum;
            public uint startTime;
            public uint intervalTime;
            public uint reserved;

            public double[] geoInfo = new double[4];
        }

        [Serializable]
        public class WindDirectionData
        {
            public string[,] dir_values;
        }

        [Serializable]
        public class WindSpeedData
        {
            public string[,] spd_values;
        }
        protected MemoryMappedFile mmf;
        MemoryMappedViewAccessor mmfWriter;
        MemoryMappedViewAccessor mmfReader;
        MemoryMappedFile mmf2;
        public Form1()
        {
            InitializeComponent();
            string whole_file = System.IO.File.ReadAllText(@"E:\DSME\GFS\NC\gfs_above_msl-u.csv");
            var value_u = Change_array(whole_file);
            Console.WriteLine(value_u[0, 1]);

            whole_file = System.IO.File.ReadAllText(@"E:\DSME\GFS\NC\gfs_above_msl-v.csv");
            var value_v = Change_array(whole_file);
            Console.WriteLine(value_v.GetLength(0));
            Console.WriteLine(value_v.GetLength(1));

            var pie = Math.Acos(-1);
            var speed = math_speed(value_u, value_v);
            WindSpeedData wsd = new WindSpeedData();
            wsd.spd_values = speed;

            var direction = math_direction(value_u, value_v, pie);
            WindDirectionData wdd = new WindDirectionData();
            wdd.dir_values = direction;

            WeatherDataInfo wdi = new WeatherDataInfo();
            wdi.fileName = Encoding.UTF8.GetBytes("direction");
            wdi.timeLayerNum = 3;
            wdi.latitudeNum = 361;
            wdi.longitudeNum = 720;
            wdi.startTime = 00;
            wdi.intervalTime = 3;
            wdi.reserved = 0;

            wdi.geoInfo[0] = 0;
            wdi.geoInfo[1] = 0.5;
            wdi.geoInfo[2] = 90;
            wdi.geoInfo[3] = 0.5;

            //Console.WriteLine(speed[0, 1]);
            WriteObjectToMMF(@"E:\DSME\GFS\windSpeedData.MMF", wsd, @"Global\windSpeedData");
            WriteObjectToMMF(@"E:\DSME\GFS\windDirectionData.MMF", wdd, @"Global\windDirectionData");
            WriteObjectToMMF(@"E:\DSME\GFS\windSpeedInfo.MMF", wdi, @"Global\windSpeedInfo");
            WriteObjectToMMF(@"E:\DSME\GFS\windDirectionInfo.MMF", wdi, @"Global\windDirectionInfo");


            /*wsd = ReadObjectFromMMF("windSpeedData") as WindSpeedData;
            Console.WriteLine("wsd:" + wsd.spd_values[0, 1]);

            wdd = ReadObjectFromMMF("windDirectionData") as WindDirectionData;
            Console.WriteLine("wdd:" + wdd.dir_values[0, 1]);*/
        }
        private void Button1_Click(object sender, EventArgs e)
        {
            
            
            //test_server("windSpeedData", wsd);
            // wsd = ReadObjectFromMMF("windSpeedData") as windSpeedData;
            // Console.WriteLine("wsd:" + wsd.values[0, 1]);



            //Console.WriteLine(direction[0, 1]);

            //wdd = ReadObjectFromMMF("windDirectionData") as windDirectionData;
            //Console.WriteLine("wdd:" + wdd.values[0, 1]);
            //test_server("windDirectionData", wdd);



            //wdi = ReadObjectFromMMF("windDirectionInfo") as WeatherDataInfo;
            /*Console.WriteLine("wdi_file:" + wdi.fileName.GetType() + "// " + wdi.fileName[0]);
            Console.WriteLine("wdi_timeLayerNum:" + wdi.timeLayerNum.GetType() + "// " + wdi.timeLayerNum);
            Console.WriteLine("wdi_lat:" + wdi.latitudeNum.GetType() + "// " + wdi.latitudeNum);
            Console.WriteLine("wdi_lon:" + wdi.longitudeNum.GetType() + "// " + wdi.longitudeNum);
            Console.WriteLine("wdi_startTime:" + wdi.startTime.GetType() + "// " + wdi.startTime);
            Console.WriteLine("wdi_intervalTime:" + wdi.intervalTime.GetType() + "// " + wdi.intervalTime);
            Console.WriteLine("wdi_reserved:" + wdi.reserved.GetType() + "// " + wdi.reserved);
            Console.WriteLine("wdi_geoInfo:" + wdi.geoInfo.GetType() + "// " + wdi.geoInfo);*/

        }
        public string[,] math_speed(string[,] u_data, string[,] v_data)
        {
            //lat = 가로선 
            //lon = 세로선
            // u 나 v 나 어차피 사이즈는 같기에 u 로 편의상 쓴다.

            string[,] buff = new string[u_data.GetLength(0), u_data.GetLength(1)];
            for (int lat = 0; lat < u_data.GetLength(0); lat++)//361
            {
                for (int lon = 0; lon < u_data.GetLength(1); lon++)//720
                {
                    if (!u_data[lat, lon].Equals("nan"))
                    {
                        var u_data_pow = Math.Pow(double.Parse(u_data[lat, lon]), 2); // u data 의 제곱
                        var v_data_pow = Math.Pow(double.Parse(v_data[lat, lon]), 2); // u data 의 제곱
                        var u_v_speed = Math.Sqrt(u_data_pow + v_data_pow);


                        buff[lat, lon] = u_v_speed.ToString();
                    }
                    else if (u_data[lat, lon].Equals("nan"))
                    {
                        buff[lat, lon] = "nan";
                    }

                }
            }
            return buff;
        }

        public string[,] math_direction(string[,] u_data, string[,] v_data, double pie)
        {
            string[,] buff = new string[u_data.GetLength(0), u_data.GetLength(1)];
            for (int lat = 0; lat < u_data.GetLength(0); lat++)//361
            {
                for (int lon = 0; lon < u_data.GetLength(1); lon++)//720
                {
                    if (!u_data[lat, lon].Equals("nan"))
                    {
                        var direction = 180 + ((180 / pie) * Math.Atan2(double.Parse(u_data[lat, lon]), double.Parse(v_data[lat, lon])));

                        buff[lat, lon] = direction.ToString();
                    }
                    else if (u_data[lat, lon].Equals("nan"))
                    {
                        buff[lat, lon] = "nan";
                    }
                }
            }
            return buff;
        }
        public string[,] Change_array(string read_text)
        {
            // Split into lines.
            read_text = read_text.Replace('\n', '\r');
            string[] lines = read_text.Split(new char[] { '\r' },
                StringSplitOptions.RemoveEmptyEntries);

            // See how many rows and columns there are.
            int num_rows = lines.Length;
            int num_cols = lines[0].Split(',').Length;

            // Allocate the data array.
            string[,] values = new string[num_rows, num_cols];

            // Load the array.
            for (int r = 0; r < num_rows; r++)
            {
                string[] line_r = lines[r].Split(',');
                for (int c = 0; c < num_cols; c++)
                {
                    if (!line_r[c].ToLower().Contains("nan"))
                    {
                        float buff;
                        buff = float.Parse(line_r[c]);
                        values[r, c] = buff.ToString();
                    }
                    else
                    {
                        values[r, c] = line_r[c];
                    }
                }
            }
            return values;
        }

        private void Button2_Click(object sender, EventArgs e)
        {

            WindSpeedData wsd = null;
            wsd = ReadObjectFromMMF("windSpeedData") as WindSpeedData;
            Console.WriteLine("wsd:" + wsd.spd_values[0, 1]);

            
        }
        public void WriteObjectToMMF(string mmfFile, object objectData, string name)
        {
            bool mutexCreated;
            Mutex mutex;
            MutexSecurity mutexSecurity = new MutexSecurity();
            MemoryMappedFileSecurity mmfSecurity = new MemoryMappedFileSecurity();

            mutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
            MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
            mmfSecurity.AddAccessRule(new AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl,
            AccessControlType.Allow));

            mutex = new Mutex(false, @"Global\MyMutex", out mutexCreated, mutexSecurity);
            if (mutexCreated == false)
            {
                Console.WriteLine("There has been an error creating the mutex");
            } 
            else
            {
                Console.WriteLine("mutex created successfully");
            } 


            // Convert .NET object to byte array
            byte[] buffer = ObjectToByteArray(objectData);

            // Create a new memory mapped file
            // Create a SecurityIdentifier object for "everyone".
            mmf = MemoryMappedFile.CreateOrOpen(name, buffer.Length, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, mmfSecurity, HandleInheritability.Inheritable);
            
            // Create a view accessor into the file to accommmodate binary data size
            using (mmfWriter = mmf.CreateViewAccessor())
            {
                Console.WriteLine(name);
                mutex.WaitOne();
                mmfWriter.WriteArray<byte>(0, buffer, 0, buffer.Length);
                mutex.ReleaseMutex();
            }
            
        }

        public byte[] ObjectToByteArray(object inputObject)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();    // Create new BinaryFormatter
            MemoryStream memoryStream = new MemoryStream();             // Create target memory stream
            binaryFormatter.Serialize(memoryStream, inputObject);       // Serialize object to stream
            var buf = memoryStream.ToArray();
            return buf;                             // Return stream as byte array
        }

        public object ReadObjectFromMMF(string name)
        {
            // Get a handle to an existing memory mapped file
            Console.WriteLine(name);
            using (mmf2 = MemoryMappedFile.OpenExisting(@"Global\" + name, MemoryMappedFileRights.Read))
            {

                using (mmfReader = mmf2.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read))
                {
                    Mutex mutex = Mutex.OpenExisting(@"Global\MyMutex");
                    mutex.WaitOne();
                    byte[] buffer = new byte[mmfReader.Capacity];
                    mmfReader.ReadArray<byte>(0, buffer, 0, buffer.Length);
                    mutex.ReleaseMutex();
                    // Convert the buffer to a .NET object
                    return ByteArrayToObject(buffer);
                }
            }

        }

        public object ByteArrayToObject(byte[] buffer)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter(); // Create new BinaryFormatter
            MemoryStream memoryStream = new MemoryStream(buffer);    // Convert buffer to memorystream
            return binaryFormatter.Deserialize(memoryStream);        // Deserialize stream to an object
        }

        private void Button3_Click(object sender, EventArgs e)
        {
            WindDirectionData wdd = null;
            
            wdd = ReadObjectFromMMF("windDirectionData") as WindDirectionData;
            Console.WriteLine("wdd:" + wdd.dir_values[0, 1]);
        }

        private void Button4_Click(object sender, EventArgs e)
        {
            WeatherDataInfo wdi = null;
            wdi = ReadObjectFromMMF("windDirectionInfo") as WeatherDataInfo;
            Console.WriteLine("wdi_file:" + wdi.fileName.GetType() + "// " + wdi.fileName[0]);
            Console.WriteLine("wdi_timeLayerNum:" + wdi.timeLayerNum.GetType() + "// " + wdi.timeLayerNum);
            Console.WriteLine("wdi_lat:" + wdi.latitudeNum.GetType() + "// " + wdi.latitudeNum);
            Console.WriteLine("wdi_lon:" + wdi.longitudeNum.GetType() + "// " + wdi.longitudeNum);
            Console.WriteLine("wdi_startTime:" + wdi.startTime.GetType() + "// " + wdi.startTime);
            Console.WriteLine("wdi_intervalTime:" + wdi.intervalTime.GetType() + "// " + wdi.intervalTime);
            Console.WriteLine("wdi_reserved:" + wdi.reserved.GetType() + "// " + wdi.reserved);
            Console.WriteLine("wdi_geoInfo:" + wdi.geoInfo.GetType() + "// " + wdi.geoInfo);
        }

        private void Button5_Click(object sender, EventArgs e)
        {
            WeatherDataInfo wsi = null;
            wsi = ReadObjectFromMMF("windSpeedInfo") as WeatherDataInfo;
            Console.WriteLine("wdi_file:" + wsi.fileName.GetType() + "// " + wsi.fileName[0]);
            Console.WriteLine("wdi_timeLayerNum:" + wsi.timeLayerNum.GetType() + "// " + wsi.timeLayerNum);
            Console.WriteLine("wdi_lat:" + wsi.latitudeNum.GetType() + "// " + wsi.latitudeNum);
            Console.WriteLine("wdi_lon:" + wsi.longitudeNum.GetType() + "// " + wsi.longitudeNum);
            Console.WriteLine("wdi_startTime:" + wsi.startTime.GetType() + "// " + wsi.startTime);
            Console.WriteLine("wdi_intervalTime:" + wsi.intervalTime.GetType() + "// " + wsi.intervalTime);
            Console.WriteLine("wdi_reserved:" + wsi.reserved.GetType() + "// " + wsi.reserved);
            Console.WriteLine("wdi_geoInfo:" + wsi.geoInfo.GetType() + "// " + wsi.geoInfo);
        }
    }
}
