using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

namespace BilaPani
{
    public partial class MainForm : Form
    {

        private List<List<MapField>> mapa = new List<List<MapField>>();
        private List<MapField> FoundPath = new List<MapField>();
        private List<MapField> Blacklist = new List<MapField>();
        private int MapWidth = 0;
        private int MapHeight = 0;

        private MapField TeleField = new MapField('s', 90, 90);
        private MapField CurField = new MapField('s', 90, 90);

        private List<Zved> Zvedove = new List<Zved>();

        public MainForm()
        {
            InitializeComponent();
        }

        private void inputFileDialogButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.FileName = inputFilePathBox.Text;
            ofd.CheckFileExists = true;
            ofd.CheckPathExists = true;

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                inputFilePathBox.Text = ofd.FileName;    
            }
        }

        private void loadInputFile_Click(object sender, EventArgs e)
        {
            if (!File.Exists(inputFilePathBox.Text))
            {
                MessageBox.Show("Zadaná cesta k souboru je neplatná.", "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            inputFilePathBox.Enabled = false;
            inputFileDialogButton.Enabled = false;
            loadInputFile.Enabled = false;
            krokovac.Enabled = false;

            mapa.Clear();
            FoundPath.Clear();
            Blacklist.Clear();

            StreamReader reader = new StreamReader(inputFilePathBox.Text);

            bool isFirst = true;
            int lineNum = -1;

            while (!reader.EndOfStream)
            {
                lineNum++;

                string inputLine = reader.ReadLine();

                if (isFirst)
                {
                    isFirst = false;

                    string buffer = "";

                    foreach (char c in inputLine)
                    {
                        if (Char.IsDigit(c))
                        {
                            buffer += c;
                        }
                        else
                        {
                            MapWidth = Int32.Parse(buffer) - 1;
                            buffer = "";
                        }
                    }

                    if (MapHeight == 0)
                    {
                        MapHeight = Int32.Parse(buffer) - 1;
                    }
                }
                else
                {
                    List<MapField> buffer = new List<MapField>();
                    List<char> movementsBuffer = new List<char>();
                    bool parsingMovements = false;
                    int x = 1;

                    foreach (char c in inputLine)
                    {
                        
                        if (c == '&' || c == 'X' || c == '.' || c == '#')
                        {
                            buffer.Add(new MapField(c, x, lineNum));

                            if (c == '#')
                            {
                                TeleField = new MapField(c, x, lineNum); 
                            }
                            else if (c == '&')
                            {
                                CurField = new MapField(c, x, lineNum);
                            }

                            x++;
                        }
                        else if (c == '@')
                        {
                            buffer.Add(new MapField(c, x, lineNum));
                            parsingMovements = true;
                        }
                        else if (parsingMovements)
                        {
                            if (c == '<' || c == '>' || c == '^' || c == 'v')
                            {
                                movementsBuffer.Add(c);
                            }
                            else
                            {
                                if (movementsBuffer.Count < 2)
                                {
                                    throw new ApplicationException();
                                }

                                Zved z = new Zved(x, lineNum, movementsBuffer);
                                Zvedove.Add(z);


                                x++;
                                parsingMovements = false;
                            }
                        }
                        else
                        {
                            throw new Exception("Unknown map character");
                        }
                    }

                    if (parsingMovements)
                    {
                        if (movementsBuffer.Count < 2)
                        {
                            throw new ApplicationException("Not enough movements in buffer");
                        }

                        Zved z = new Zved(x, lineNum, movementsBuffer);
                        Zvedove.Add(z);

                    }

                    mapa.Add(buffer);
                }                
            }

            RenderMap(drawingCanvas.CreateGraphics());
        }

        private void RenderMap(Graphics g)
        {
            int fieldHeight = 20;
            int fieldWidth = 20;

            int X = 0;
            int Y = 0;

            g.Clear(Color.White);

            foreach (List<MapField> mapRow in mapa)
            {
                X = 0;

                for (int i = 0; i <= MapWidth; i++)
                {
                    Bitmap b = new Bitmap(20, 20);

                    switch (mapRow[i].Type)
                    {
                        case '&':
                            b = BilaPani.Properties.Resources.pani;
                            break;
                        case '#':
                            b = BilaPani.Properties.Resources.televize;
                            break;
                        case '.':
                            b = BilaPani.Properties.Resources.dlazdice;
                            break;
                        case '@':
                            b = BilaPani.Properties.Resources.zved;
                            break;
                        case 'X':
                            b = BilaPani.Properties.Resources.zed;
                            break;
                    }

                    g.DrawImage(b, X, Y, fieldWidth, fieldHeight);
                    X += fieldWidth;
                }

                Y += fieldHeight;
            }
 
            SearchForWay();

            if (FoundPath.Count == 0)
            {
                MessageBox.Show("Nebyla nalezena žádná cesta do 2500 kroků.", "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                List<MapField> newHistory = new List<MapField>();

                MapField last = new MapField('x', 10, 10);

                foreach (MapField m in FoundPath)
                {
                    if (m != last)
                    {
                        newHistory.Add(m);
                        last = m;
                    }
                }

                MessageBox.Show("Byla nalezena cesta z původní lokace k televizi, o počtu kroků " + FoundPath.Count, "Informace", MessageBoxButtons.OK, MessageBoxIcon.Information);

                foreach (MapField m in FoundPath)
                {
                    g.FillRectangle(Brushes.Green, ((m.X) * 20) + 5, ((m.Y) * 20) + 5, 10, 10);
                }

                /*inputFilePathBox.Enabled = true;
                inputFileDialogButton.Enabled = true;
                loadInputFile.Enabled = true;*/
                krokovac.Maximum = FoundPath.Count;
                krokovac.Value = 1;
                krokovac.Enabled = true;

            }
        }

        public void SearchForWay()
        {
            List<MapField> history = new List<MapField>();

            if (TeleField.X == 90)
            {
                throw new Exception("Television not set");
            }

            foreach (List<MapField> mapRow in mapa)
            {
                foreach (MapField mapItem in mapRow)
                {
                    mapItem.CalcVal(TeleField);
                }
            }

            // Performance limit to not run forever but only for 1000 moves at max.

            for (int move = 0; move < 2500; move++)
            {
                List<MapField> Neighbours = new List<MapField>(4);

                if (move == 0 && CurField.Type != '&')
                {
                    throw new ApplicationException("Chyba @279");
                }

                

                if (CurField.Y > 0 && mapa[CurField.Y - 1][CurField.X].Type != 'X') // 2
                {
                    Neighbours.Add(mapa[CurField.Y - 1][CurField.X]);
                }

                if (CurField.Y < MapHeight && mapa[CurField.Y + 1][CurField.X].Type != 'X') // 1
                {
                    Neighbours.Add(mapa[CurField.Y + 1][CurField.X]);
                }

                if (CurField.X < MapWidth && mapa[CurField.Y][CurField.X + 1].Type != 'X')
                {
                    Neighbours.Add(mapa[CurField.Y][CurField.X + 1]);
                }

                if (CurField.X > 0 && mapa[CurField.Y][CurField.X - 1].Type != 'X')
                {
                    Neighbours.Add(mapa[CurField.Y][CurField.X - 1]);
                }

                MapField bestNeighbour = new MapField('X', 0, 0);
                int LowestVal = 999;

                foreach (MapField Neighbour in Neighbours)
                {
                    if (Neighbour.Val < LowestVal)
                    {
                        if (Neighbour.Type == '#')
                        {
                            history.Add(Neighbour);
                            FoundPath = history;
                            return;
                        }


                        if (history.Count == 0 || history[history.Count - 1] != Neighbour)
                        {
                            if (history.Count > 1)
                            {
                                if (history[history.Count - 2] != Neighbour)
                                {
                                    if (Blacklist.Contains(Neighbour) == false)
                                    {
                                        bestNeighbour = Neighbour;
                                        LowestVal = Neighbour.Val;
                                    }
                                }
                            }
                            else
                            {
                                if (Blacklist.Contains(Neighbour) == false)
                                {
                                    bestNeighbour = Neighbour;
                                    LowestVal = Neighbour.Val;
                                }
                            }
                        }
                    }
                }


                if (LowestVal == 999)
                {
                    Blacklist.Add(CurField);

                    // stay on current field which is now blacklisted!

                    bestNeighbour = CurField;
                }

                history.Add(bestNeighbour);

                CurField = bestNeighbour;

            }


            
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Bitmap b = new Bitmap(1600, 1600);
            drawingCanvas.DrawToBitmap(b, new Rectangle(0,0,1600,1600));
            b.Save(@"D:\test.bmp");
        }

        private void krokovac_ValueChanged(object sender, EventArgs e)
        {
            Graphics g = drawingCanvas.CreateGraphics();
            int index = 1;
            int val = (int)krokovac.Value;

            foreach (MapField m in FoundPath)
            {
                if (index < val)
                {
                    g.FillRectangle(Brushes.Orange, ((m.X) * 20) + 5, ((m.Y) * 20) + 5, 10, 10);
                }
                else
                {
                    g.FillRectangle(Brushes.Green, ((m.X) * 20) + 5, ((m.Y) * 20) + 5, 10, 10);
                }

                index++;
            }

        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            //MainForm.ActiveForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
            MainForm.ActiveForm.SetBounds(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
        }

    }
}