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

namespace MachinationsTests
{
    public class UserComponentsTests
    {

        [Test]
        [TestCase(UserComponentType.UserComponent)]
        public void TestInputComponentCreation(UserComponentType type)
        {
            var graph = CreateGraph1(type);
            CheckIfGraphZipped(graph);
        }

        [Test]
        [TestCase(UserComponentType.UserComponent)]
        public void TestInputComponentZipping(UserComponentType type)
        {
            var graph = CreateGraph1(type);
            var component = graph.UserComponents.Single();
            component.UnZip();
            CheckIfGraphUnzipped(graph);
            CheckIfComponentUnZipped(component);
            component.Zip();
            CheckIfGraphZipped(graph);
            CheckIfComponentZipped(component);
        }

        [Test]
        [TestCase(UserComponentType.UserComponent)]
        public void TestInputOutputComponentCreation(UserComponentType type)
        {
            var graph = CreateGraph2(type);
            CheckIfGraph2Zipped(graph);
        }

        [Test]
        [TestCase(UserComponentType.UserComponent)]
        public void TestInputOutputComponentZipping(UserComponentType type)
        {
            var graph = CreateGraph2(type);
            var component = graph.UserComponents.Single();
            component.UnZip();
            CheckIfGraph2Unzipped(graph);
            //CheckIfComponent2UnZipped(component);
            component.Zip();
            CheckIfGraph2Zipped(graph);

            graph.Running = true;
            CheckIfGraph2Unzipped(graph);
            graph.Running = false;
            CheckIfGraph2Zipped(graph);
            //CheckIfComponent2Zipped(component);
        }

        [Test]
        [TestCase(UserComponentType.UserComponent)]
        public void TestUserComponentsSaveLoad(UserComponentType type)
        {
            var component = CreateInputOutputComponent(type);
            XmlDocument document = new XmlDocument();
            var element = component.GenerateXmlTemplate(document);
            IUserComponentSchema component2 = CreateUserComponentSchema(type, element);

            Assert.That(component2.GetInstance(0).IsZipped);
            Assert.That(component2.GetInstance(0).PresentedElements.Count().Equals(component.GetInstance(0).PresentedElements.Count()));
            Assert.That(component2.GetInstance(0).InternalGraph.Elements.Count.Equals(component.GetInstance(0).InternalGraph.Elements.Count));
            for (int i = 0; i < component2.GetInstance(0).InternalGraph.Elements.Count; i++)
            {
                Assert.That(
                    component2.GetInstance(0).InternalGraph.Elements[i].Description.Equals(
                        component.GetInstance(0).InternalGraph.Elements[i].Description));
            }
        }

        private static IUserComponentSchema CreateUserComponentSchema(UserComponentType type, XmlElement element = null)
        {
            IUserComponentSchema component2;
            switch (type)
            {
                case UserComponentType.UserComponent:
                    return element == null ? new UserComponent() : new UserComponent(element);

                default:throw new NotImplementedException();
            }
            return component2;
        }

        [Test]
        [TestCase(UserComponentType.UserComponent)]
        public void TestComponentConnection(UserComponentType type)
        {
            var graph = new MachinationsGraph();
            var source1 = CreateSourceComponent(type);
            var source2 = CreateSourceComponent(type);
            var pipe = CreateInputOutputComponent(type);
            pipe.GetInstance(0).InternalGraph.Elements.OfType<ResourceConnection>().ToList().ForEach(c => c.Label.Text = "2");
            pipe.GetInstance(0).AppendToGraph(graph);
            source1.GetInstance(0).AppendToGraph(graph);
            source2.GetInstance(0).AppendToGraph(graph);
            graph.CreateResourceConnection(source1.GetInstance(0).OuterInOuts.Single(), pipe.GetInstance(0).OuterInOuts.Single(io => io.Input), ColorsUtils.InitColor);
            graph.CreateResourceConnection(source2.GetInstance(0).OuterInOuts.Single(), pipe.GetInstance(0).OuterInOuts.Single(io => io.Input), ColorsUtils.InitColor);
            var pool = new Pool {ActivationMode = ActivationMode.Automatic};
            graph.CreateResourceConnection(pipe.GetInstance(0).OuterInOuts.Single(io => !io.Input), pool, ColorsUtils.InitColor, "2");
            graph.AddElement(pool);
            graph.Running = true;
            graph.MoreTurns(2);
            Assert.That(pool.Resources.Count == 2);
            graph.Running = false;
        }
        

        private void CheckIfGraph2Unzipped(MachinationsGraph graph)
        {
            Assert.That(graph.Elements.Count == 6);
            var source1 = graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.Source);
            var connection1 = source1.Outputs.Single();
            Assert.That(connection1.Start == source1);
            var pool1 = (Pool)connection1.End;
            Assert.That(pool1.Inputs.Single() == connection1);
            Assert.That(graph.Elements.Contains(pool1));
            Assert.That(pool1.Graph == graph);
            var connection2 = pool1.Outputs.Single();
            var pool2 = (Pool)connection2.End;
            Assert.That(pool2.Inputs.Single() == connection2);
        }

        private static void CheckIfGraph2Zipped(MachinationsGraph graph)
        {
            Assert.That(graph.Elements.Count == 7);
            var source1 = graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.Source);
            var connection1 = source1.Outputs.Single();
            var inOut1 = (OuterInOut) connection1.End;
            Assert.That(inOut1.OuterConnections.Count == 1);
            Assert.That(inOut1.OuterConnections.Single() == connection1);
            //Assert.That(inOut1.InnerConnections.Count == 1);
            //Assert.That(inOut1.MergedConnections.Count() == 1);

