using ActionGame.GameResources.Models;
using GameMaker.Logic;
using GameMaker.Models;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;


namespace GameMaker.UI
{
    public partial class WorldCellEditPanel : Panel
    {
        public Session Session { get; set; }
        public WorldCellDefinition WorldCell { get; set; }
        public TileSetSelectPanel TileSetSelectPanel { get; set; }
        public WorldForm.EditMode? EditMode { get; set; }
        public Int32? SelectedTileX { get; set; }
        public Int32? SelectedTileY { get; set; }

        private Int32 tileSize;
        private Dictionary<Int32, Image> cacheTextureImages;
        private Dictionary<Int32, Image> cacheTileImages;
        private Dictionary<Int32, Image> cacheCharacterImages;



        public WorldCellEditPanel()
        {
            InitializeComponent();

            this.cacheTextureImages = new Dictionary<Int32, Image>();
            this.cacheTileImages = new Dictionary<Int32, Image>();
            this.cacheCharacterImages = new Dictionary<Int32, Image>();

            this.TileSize = 64;
            
            this.EditMode = null;
            this.DoubleBuffered = true;
        }


        public Int32 TileSize
        {
            get
            {
                return tileSize;
            }
            set
            {
                tileSize = value;
                this.AutoScrollMinSize = new Size(tileSize * 22, tileSize * 11);
            }
        }


        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Pen selectedPen = new Pen(Color.FromArgb(255, 0, 0));
            Brush defaultBrush = new SolidBrush(Color.FromArgb(0, 255, 255, 255));
            Brush doorBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0));
            Brush npcBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 255));

            e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);

            if (this.WorldCell == null) return;

            //draw tiles (and if no edit mode, select box)
            foreach(var kx in this.WorldCell.TilesLayer1.Keys)
            {
                foreach(var ky in this.WorldCell.TilesLayer1[kx].Keys)
                {
                    Int32? tileKey = null;
                    Rectangle rect = new Rectangle(kx * TileSize, ky * TileSize, TileSize, TileSize);

                    tileKey  = this.WorldCell.TilesLayer1[kx][ky];
                    
                    if (tileKey != null)
                    {
                        TileSetDefinition tile = this.Session.TileSets[tileKey.Value];
                        Image image = GetTileImage(tile);
                        
                        e.Graphics.DrawImage(image, rect);
                    }
                    else
                    {
                        e.Graphics.FillRectangle(defaultBrush, rect);
                    }

                    tileKey = this.WorldCell.TilesLayer2[kx][ky];
                    if (tileKey != null)
                    {
                        TileSetDefinition tile = this.Session.TileSets[tileKey.Value];
                        Image image = GetTileImage(tile);
                        
                        e.Graphics.DrawImage(image, rect);
                    }

                    if ((this.EditMode == null) || (this.EditMode == WorldForm.EditMode.Door) || (this.EditMode == WorldForm.EditMode.NPC) || this.EditMode == WorldForm.EditMode.Enemy)
                    {
                        if ((kx == this.SelectedTileX) && (ky == this.SelectedTileY))
                        {
                            rect = new Rectangle(kx * TileSize, ky * TileSize, TileSize - 1, TileSize - 1);
                            e.Graphics.DrawRectangle(selectedPen, rect);
                        }
                    }
                }
            }

            //draw doors (if in door edit mode)
            if(this.EditMode == WorldForm.EditMode.Door)
            {
                foreach(DoorDefinition door in this.WorldCell.Doors)
                {
                    Rectangle rect = new Rectangle(door.X * TileSize, door.Y * TileSize, TileSize, TileSize);
                    e.Graphics.FillRectangle(doorBrush, rect);
                }
            }

            //draw npcs (if in npc edit mode)
            if(this.EditMode == WorldForm.EditMode.NPC)
            {
                foreach(NonPlayerCharacterDefinition npc in this.WorldCell.NPCs)
                {
                    Image image = GetNPCImage(npc);

                    Rectangle rect = new Rectangle(npc.TileX * TileSize, npc.TileY * TileSize, TileSize, TileSize);
                    e.Graphics.FillRectangle(npcBrush, rect);
                    e.Graphics.DrawImage(image, rect);
                }
            }

            //draw enemies (if in enemy edit mode)
            if (this.EditMode == WorldForm.EditMode.Enemy)
            {
                foreach (EnemyDefinitionPlacement enemy in this.WorldCell.EnemyDefinitionPlacements)
                {
                    Image image = GetEnemyImage(enemy);

                    Rectangle rect = new Rectangle(enemy.TileX * TileSize, enemy.TileY * TileSize, TileSize, TileSize);
                    e.Graphics.FillRectangle(npcBrush, rect);
                    e.Graphics.DrawImage(image, rect);
                }
            }
        }


        protected override void OnMouseMove(MouseEventArgs me)
        {
            base.OnMouseMove(me);

            ApplyMouse(me);
        }


        protected override void OnMouseDown(MouseEventArgs me)
        {
            base.OnMouseDown(me);

            ApplyMouse(me);
        }


        private void ApplyMouse(MouseEventArgs me)
        {
            if ((me.Button == MouseButtons.Left) || (me.Button == MouseButtons.Right))
            {
                Int32 tx = (me.X + this.HorizontalScroll.Value) / this.TileSize;
                Int32 ty = (me.Y + this.VerticalScroll.Value) / this.TileSize;

                if (EditMode == WorldForm.EditMode.Tile)
                {
                    TileSetDefinition tile = this.TileSetSelectPanel.SelectedTileSet;
                    if (tile == null) return;
                    if (this.WorldCell.TilesLayer1.ContainsKey(tx) == false) return;
                    if (this.WorldCell.TilesLayer1[tx].ContainsKey(ty) == false) return;

                    if (Control.ModifierKeys == Keys.Control)
                    {
                        if (me.Button == MouseButtons.Left) this.WorldCell.TilesLayer1[tx][ty] = null;
                        else if (me.Button == MouseButtons.Right) this.WorldCell.TilesLayer2[tx][ty] = null;
                    }
                    else if (Control.ModifierKeys == Keys.Shift)
                    {
                        if (me.Button == MouseButtons.Left)
                        {
                            for (int x = 0; x < 22; x++)
                            {
                                for (int y = 0; y < 11; y++)
                                {
                                    this.WorldCell.TilesLayer1[x][y] = tile.Key;
                                }
                            }
                        }
                        else
                        {
                            for (int x = 0; x < 22; x++)
                            {
                                for (int y = 0; y < 11; y++)
                                {
                                    this.WorldCell.TilesLayer2[x][y] = tile.Key;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (me.Button == MouseButtons.Left) this.WorldCell.TilesLayer1[tx][ty] = tile.Key;
                        else this.WorldCell.TilesLayer2[tx][ty] = tile.Key;
                    }
                }
                else if ((EditMode == WorldForm.EditMode.Door) || (EditMode == WorldForm.EditMode.NPC) || EditMode == WorldForm.EditMode.Enemy)
                {
                    this.SelectedTileX = tx;
                    this.SelectedTileY = ty;
                }
                else if (EditMode == null)
                {
                    this.SelectedTileX = tx;
                    this.SelectedTileY = ty;
                }

                this.Refresh();
            }
        }


        private Image GetTileImage(TileSetDefinition tile)
        {
            Image textureImage = null;
            Image tileImage = null;

            //get texture
            if(cacheTextureImages.ContainsKey(tile.TextureKey) == false)
            {
                Image image = Image.FromFile(RawResourceHelper.GetFilePathForTexture(this.Session.WorkingDirectory, this.Session.Textures[tile.TextureKey].FilePath));
                cacheTextureImages.Add(tile.TextureKey, image);
            }
            textureImage = cacheTextureImages[tile.TextureKey];

            //get tile
            if (cacheTileImages.ContainsKey(tile.Key) == false)
            {
                cacheTileImages.Add(tile.Key, GetImageSubset(textureImage, new Rectangle(tile.X, tile.Y, tile.Width, tile.Height)));
            }
            tileImage = cacheTileImages[tile.Key];

            return tileImage;
        }


        private Image GetNPCImage(NonPlayerCharacterDefinition npc)
        {
            Image textureImage = null;
            Image charImage = null;

            Int32 textureKey = -1;
            foreach(CharacterSetDefinition charset in this.Session.CharacterSets)
            {
                if(charset.Key == npc.CharacterSetKey)
                {
                    textureKey = charset.TextureKey;
                }
            }

            //get texture
            if (cacheTextureImages.ContainsKey(textureKey) == false)
            {
                Image image = Image.FromFile(RawResourceHelper.GetFilePathForTexture(this.Session.WorkingDirectory, this.Session.Textures[textureKey].FilePath));
                cacheTextureImages.Add(textureKey, image);
            }
            textureImage = cacheTextureImages[textureKey];

            //get char
            if (cacheCharacterImages.ContainsKey(npc.CharacterSetKey) == false)
            {
                cacheCharacterImages.Add(npc.CharacterSetKey, GetImageSubset(textureImage, new Rectangle(32, 48, 32, 48)));
            }
            charImage = cacheCharacterImages[npc.CharacterSetKey];

            return charImage;
        }


        private Image GetEnemyImage(EnemyDefinitionPlacement enemyPlacement)
        {
            Image textureImage = null;
            Image charImage = null;

            EnemyDefinition enemyDefinition = null;

            Int32 textureKey = -1;
            foreach(EnemyDefinition enemy in this.Session.Enemies)
            {
                if(enemy.Key == enemyPlacement.Key)
                {
                    enemyDefinition = enemy;

                    foreach (CharacterSetDefinition charset in this.Session.CharacterSets)
                    {
                        if (charset.Key == enemy.CharacterSetKey)
                        {
                            textureKey = charset.TextureKey;
                        }
                    }
                }
            }

            //get texture
            if (cacheTextureImages.ContainsKey(textureKey) == false)
            {
                Image image = Image.FromFile(RawResourceHelper.GetFilePathForTexture(this.Session.WorkingDirectory, this.Session.Textures[textureKey].FilePath));
                cacheTextureImages.Add(textureKey, image);
            }
            textureImage = cacheTextureImages[textureKey];

            //get char
            if (cacheCharacterImages.ContainsKey(enemyDefinition.CharacterSetKey) == false)
            {
                cacheCharacterImages.Add(enemyDefinition.CharacterSetKey, GetImageSubset(textureImage, new Rectangle(32, 48, 32, 48)));
            }
            charImage = cacheCharacterImages[enemyDefinition.CharacterSetKey];

            return charImage;
        }


        private Image GetImageSubset(Image image, Rectangle rect)
        {
            Bitmap newImage = new Bitmap(rect.Width, rect.Height);
            Graphics g = Graphics.FromImage(newImage);

            g.DrawImage(image, 0, 0, rect, GraphicsUnit.Pixel);

            return newImage;
        }


        public DoorDefinition GetSelectedDoor()
        {
            if (this.WorldCell == null) return null;

            DoorDefinition door = null;
            Int32? x = this.SelectedTileX;
            Int32? y = this.SelectedTileY;

            if ((x != null) && (y != null))
            {
                foreach (DoorDefinition doorDefinition in this.WorldCell.Doors)
                {
                    if ((doorDefinition.X == x) && (doorDefinition.Y == y))
                    {
                        door = doorDefinition;
                    }
                }
            }

            return door;
        }


        public NonPlayerCharacterDefinition GetSelectedNPC()
        {
            NonPlayerCharacterDefinition npc = null;
            Int32? x = this.SelectedTileX;
            Int32? y = this.SelectedTileY;

            if ((x != null) && (y != null))
            {
                foreach(NonPlayerCharacterDefinition nonPlayerCharacterDefinition in this.WorldCell.NPCs)
                {
                    if((nonPlayerCharacterDefinition.TileX == x) && (nonPlayerCharacterDefinition.TileY == y))
                    {
                        npc = nonPlayerCharacterDefinition;
                    }
                }
            }

            return npc;
        }


        public EnemyDefinitionPlacement GetSelectedEnemyPlacement()
        {
            EnemyDefinitionPlacement enemy = null;
            Int32? x = this.SelectedTileX;
            Int32? y = this.SelectedTileY;

            if ((x != null) && (y != null))
            {
                foreach (EnemyDefinitionPlacement enenmyPlacement in this.WorldCell.EnemyDefinitionPlacements)
                {
                    if ((enenmyPlacement.TileX == x) && (enenmyPlacement.TileY == y))
                    {
                        enemy = enenmyPlacement;
                    }
                }
            }

            return enemy;
        }


    }
}
