using System.Linq;
using System.Windows.Media;
using System.Xml;
using Machinations;
using Machinations.FromFlash.graph;
using Machinations.FromFlash.machinations.model;
using NUnit.Framework;

namespace MachinationsTests
{
    public class TestGraph
    {
        [Test]
        public void TestSourceToPool()
        {
            var graph = CreateSourcePoolGraph();
            Pool pool = (Pool) graph.Elements[1];
            Assert.That(pool.ResourceCount == 0);
            graph.Running = true;
            graph.OneTurnWithoutUserActions();
            Assert.That(pool.ResourceCount == 1);
        }

        [Test]
        public void TestStateConnectionIncrement()
        {
            var graph = CreateSourcePoolGraph();
            Pool pool = (Pool)graph.Elements[1];
            graph.CreateStateConnection(pool, graph.Elements[2], ColorsUtils.InitColor);
            //stateConection.Label.Text = "+1";
            graph.Running = true;
            graph.OneTurnWithoutUserActions();
            Assert.That(pool.ResourceCount == 1);
            graph.OneTurnWithoutUserActions();
            Assert.That(pool.ResourceCount == 3);
            graph.OneTurnWithoutUserActions();
            Assert.That(pool.ResourceCount == 7);
        }

        [Test]
        public void TestConnectionInhibition()
        {
            var graph = CreateSourcePoolGraph();
            Pool pool = (Pool)graph.Elements[1];
            var stateConection = graph.CreateStateConnection(pool, graph.Elements[2], ColorsUtils.InitColor);
            stateConection.Label.Text = "==0";
            graph.Running = true;
            graph.OneTurnWithoutUserActions();
            Assert.That(pool.ResourceCount == 1);
            graph.OneTurnWithoutUserActions();
            Assert.That(pool.ResourceCount == 1);
        }

        [Test]
        public void OverrideGraphTest()
        {
            var graph = CreateSourcePoolGraph();
            int originalElementsCount = graph.Elements.Count;
            var document = graph.GenerateXml();
            ProgramControlUtils.LoadGraph(true, graph, document.DocumentElement);
            Assert.That(graph.Elements.Count == originalElementsCount);
        }

        [Test]
        public void TestStartNode()
        {
            var graph = CreateSourcePoolGraph();
            var source = graph.Elements.OfType<MachinationsNode>().Single(e => e.NodeClass == NodeClass.Source);
            source.ActivationMode = ActivationMode.OnStart;
            Pool pool = (Pool)graph.Elements.OfType<MachinationsNode>().Single(e => e.NodeClass == NodeClass.Pool);
            Assert.That(pool.Resources.Count == 0);
            graph.Running = true;
            Assert.That(pool.Resources.Count == 1);
            graph.OneTurn();
            Assert.That(pool.Resources.Count == 1);
        }

        [Test]
        public void AddGraphTest()
        {
            var graph = CreateSourcePoolGraph();
            int originalElementsCount = graph.Elements.Count;
            var document = graph.GenerateXml();
            ProgramControlUtils.LoadGraph(false, graph, document.DocumentElement);
            Assert.That(graph.Elements.Count == 2*originalElementsCount);
        }

        [Test]
        public void TestAllComponents()
        {
            MachinationsGraph graph = new MachinationsGraph();
            graph.AddElement(new Pool());
            graph.AddElement(new Gate());
            graph.AddElement(new Source());
            graph.AddElement(new Drain());
            graph.CreateResourceConnection(graph.Elements[2], graph.Elements[3], ColorsUtils.InitColor);
            graph.CreateStateConnection(graph.Elements[0], graph.Elements[4], ColorsUtils.InitColor);
            graph.AddElement(new Converter());
            graph.AddElement(new Trader());
            graph.AddElement(new Delay());
            graph.AddElement(new EndCondition());

            var document = graph.GenerateXml();
            MachinationsGraph graph2 = LoadGraph(document);
            Assert.That(graph2.Elements.Count == 10);
            Assert.That(graph2.Elements[0] is Pool);
            Assert.That(graph2.Elements[1] is Gate);
            Assert.That(graph2.Elements[2] is Source);
            Assert.That(graph2.Elements[3] is Drain);
            Assert.That(graph2.Elements[4] is ResourceConnection);
            Assert.That(graph2.Elements[5] is StateConnection);
            Assert.That(graph2.Elements[6] is Converter);
            Assert.That(graph2.Elements[7] is Trader);
            Assert.That(graph2.Elements[8] is Delay);
            Assert.That(graph2.Elements[9] is EndCondition);
            Assert.That(graph2.Elements[0].Outputs.Count() == 1);
            Assert.That(graph2.Elements[2].Outputs.Count() == 1);
            Assert.That(graph2.Elements[3].Inputs.Count() == 1);
            Assert.That(graph2.Elements[4].Inputs.Count() == 1);
            ResourceConnection resourceConnection = (ResourceConnection) graph2.Elements[4];
            Assert.That(resourceConnection.Start == graph2.Elements[2]);
            Assert.That(resourceConnection.End == graph2.Elements[3]);
            var stateConnection = (StateConnection)graph2.Elements[5];
            Assert.That(stateConnection.Start == graph2.Elements[0]);
            Assert.That(stateConnection.End == graph2.Elements[4]);
        }

        [Test]
        public void SaveLoadTest()
        {
            var graph = CreateSourcePoolGraph();
            var document = graph.GenerateXml();
            //string p = Path.GetFullPath("SaveLoadTest.xml");
            document.Save("SaveLoadTest.xml");
            XmlDocument loaded = new XmlDocument();
            loaded.Load("SaveLoadTest.xml");
            new MachinationsGraph();
            Assert.That(XMLCompare(document.DocumentElement, loaded.DocumentElement));
        }


        private static MachinationsGraph CreateSourcePoolGraph()
        {
            MachinationsGraph graph = new MachinationsGraph();
            Source source = new Source();
            graph.AddElement(source);
            Pool pool = new Pool();
            graph.AddElement(pool);
            graph.CreateResourceConnection(source, pool, ColorsUtils.InitColor);
            return graph;
        }

        private bool XMLCompare(XmlElement primary, XmlElement secondary)
        {
            if (primary.HasAttributes)
            {
                if (primary.Attributes.Count != secondary.Attributes.Count)
                    return false;
                foreach (XmlAttribute attr in primary.Attributes)
                {
                    if (secondary.Attributes[attr.Name] == null)
                        return false;
                    if (attr.Value.ToLower() != secondary.Attributes[attr.Name].Value.ToLower())
                        return false;
                }
            }
            if (primary.HasChildNodes)
            {
                if (primary.ChildNodes.Count != secondary.ChildNodes.Count)
                    return false;
                for (int i = 0; i <= primary.ChildNodes.Count - 1; i++)
                {
                    if (XMLCompare((XmlElement)primary.ChildNodes[i], (XmlElement)secondary.ChildNodes[i]) == false)
                        return false;
                }
            }
            return true;
        }

        private MachinationsGraph LoadGraph(XmlDocument document)
        {
            MachinationsGraph graph = new MachinationsGraph();
            ProgramControlUtils.LoadGraph(true, graph, document.DocumentElement);
            return graph;
        }
    }
}
