using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Xml;

namespace IronIntel.Contractor.Shape
{
    public static class ShapeFileParser
    {
        /// <summary>
        /// 解析.shp文件
        /// </summary>
        /// <param name="fileName"></param>
        public static void ParseFromShapeFile(string fileName, Shape shape)
        {
            using (FileStream fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                ParseFromShapeFile(fileStream, shape);
            }
        }

        /// <summary>
        ///  解析.shp文件
        /// </summary>
        /// <param name="buffer">.shp文件数据</param>
        /// <param name="shape"></param>
        public static void ParseFromShapeFile(byte[] buffer, Shape shape)
        {
            using (MemoryStream ms = new MemoryStream(buffer, false))
            {
                ParseFromShapeFile(ms, shape);
            }
        }

        /// <summary>
        /// 解析.shp文件
        /// </summary>
        /// <param name="fs">装载.shp文件数据的流</param>
        public static void ParseFromShapeFile(Stream fs, Shape shape)
        {
            fs.Seek(0, SeekOrigin.Begin);
            using (BinaryReader binaryReader = new BinaryReader(fs))
            {
                binaryReader.ReadBytes(24);

                int FileLength = binaryReader.ReadInt32();//<0代表数据长度未知
                int FileBanben = binaryReader.ReadInt32();
                int ShapeType = binaryReader.ReadInt32();

                double xmin = binaryReader.ReadDouble();
                double ymax = -1 * binaryReader.ReadDouble();
                double xmax = binaryReader.ReadDouble();
                double ymin = -1 * binaryReader.ReadDouble();
                double width = xmax - xmin;
                double height = ymax - ymin;

                binaryReader.ReadBytes(32);

                switch (ShapeType)
                {
                    case 1://Point
                    case 11://PointZ
                    case 21://PointM
                        MapPoints points = new MapPoints();
                        shape.Points.Add(points);
                        ParsePoints(binaryReader, points);
                        break;
                    case 3://PolyLine
                    case 13://PolyLineZ
                    case 23://PolyLineM
                        ParsePolylines(binaryReader, shape.Polylines);
                        break;
                    case 5://Polygon
                    case 15://PolygonZ
                    case 25://PolygonM
                        ParsePolygons(binaryReader, shape.Polygons);
                        break;
                }
            }
        }

        /// <summary>
        /// 从zip文件当中解析.shp
        /// </summary>
        /// <param name="stream">装载.zip文件内容的流</param>
        /// <param name="shape"></param>
        public static void ParseFromZipFile(Stream stream, Shape shape)
        {
            const string EXT = ".shp";
            //using (ZipInputStream s = new ZipInputStream(stream))
            //{
            //    ZipEntry zipentry = s.GetNextEntry();
            //    while (zipentry != null)
            //    {
            //        if (zipentry.IsDirectory)
            //        {
            //            zipentry = s.GetNextEntry();
            //            continue;
            //        }
            //        string ext = Path.GetExtension(zipentry.FileName);
            //        if (string.Compare(ext, EXT, true) == 0)
            //        {
            //            Stream shpStream = new MemoryStream();
            //            int size = 0;
            //            byte[] data = new byte[2048];
            //            while (true)
            //            {
            //                size = s.Read(data, 0, data.Length);
            //                if (size > 0)
            //                {
            //                    shpStream.Write(data, 0, size);
            //                }
            //                else
            //                {
            //                    break;
            //                }
            //            }
            //            ParseFromShapeFile(shpStream, shape);
            //        }
            //        zipentry = s.GetNextEntry();
            //    }//while
            //}
        }

        /// <summary>
        /// 从.zip文件当中解析.shp
        /// </summary>
        /// <param name="buffer">.zip文件数据</param>
        /// <param name="shape"></param>
        public static void ParseFromZipFile(byte[] buffer, Shape shape)
        {
            using (MemoryStream ms = new MemoryStream(buffer, false))
            {
                ParseFromZipFile(ms, shape);
            }
        }

        /// <summary>
        /// 从.zip文件当中解析.shp
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="shape"></param>
        public static void ParseFromZipFile(string filename, Shape shape)
        {
            using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                ParseFromZipFile(fs, shape);
            }
        }

        private static void ParsePoints(BinaryReader binaryReader, MapPoints points)
        {
            while (binaryReader.PeekChar() != -1)
            {
                binaryReader.ReadUInt32();
                binaryReader.ReadInt32();
                int shapeType = binaryReader.ReadInt32();

                MapPoint p = new MapPoint();
                p.Longitude = binaryReader.ReadDouble();
                p.Latitude = binaryReader.ReadDouble();
                points.Points.Add(p);

                if (shapeType == 11)//PointZ
                {
                    binaryReader.ReadDouble();//Z
                    binaryReader.ReadDouble();//M
                }
                else if (shapeType == 21)//PointM
                {
                    binaryReader.ReadDouble();//M
                }
            }
        }