            var pool1 = graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.Pool);
            var connection2 = pool1.Inputs.Single();
            var inOut2 = (OuterInOut) connection2.Start;
            Assert.That(inOut2.OuterConnections.Count == 1);
            Assert.That(inOut2.OuterConnections.Single() == connection2);
            //Assert.That(inOut2.InnerConnections.Count == 1);
            //Assert.That(inOut2.MergedConnections.Count() == 1);
        }




        private static void CheckIfGraphZipped(MachinationsGraph graph)
        {
            Assert.That(graph.Elements.Count == 4);
            var source1 = graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.Source);
            var connection1 = (ResourceConnection) graph.Elements.Single(e => e is ResourceConnection);
            var inOut1 = (OuterInOut)graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.OuterInOut);
            Assert.That(inOut1.OuterConnections.Single() == connection1);
            Assert.That(source1.Outputs.Single() == connection1);
            Assert.That(connection1.Start == source1 && connection1.End == inOut1);
            IUserComponentSchema userComponent = graph.Elements.OfType<IUserComponentSchema>().Single();
            Assert.That(userComponent.InnerInOuts.Single().InnerConnections.Count == 1);
            //userComponent.UnZip();
            //Assert.That(userComponent.InAndOuts.Single().MergedConnections.Count() == 1);
        }

        private static void CheckIfGraphUnzipped(MachinationsGraph graph)
        {
            Assert.That(graph.Elements.Count == 4);
            var source1 = graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.Source);
            var connection1 = (ResourceConnection) graph.Elements.Single(e => e is ResourceConnection);
            var pool1 = graph.Elements.OfType<GraphNode>().Single(e => e.NodeClass == NodeClass.Pool);
            Assert.That(pool1.Inputs.Single() == connection1);
            Assert.That(source1.Outputs.Single() == connection1);
            Assert.That(connection1.Start == source1 && connection1.End == pool1);
        }

        private void CheckIfComponentZipped(IUserComponentSchema component)
        {
            var pool = component.GetInstance(0).InternalGraph.Elements.OfType<GraphNode>().Single(n => n.NodeClass == NodeClass.Pool);
            Assert.That(pool.Graph == component.GetInstance(0).InternalGraph);
            Assert.That(pool.Inputs.Single().Start is InnerInOut);
        }

        private void CheckIfComponentUnZipped(IUserComponentSchema component)
        {
            var pool = component.GetInstance(0).InternalGraph.Elements.OfType<GraphNode>().Single(n => n.NodeClass == NodeClass.Pool);
            Assert.That(pool.Graph == component.Graph);
            Assert.That(pool.Inputs.Single().Start is Source);
        }


        private MachinationsGraph CreateGraph1(UserComponentType type)
        {
            var uc = Create1InputComponent(type);
            Assert.That(uc.GetInstance(0).OuterInOuts.Count(io => io.Input) == 1);
            Assert.That(uc.GetInstance(0).IsZipped);
            MachinationsGraph graph = new MachinationsGraph();
            uc.GetInstance(0).AppendToGraph(graph);
            Assert.That(uc.GetInstance(0).IsZipped);
            Assert.That(Equals(graph.UserComponents.Single(), uc));
            var source = new Source();
            graph.AddElement(source);
            graph.CreateResourceConnection(source, uc.GetInstance(0).OuterInOuts.First(io => io.Input), ColorsUtils.InitColor);
            return graph;
        }

        private MachinationsGraph CreateGraph2(UserComponentType type)
        {
            var uc = CreateInputOutputComponent(type);
            Assert.That(uc.InnerInOuts.Count(io => io.Input) == 1);
            Assert.That(uc.InnerInOuts.Count(io => !io.Input) == 1);
            Assert.That(uc.GetInstance(0).IsZipped);
            MachinationsGraph graph = new MachinationsGraph();
            uc.GetInstance(0).AppendToGraph(graph);
            Assert.That(uc.GetInstance(0).IsZipped);
            Assert.That(graph.UserComponents.Single() == uc);
            var source = new Source();
            graph.AddElement(source);
            graph.CreateResourceConnection(source, uc.GetInstance(0).OuterInOuts.First(io => io.Input), ColorsUtils.InitColor);
            var pool = new Pool();
            graph.AddElement(pool);
            graph.CreateResourceConnection(uc.GetInstance(0).OuterInOuts.Single(io => !io.Input), pool, ColorsUtils.InitColor);
            return graph;
        }

        private IUserComponentSchema Create1InputComponent(UserComponentType type)
        {
            IUserComponentSchema uc = CreateUserComponentSchema(type);
            var input = uc.CreateInOut(new Point());
            var pool = new Pool();
            uc.GetInstance(0).InternalGraph.AddElement(pool);
            uc.GetInstance(0).InternalGraph.CreateResourceConnection(input, pool, ColorsUtils.InitColor);
            uc.UpdateOuters();
            return uc;
        }

        private IUserComponentSchema CreateInputOutputComponent(UserComponentType type)
        {
            IUserComponentSchema uc = Create1InputComponent(type);
            var output = uc.CreateInOut(new Point());
            uc.UpdateOuters();
            var pool = uc.GetInstance(0).InternalGraph.Elements.OfType<Pool>().Single();
            uc.GetInstance(0).InternalGraph.CreateResourceConnection(pool, output, ColorsUtils.InitColor);
            return uc;
        }

        private IUserComponentSchema CreateSourceComponent(UserComponentType type)
        {
            IUserComponentSchema uc = CreateUserComponentSchema(type);
            var input = uc.CreateInOut(new Point());
            uc.UpdateOuters();
            var source = new Source();
            uc.GetInstance(0).InternalGraph.AddElement(source);
            uc.GetInstance(0).InternalGraph.CreateResourceConnection(source, input, ColorsUtils.InitColor);
            return uc;
        }
    }
}
