using MySql.Data.MySqlClient;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Services.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using static OpenMetaverse.Primitive;

namespace GetRegionTextures
{
    class GetRegionTextures
    {
        private static MySqlConnection m_mySQLConnection = null;
        private static String m_assetURL = "http://127.0.0.1:8002/assets/";

        private static List<UUID> m_assets = new List<UUID>();

        private static List<MySQLPrimObject> getAllScenePrims(String regionID)
        {
            List<MySQLPrimObject> returnData = new List<MySQLPrimObject>();

            MySqlCommand _mysqlCommand = m_mySQLConnection.CreateCommand();
            _mysqlCommand.CommandTimeout = int.MaxValue;
            _mysqlCommand.CommandText = "SELECT prims.Name,prims.UUID,primshapes.Texture FROM prims LEFT JOIN primshapes ON prims.UUID = primshapes.UUID WHERE RegionUUID = ?RegionUUID";
            _mysqlCommand.Parameters.AddWithValue("RegionUUID", regionID);

            MySqlDataReader _commandReader = _mysqlCommand.ExecuteReader();

            while (_commandReader.Read())
                returnData.Add(new MySQLPrimObject(_commandReader.GetString(0), _commandReader.GetString(1), (byte[])_commandReader["Texture"]));

            _commandReader.Close();

            return returnData;
        }

        private static List<String> getAllRegionIDsFromStorage()
        {
            List<String> returnData = new List<String>();

            MySqlCommand _mysqlCommand = m_mySQLConnection.CreateCommand();
            _mysqlCommand.CommandTimeout = int.MaxValue;
            _mysqlCommand.CommandText = "SELECT RegionUUID FROM terrain";

            MySqlDataReader _commandReader = _mysqlCommand.ExecuteReader();

            while (_commandReader.Read())
                returnData.Add(_commandReader.GetString(0));

            _commandReader.Close();

            return returnData;
        }

        public static Stream GenerateStreamFromString(string s)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

        static void Main(string[] args)
        {
            if(!File.Exists("ffmpeg.exe"))
            {
                Console.WriteLine("Cant find ffmpeg.exe");
                Console.ReadLine();
                return;
            }

            String config = "";

            if (File.Exists("OpenSim.ini"))
                config += File.ReadAllText("OpenSim.ini") + "\n";

            if (File.Exists("config-include/GridCommon.ini"))
                config += File.ReadAllText("config-include/GridCommon.ini") + "\n";

            IConfigSource source = new IniConfigSource(GenerateStreamFromString(config));
            source.ExpandKeyValues();

            if (source.Configs["DatabaseService"] == null)
                return;

            if (source.Configs["HGAssetService"] == null)
                return;


            if (source.Configs["DatabaseService"].GetString("ConnectionString", null) == null)
                return;

            if (source.Configs["HGAssetService"].GetString("HomeURI", "http://127.0.0.1:8002") == null)
                return;

            m_assetURL = source.Configs["HGAssetService"].GetString("HomeURI", "http://127.0.0.1:8002") + "/assets/";

            Console.WriteLine("AssetServer URL: " + m_assetURL);

            m_mySQLConnection = new MySqlConnection(source.Configs["DatabaseService"].GetString("ConnectionString", null));
            m_mySQLConnection.Open();

            if (!Directory.Exists("RegionData"))
                Directory.CreateDirectory("RegionData");

            List<String> allRegionIDs = getAllRegionIDsFromStorage();

            foreach(String regionID in allRegionIDs)
            {
                Console.WriteLine("Start with region '" + regionID + "'");

                if (!Directory.Exists("RegionData/" + regionID))
                    Directory.CreateDirectory("RegionData/" + regionID);

                if (!Directory.Exists("RegionData/assets"))
                    Directory.CreateDirectory("RegionData/assets");

                if (!Directory.Exists("RegionData/" + regionID + "/images"))
                    Directory.CreateDirectory("RegionData/" + regionID + "/images");

                List<MySQLPrimObject> prims = getAllScenePrims(regionID);

                int primCounter = 0;
                foreach(MySQLPrimObject prim in prims)
                {
                    Console.WriteLine("   ["+ ++primCounter + " / "+ prims .Count+ "]Start with scene object '" + prim.Name + "'");
                    Primitive.TextureEntry textureEntry = new Primitive.TextureEntry(prim.RawTextueData, 0, prim.RawTextueData.Length);

                    foreach (TextureEntryFace faceEntry in textureEntry.FaceTextures)
                    {
                        if (faceEntry != null)
                        {
                            if (faceEntry.TextureID != UUID.Zero)
                            {
                                prim.Texture.Add(faceEntry.TextureID);
                                Console.WriteLine("       Found texture '" + faceEntry.TextureID + "'");

                                if (!m_assets.Contains(faceEntry.TextureID))
                                    m_assets.Add(faceEntry.TextureID);
                            }
                        }
                    }        
                }

                //Create CSV
                List<String> entrys = new List<String>();
                foreach (MySQLPrimObject prim in prims)
                {
                    String newEntry = prim.Name + ";" + prim.ID.ToString() + ";";

                    foreach (UUID texturID in prim.Texture)
                        newEntry += texturID.ToString() + ";";

                    entrys.Add(newEntry);
                }

                File.WriteAllLines("RegionData/" + regionID + "/result.csv", entrys.ToArray());

                //Start Asset Download
                using (WebClient assetHTTPClient = new WebClient())
                {
                    int downloadCounter = 0;

                    foreach(UUID assetID in m_assets)
                    {
                        try
                        {
                            Console.WriteLine("[" + ++downloadCounter + " / " + m_assets.Count + "] Download asset " + assetID.ToString());
                            if(!File.Exists("RegionData/assets/" + assetID.ToString() + ".jpeg2000"))
                                assetHTTPClient.DownloadFile(m_assetURL + assetID.ToString() + "/data", "RegionData/assets/" + assetID.ToString() + ".jpeg2000");

                            if (!File.Exists("RegionData/" + regionID + "/images/" + assetID.ToString() + ".png"))
                            {
                                Process process = new Process();
                                process.StartInfo.FileName = "ffmpeg.exe";
                                process.StartInfo.Arguments = "-i RegionData/assets/" + assetID.ToString() + ".jpeg2000 RegionData/" + regionID + "/images/" + assetID.ToString() + ".png";
                                process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                                process.Start();
                                process.WaitForExit();
                            }
                        }catch(Exception error)
                        {
                            Console.WriteLine("[" + downloadCounter + " / " + m_assets.Count + "] Download failed for asset " + assetID.ToString() + ": " + error.Message);
                        }
                    }
                }
            }
        }
    }
}
