using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using ECOT.Engine;
using Microsoft.Xna.Framework.Graphics;

namespace ECOT.Game
{
    public class MapLoader
    {
        private Map map;

        private static readonly string magicNb = "STET";

        public MapLoader(Map map)
        {
            this.map = map;
        }

        private string GetLocalizedStoryLine(string originalStoryLine)
        {
            string formattedMapName = this.map.Name.Replace(" ", "");
            formattedMapName = formattedMapName.Replace("é", "e");

            string localizedStoryLine = Kernel.Instance.Text.Get(string.Format("MapStoryLine{0}", formattedMapName));
            localizedStoryLine = localizedStoryLine ?? originalStoryLine;

            return localizedStoryLine;
        }

        public void LoadInfo(string filename)
        {
            MemoryStream stream = new MemoryStream(Kernel.Instance.Medias.Get<byte[]>(filename), false);
            BinaryReader reader = new BinaryReader(stream);

            // Magic number
            if (!magicNb.Equals(new string(reader.ReadChars(4))))
                throw new Exception("Invalid file type");

            // Récupère les informations
            this.map.Name = reader.ReadString();
            this.map.Size = new Size(reader.ReadInt32(), reader.ReadInt32());
            this.map.Level = reader.ReadString();
            this.map.StartDate = reader.ReadString();
            this.map.Storyline = this.GetLocalizedStoryLine(reader.ReadString());

            // Ferme le reader
            reader.Close();
        }

        public void Load(string filename)
        {
            FileStream stream = new FileStream(Kernel.Instance.DataPath + filename, FileMode.Open);
            BinaryReader reader = new BinaryReader(stream);

            // Magic number
            if (!magicNb.Equals(new string(reader.ReadChars(4))))
                throw new Exception("Invalid file type");

            // Récupère les informations
            this.map.Name = reader.ReadString();
            this.map.Size = new Size(reader.ReadInt32(), reader.ReadInt32());
            this.map.Level = reader.ReadString();
            this.map.StartDate = reader.ReadString();
            this.map.Storyline = this.GetLocalizedStoryLine(reader.ReadString());

            // Récupère la table des tilesets
            Dictionary<int, string> tilesets = new Dictionary<int, string>();
            int nbTilesets = reader.ReadInt32();
            Dictionary<int, TileFamily[,]> families = new Dictionary<int, TileFamily[,]>();

            for (int i = 0; i < nbTilesets; i++)
            {
                int tilesetId = reader.ReadInt32();
                string tileset = reader.ReadString();
                tilesets[tilesetId] = Kernel.Instance.CurrentDirectory + tileset.Substring(0, tileset.Length - 4);

                families[tilesetId] = this.GetFamilies(tilesets[tilesetId]);
            }

            // Pre-charge les textures de chaque tilesets
            Dictionary<int, Texture2D> tilesetsTex = new Dictionary<int, Texture2D>();
            foreach (KeyValuePair<int, string> item in tilesets)
                tilesetsTex[item.Key] = Kernel.Instance.Medias.Get<Texture2D>(item.Value);

            // Récupère la table des textures de ville
            string[] cityTextures = new string[reader.ReadInt32()];
            for (int i = 0; i < cityTextures.Length; i++)
            {
                string cityTexture = reader.ReadString();
                cityTextures[i] = cityTexture.Substring(0, cityTexture.Length - 4);
            }

            // Récupère les propriétés de chaque tile
            Dictionary<TileFamily, TileProperties> propertiesMap = new Dictionary<TileFamily, TileProperties>();
            this.GetTilesProperties(propertiesMap);

            // Récupère les tiles
            GetTiles(reader, map.Tiles, tilesetsTex, families, propertiesMap);
            // Récupère les éléments
            GetTiles(reader, map.Elements, tilesetsTex, families, propertiesMap);
            // Récupère les villes
            this.GetCities(reader, cityTextures);

            // Ferme le reader
            reader.Close();

        }