        private static void ParsePolylines(BinaryReader binaryReader, List<Polyline> polylines)
        {
            while (binaryReader.PeekChar() != -1)
            {
                Polyline polyling = new Polyline();
                polylines.Add(polyling);

                binaryReader.ReadUInt32();
                binaryReader.ReadInt32();
                int shapeType = binaryReader.ReadInt32();

                polyling.TopLeft = new MapPoint();
                polyling.BottomRight = new MapPoint();
                polyling.TopLeft.Longitude = binaryReader.ReadDouble();
                polyling.TopLeft.Latitude = binaryReader.ReadDouble();
                polyling.BottomRight.Longitude = binaryReader.ReadDouble();
                polyling.BottomRight.Latitude = binaryReader.ReadDouble();

                List<int> parts = new List<int>();//记录每条线的起始点索引
                int partCount = binaryReader.ReadInt32();
                int pointCount = binaryReader.ReadInt32();
                for (int i = 0; i < partCount; i++)
                {
                    int part = new int();
                    part = binaryReader.ReadInt32();
                    parts.Add(part);
                }

                MapPoints ring = null;
                int partIndex = 0;
                for (int j = 0; j < pointCount; j++)
                {
                    if (partIndex < parts.Count && parts[partIndex] == j)
                    {
                        ring = new MapPoints();
                        polyling.Parts.Add(ring);
                    }
                    MapPoint mp = new MapPoint();
                    mp.Longitude = binaryReader.ReadDouble();
                    mp.Latitude = binaryReader.ReadDouble();
                    ring.Points.Add(mp);
                }

                if (shapeType == 13)//PolylineZ
                {
                    binaryReader.ReadDouble();//Zmin
                    binaryReader.ReadDouble();//Zmax
                    for (int j = 0; j < pointCount; j++)
                    {
                        binaryReader.ReadDouble();//Zarray
                    }

                    binaryReader.ReadDouble();//Mmin
                    binaryReader.ReadDouble();//Mmax
                    for (int j = 0; j < pointCount; j++)
                    {
                        binaryReader.ReadDouble();//Marray
                    }
                }
                else if (shapeType == 23)//PolylineM
                {
                    binaryReader.ReadDouble();//Mmin
                    binaryReader.ReadDouble();//Mmax
                    for (int j = 0; j < pointCount; j++)
                    {
                        binaryReader.ReadDouble();//Marray
                    }
                }
            }
        }

        private static void ParsePolygons(BinaryReader binaryReader, List<Polygon> ls)
        {
            while (binaryReader.PeekChar() != -1)
            {
                binaryReader.ReadUInt32();
                binaryReader.ReadInt32();
                int shapeType = binaryReader.ReadInt32();

                Polygon p = new Polygon();
                p.TopLeft = new MapPoint();
                p.BottomRight = new MapPoint();
                p.TopLeft.Longitude = binaryReader.ReadDouble();
                p.TopLeft.Latitude = binaryReader.ReadDouble();
                p.BottomRight.Longitude = binaryReader.ReadDouble();
                p.BottomRight.Latitude = binaryReader.ReadDouble();

                List<int> parts = new List<int>();//记录每个区域的起始点索引
                int partCount = binaryReader.ReadInt32();
                int pointCount = binaryReader.ReadInt32();
                for (int j = 0; j < partCount; j++)
                {
                    int part = binaryReader.ReadInt32();
                    parts.Add(part);
                }

                MapPoints ring = null;
                int partIndex = 0;
                for (int j = 0; j < pointCount; j++)
                {
                    if (partIndex < parts.Count && parts[partIndex] == j)
                    {
                        ring = new MapPoints();
                        p.Rings.Add(ring);
                        partIndex++;
                    }
                    MapPoint mp = new MapPoint();
                    mp.Longitude = binaryReader.ReadDouble();
                    mp.Latitude = binaryReader.ReadDouble();
                    ring.Points.Add(mp);
                }

                ls.Add(p);

                if (shapeType == 15)//PolygonZ
                {
                    binaryReader.ReadDouble();//Zmin
                    binaryReader.ReadDouble();//Zmax
                    for (int j = 0; j < pointCount; j++)
                    {
                        binaryReader.ReadDouble();//Zarray
                    }

                    binaryReader.ReadDouble();//Mmin
                    binaryReader.ReadDouble();//Mmax
                    for (int j = 0; j < pointCount; j++)
                    {
                        binaryReader.ReadDouble();//Marray
                    }
                }
                else if (shapeType == 25)//PolygonM
                {
                    binaryReader.ReadDouble();//Mmin
                    binaryReader.ReadDouble();//Mmax
                    for (int j = 0; j < pointCount; j++)
                    {
                        binaryReader.ReadDouble();//Marray
                    }
                }
            }
        }




        #region kmz/kml

        /// <summary>
        /// 从kmz文件当中解析.kml，再解析kml
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="shape"></param>
        public static void ParseFromKMZFile(byte[] buffer, Shape shape)
        {
            using (MemoryStream ms = new MemoryStream(buffer, false))
            {
                ParseFromKMZFile(ms, shape);
            }
        }