        private TileFamily[,] GetFamilies(string tileset)
        {
            TileFamily[,] families = new TileFamily[4, 4];
            StreamReader reader = null;

            // TEMP
            tileset = tileset.Replace(Kernel.Instance.CurrentDirectory + @"Textures\TileSheet", "Tilesets");

            try
            {
                reader = new StreamReader(new MemoryStream(Kernel.Instance.Medias.Get<byte[]>(tileset + ".tileset")));
            }
            catch
            {
                // DEBUG
                // Si le fichier tileset n'est pas trouvé, on met de l'herbe par défaut
                for (int i = 0; i < 4; i++)
                    for (int j = 0; j < 4; j++)
                        families[i, j] = TileFamily.Grass;
                return families;
            }

            string line;
            string[] chunks;

            for (int j = 0; j < 4; j++)
            {
                line = reader.ReadLine();
                chunks = line.Trim().Substring(1, line.Length - 2).Split(new char[] { ',' });
                for (int i = 0; i < chunks.Length; i++)
                {
                    string[] chunkFamilies = chunks[i].Trim().Split(new char[] { '|' });
                    for (int k = 0; k < chunkFamilies.Length; k++)
                    {
#if !XBOX

                        string test = chunkFamilies[k].Trim();
                        families[i, j] |= (TileFamily)Enum.Parse(typeof(TileFamily), test, true);

#else
                        families[i, j] |= (TileFamily)Utilities.ParseEnum(typeof(TileFamily), chunkFamilies[k].Trim());
                        
#endif
                    }
                }
            }


            
            
            reader.Close();
            return families;
        }

        private static void GetTiles(BinaryReader reader, Tile[,] tiles, Dictionary<int, Texture2D> tilesetsTex, Dictionary<int, TileFamily[, ]> families, Dictionary<TileFamily, TileProperties> propertiesMap)
        {
            Tile tile;

            int tileCount = reader.ReadInt32();
            for (int i = 0; i < tileCount; i++)
            {
                int x = reader.ReadInt32();
                int y = reader.ReadInt32();
                int tilesetId = reader.ReadInt32();
                int tx = reader.ReadInt32();
                int ty = reader.ReadInt32();

                tile = tiles[x, y] = new Tile();
                tile.Texture = tilesetsTex[tilesetId];
                tile.Position = new Point(tx, ty);
                tile.Family = families[tilesetId][tx, ty];
                if (propertiesMap.ContainsKey(tile.Family))
                    tile.Properties = propertiesMap[tile.Family];
            }

        }

        private void GetCities(BinaryReader reader, string[] cityTextures)
        {
            City city;

            int cityCount = reader.ReadInt32();
            for (int i = 0; i < cityCount; i++)
            {
                city = new City();
                city.Position = new Vector2(reader.ReadInt32(), reader.ReadInt32());
                //city.Texture =Kernel.Instance.Medias.Get<Texture2D>(cityTextures[reader.ReadInt32()]);
                reader.ReadInt32();
                city.Name = reader.ReadString();
                // FIXME: this is because archive are encoded in ASCII ...
                city.Name = city.Name.Replace("?", "é");
                if (city.Name == "Paris")
                {
                    city.Population.Value = reader.ReadInt32();
                    city.Level = 1;
                    reader.ReadInt32(); // Correspond au niveau - Actuellement "bridage" au premier niveau

                    city.Enabled = true;

                    // Compatibilité Chronique 2 - Programmez
                    reader.ReadDouble();
                    reader.ReadDouble();
                    reader.ReadDouble();
                    reader.ReadBoolean(); // important (pour transports)
                    reader.ReadDouble();
                    reader.ReadDouble();

                    city.Mayor = reader.ReadString();
                    city.RealPopulation = reader.ReadInt32();
                    city.RealSurface = reader.ReadInt32();
                    city.Province = reader.ReadString();

                    if (city.Name == "Paris")
                        this.map.AddCity(city);
                }
                else
                {
                    reader.ReadInt32();
                    reader.ReadInt32();

                    city.Enabled = true;

                    // Compatibilité Chronique 2 - Programmez
                    reader.ReadDouble();
                    reader.ReadDouble();
                    reader.ReadDouble();
                    reader.ReadBoolean(); // important (pour transports)
                    reader.ReadDouble();
                    reader.ReadDouble();

                    city.Mayor = reader.ReadString();
                    city.RealPopulation = reader.ReadInt32();
                    city.RealSurface = reader.ReadInt32();
                    city.Province = reader.ReadString();

                }
            }
        }

        private void GetTilesProperties(Dictionary<TileFamily, TileProperties> propertiesMap)
        {
            TileProperties properties = null;

            // Herbe
            properties = new TileProperties();
            properties.ConstructionCostFactor = 1;
            properties.ConstructionTimeFactor = 1;

            properties.WaterInfluenceFactor = 1;
            properties.FoodInfluenceFactor = 1;
            properties.EnergyInfluenceFactor = 1;
            properties.HousingInfluenceFactor = 1;
            properties.HealthInfluenceFactor = 1.2;
            properties.TransportInfluenceFactor = 1;
            properties.JobInfluenceFactor = 1;
            properties.EducationInfluenceFactor = 1.3;

            properties.PollutionInfluenceFactor = 1;

            propertiesMap.Add(TileFamily.Grass, properties);

            // Sable
            properties = new TileProperties();
            properties.ConstructionCostFactor = 2;
            properties.ConstructionTimeFactor = 3;

            properties.WaterInfluenceFactor = 0.7;
            properties.FoodInfluenceFactor = 0.25;
            properties.EnergyInfluenceFactor = 1;
            properties.HousingInfluenceFactor = 1.5;
            properties.HealthInfluenceFactor = 1;
            properties.TransportInfluenceFactor = 0.8;
            properties.JobInfluenceFactor = 1;
            properties.EducationInfluenceFactor = 1;

            properties.PollutionInfluenceFactor = 1.2;

            propertiesMap.Add(TileFamily.Sand, properties);

            // Sol aride
            properties = new TileProperties();
            properties.ConstructionCostFactor = 0.75;
            properties.ConstructionTimeFactor = 1;

            properties.WaterInfluenceFactor = 0.5;
            properties.FoodInfluenceFactor = 0.5;
            properties.EnergyInfluenceFactor = 1;
            properties.HousingInfluenceFactor = 0.7;
            properties.HealthInfluenceFactor = 1;
            properties.TransportInfluenceFactor = 1.25;
            properties.JobInfluenceFactor = 1;
            properties.EducationInfluenceFactor = 1;

            properties.PollutionInfluenceFactor = 1.5;

            propertiesMap.Add(TileFamily.AridGround, properties);

            // Terre
            properties = new TileProperties();
            properties.ConstructionCostFactor = 0.75;
            properties.ConstructionTimeFactor = 0.75;

            properties.WaterInfluenceFactor = 1.2;
            properties.FoodInfluenceFactor = 1.5;
            properties.EnergyInfluenceFactor = 1.3;
            properties.HousingInfluenceFactor = 1;
            properties.HealthInfluenceFactor = 1;
            properties.TransportInfluenceFactor = 1;
            properties.JobInfluenceFactor = 1;
            properties.EducationInfluenceFactor = 1;

            properties.PollutionInfluenceFactor = 1.3;

            propertiesMap.Add(TileFamily.Ground, properties);

            // Neige
            properties = new TileProperties();
            properties.ConstructionCostFactor = 1.25;
            properties.ConstructionTimeFactor = 2;

            properties.WaterInfluenceFactor = 1.5;
            properties.FoodInfluenceFactor = 0.5;
            properties.EnergyInfluenceFactor = 1;
            properties.HousingInfluenceFactor = 1.2;
            properties.HealthInfluenceFactor = 0.9;
            properties.TransportInfluenceFactor = 0.8;
            properties.JobInfluenceFactor = 0.9;
            properties.EducationInfluenceFactor = 0.9;

            properties.PollutionInfluenceFactor = 1;

            propertiesMap.Add(TileFamily.Snow, properties);

            // Moutain
            properties = new TileProperties();
            propertiesMap.Add(TileFamily.Montain, properties);

            // River
            properties = new TileProperties();
            propertiesMap.Add(TileFamily.River, properties);

            // Cost
            properties = new TileProperties();
            propertiesMap.Add(TileFamily.Cost, properties);

            // Border
            properties = new TileProperties();
            propertiesMap.Add(TileFamily.Border, properties);

            // Passive background
            properties = new TileProperties();
            propertiesMap.Add(TileFamily.PassiveBackground, properties);

            // Merges
            propertiesMap.Add(TileFamily.Grass | TileFamily.Sand,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.Sand]));
            propertiesMap.Add(TileFamily.Grass | TileFamily.AridGround,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.AridGround]));
            propertiesMap.Add(TileFamily.Grass | TileFamily.Ground,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.Ground]));
            propertiesMap.Add(TileFamily.Grass | TileFamily.Snow,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.Snow]));
            propertiesMap.Add(TileFamily.Grass | TileFamily.Border,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.Border]));
            propertiesMap.Add(TileFamily.Grass | TileFamily.Cost,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.Cost]));
            propertiesMap.Add(TileFamily.Grass | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.Grass], propertiesMap[TileFamily.River]));
            propertiesMap.Add(TileFamily.Sand | TileFamily.AridGround,
                TileProperties.Merge(propertiesMap[TileFamily.Sand], propertiesMap[TileFamily.AridGround]));
            propertiesMap.Add(TileFamily.Sand | TileFamily.Ground,
                TileProperties.Merge(propertiesMap[TileFamily.Sand], propertiesMap[TileFamily.Ground]));
            propertiesMap.Add(TileFamily.Sand | TileFamily.Snow,
                TileProperties.Merge(propertiesMap[TileFamily.Sand], propertiesMap[TileFamily.Snow]));
            propertiesMap.Add(TileFamily.Sand | TileFamily.Border,
                TileProperties.Merge(propertiesMap[TileFamily.Sand], propertiesMap[TileFamily.Border]));
            propertiesMap.Add(TileFamily.Sand | TileFamily.Cost,
                TileProperties.Merge(propertiesMap[TileFamily.Sand], propertiesMap[TileFamily.Cost]));
            propertiesMap.Add(TileFamily.Sand | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.Sand], propertiesMap[TileFamily.River]));
            propertiesMap.Add(TileFamily.AridGround | TileFamily.Ground,
                TileProperties.Merge(propertiesMap[TileFamily.AridGround], propertiesMap[TileFamily.Ground]));
            propertiesMap.Add(TileFamily.AridGround | TileFamily.Snow,
                TileProperties.Merge(propertiesMap[TileFamily.AridGround], propertiesMap[TileFamily.Snow]));
            propertiesMap.Add(TileFamily.AridGround | TileFamily.Border,
                TileProperties.Merge(propertiesMap[TileFamily.AridGround], propertiesMap[TileFamily.Border]));
            propertiesMap.Add(TileFamily.AridGround | TileFamily.Cost,
                TileProperties.Merge(propertiesMap[TileFamily.AridGround], propertiesMap[TileFamily.Cost]));
            propertiesMap.Add(TileFamily.AridGround | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.AridGround], propertiesMap[TileFamily.River]));
            propertiesMap.Add(TileFamily.Ground | TileFamily.Snow,
                TileProperties.Merge(propertiesMap[TileFamily.Ground], propertiesMap[TileFamily.Snow]));
            propertiesMap.Add(TileFamily.Ground | TileFamily.Border,
                TileProperties.Merge(propertiesMap[TileFamily.Ground], propertiesMap[TileFamily.Border]));
            propertiesMap.Add(TileFamily.Ground | TileFamily.Cost,
                TileProperties.Merge(propertiesMap[TileFamily.Ground], propertiesMap[TileFamily.Cost]));
            propertiesMap.Add(TileFamily.Ground | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.Ground], propertiesMap[TileFamily.River]));
            propertiesMap.Add(TileFamily.Snow | TileFamily.Cost,
                TileProperties.Merge(propertiesMap[TileFamily.Snow], propertiesMap[TileFamily.Cost]));
            propertiesMap.Add(TileFamily.Snow | TileFamily.Border,
                TileProperties.Merge(propertiesMap[TileFamily.Snow], propertiesMap[TileFamily.Border]));
            propertiesMap.Add(TileFamily.Snow | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.Snow], propertiesMap[TileFamily.River]));
            propertiesMap.Add(TileFamily.Cost | TileFamily.Border,
                TileProperties.Merge(propertiesMap[TileFamily.Cost], propertiesMap[TileFamily.Border]));
            propertiesMap.Add(TileFamily.Cost | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.Cost], propertiesMap[TileFamily.River]));
            propertiesMap.Add(TileFamily.Border | TileFamily.River,
                TileProperties.Merge(propertiesMap[TileFamily.Border], propertiesMap[TileFamily.River]));

            propertiesMap.Add(TileFamily.Border | TileFamily.River | TileFamily.Cost,
                TileProperties.Merge(
                    TileProperties.Merge(propertiesMap[TileFamily.Border], propertiesMap[TileFamily.River]),
                    propertiesMap[TileFamily.Cost]));
        }

    }
}