        /// <summary>
        /// 从kmz文件当中解析.kml，再解析kml
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="shape"></param>
        public static void ParseFromKMZFile(Stream stream, Shape shape)
        {
            const string EXT = ".kml";
            using (ZipArchive archive = new ZipArchive(stream))
            {
                foreach (var e in archive.Entries)
                {
                    string ext = Path.GetExtension(e.Name);
                    if (string.Compare(ext, EXT, true) == 0)
                    {
                        using (StreamReader reader = new StreamReader(e.Open()))
                        {
                            using (MemoryStream fstream = new MemoryStream())
                            {
                                e.Open().CopyTo(fstream);
                                ParseFromKMLFile(fstream, shape);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 解析.kml文件
        /// </summary>
        /// <param name="fileName"></param>
        public static void ParseFromKMLFile(string fileName, Shape shape)
        {
            using (FileStream fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                ParseFromKMLFile(fileStream, shape);
            }
        }

        /// <summary>
        ///  解析.kml文件
        /// </summary>
        /// <param name="buffer">.kml文件数据</param>
        /// <param name="shape"></param>
        public static void ParseFromKMLFile(byte[] buffer, Shape shape)
        {
            using (MemoryStream ms = new MemoryStream(buffer, false))
            {
                ParseFromKMLFile(ms, shape);
            }
        }

        /// <summary>
        /// 解析.kml文件
        /// </summary>
        /// <param name="fs">装载.kml文件数据的流</param>
        public static void ParseFromKMLFile(Stream fs, Shape shape)
        {
            //fs.Seek(0, SeekOrigin.Begin);
            //StreamReader sr = new StreamReader(fs);
            //string content = sr.ReadToEnd();
            fs.Seek(0, SeekOrigin.Begin);
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(fs);
            XmlElement root = xmldoc.DocumentElement;

            XmlNodeList pointNodes = root.GetElementsByTagName("Point");
            foreach (XmlElement pe in pointNodes)
            {
                ParseKMLPoints(pe, shape.Points);
            }

            XmlNodeList lineNodes = root.GetElementsByTagName("LineString");
            foreach (XmlElement le in lineNodes)
            {
                ParseKMLLines(le, shape.Polylines);
            }

            XmlNodeList polygonNodes = root.GetElementsByTagName("Polygon");
            foreach (XmlElement pe in polygonNodes)
            {
                ParseKMLPolygons(pe, shape.Polygons);
            }
        }

        private static void ParseKMLPoints(XmlElement node, List<MapPoints> ls)
        {
            if (node == null) return;
            var coordinates = node["coordinates"];
            if (coordinates == null) return;

            MapPoints points = new MapPoints();
            ls.Add(points);

            string coordinatesstr = coordinates.InnerText.Trim();
            string[] temps = coordinatesstr.Split(',');//Longitude,Latitude,Z
            MapPoint mp = new MapPoint();
            double d = 0;
            if (double.TryParse(temps[0], out d))
                mp.Longitude = d;
            if (double.TryParse(temps[1], out d))
                mp.Latitude = d;
            points.Points.Add(mp);
        }

        private static void ParseKMLLines(XmlElement node, List<Polyline> ls)
        {
            if (node == null) return;
            var coordinates = node["coordinates"];
            if (coordinates == null) return;

            Polyline l = new Polyline();
            ls.Add(l);

            MapPoints line = new MapPoints();
            l.Parts.Add(line);

            string coordinatesstr = coordinates.InnerText.Trim();
            string[] cs = coordinatesstr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            double d = 0;
            foreach (string c in cs)
            {
                string[] temps = c.Split(',');//Longitude,Latitude,Z
                MapPoint mp = new MapPoint();
                if (double.TryParse(temps[0], out d))
                    mp.Longitude = d;
                if (double.TryParse(temps[1], out d))
                    mp.Latitude = d;
                line.Points.Add(mp);
            }
        }

        private static void ParseKMLPolygons(XmlElement node, List<Polygon> ls)
        {
            if (node == null) return;
            var outer = node["outerBoundaryIs"];
            if (outer == null) return;
            var linearRing = outer["LinearRing"];
            if (linearRing == null) return;
            var coordinates = linearRing["coordinates"];
            if (coordinates == null) return;

            Polygon p = new Polygon();
            ls.Add(p);

            MapPoints ring = new MapPoints();
            p.Rings.Add(ring);

            string coordinatesstr = coordinates.InnerText.Trim();
            string[] cs = coordinatesstr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            double d = 0;
            foreach (string c in cs)
            {
                string[] temps = c.Split(',');//Longitude,Latitude,Z
                MapPoint mp = new MapPoint();
                if (double.TryParse(temps[0], out d))
                    mp.Longitude = d;
                if (double.TryParse(temps[1], out d))
                    mp.Latitude = d;
                ring.Points.Add(mp);
            }
        }
        #endregion
    }
}


