using System;
using System.Collections.Generic;
using System.Linq;
using Machinations.FromFlash.graph;
using Machinations.FromFlash.machinations.model;

namespace Machinations.UserComponents
{
    public class MergedInOut: IInOut
    {
        private readonly InnerInOut m_innerInOut;
        private readonly OuterInOut m_outerInOut;
        private readonly Dictionary<MergedConnectionKey, MachinationsConnection>
            m_mergedConnections;


        public MergedInOut(InnerInOut innerInOut, OuterInOut outerInOut)
        {
            m_innerInOut = innerInOut;
            m_outerInOut = outerInOut;
            m_mergedConnections = new Dictionary<MergedConnectionKey, MachinationsConnection>();
            OuterConnections.ForEach(oc =>
            {
                InnerConnections.ForEach(ic =>
                {
                    var connectionKey = new MergedConnectionKey(ic, oc);
                    var mergedConnection = GraphConnectionType.GetConnectionFromClass(m_innerInOut.ConClass, connectionKey);
                    if (Input)
                    {
                        mergedConnection.Start = oc.Start;
                        mergedConnection.End = ic.End;
                    }
                    else
                    {
                        mergedConnection.Start = ic.Start;
                        mergedConnection.End = oc.End;
                    }
                    mergedConnection.Label = ic.Label;
                    
                    m_mergedConnections.Add(connectionKey, mergedConnection);
                });
            });
        }

        public string Name { get { return m_innerInOut.Name; } }
        public List<MachinationsConnection> OuterConnections { get { return m_outerInOut.OuterConnections; } }
        public List<MachinationsConnection> InnerConnections { get { return m_innerInOut.InnerConnections; } }
        public IEnumerable<MachinationsConnection> MergedConnections { get { return m_mergedConnections.Values; } }
        public bool Input { get { return m_innerInOut.Input; } }
        public void Dispose()
        {
            throw new NotImplementedException();
        }

        public void ProcessConnections(bool zipped, Graph graph)
        {
            InnerConnections.ForEach(innerConnection =>
            {
                ProcessInnerConnections(innerConnection, zipped, graph);
            });
            OuterConnections.ForEach(outerConnection =>
            {
                ProcessOuterConnections(outerConnection, zipped, graph);
            });
            foreach (var mergedConnection in m_mergedConnections.Values)
            {
                ProcessMergedConnection(mergedConnection, zipped, graph);
            }
        }

        private IEnumerable<MachinationsConnection> GetMergedConnections(MachinationsConnection connection, bool isInner)
        {
            if (isInner)
                return OuterConnections.Select(oc => GetMergedConnection(connection, oc));
            return InnerConnections.Select(ic => GetMergedConnection(ic, connection));
        }

        private MachinationsConnection GetMergedConnection(MachinationsConnection innerConnection, GraphConnection outerConnection)
        {
            return m_mergedConnections[new MergedConnectionKey(innerConnection, outerConnection)];
        }

        private void ProcessInnerConnections(MachinationsConnection innerConnection, bool zipped, Graph graph)
        {
            if (zipped)
            {
                if (Input)
                {
                    if (!innerConnection.End.Inputs.Contains(innerConnection))          //pokud jsou nactene dve stejne karty v grafu
                        innerConnection.End.AddInput(innerConnection);
                }

                else
                {
                    if (!innerConnection.Start.Outputs.Contains(innerConnection))       //pokud jsou nactene dve stejne karty v grafu
                        innerConnection.Start.AddOutput(innerConnection);
                }  
                foreach (var m in GetMergedConnections(innerConnection, true))
                {
                    foreach (var input in innerConnection.Inputs.Cast<MachinationsConnection>())
                    {
                        input.Start.AddOutput(input);
                    }
                }
            }
            else
            {
                if (Input)
                    innerConnection.End.RemoveInput(innerConnection);
                else
                    innerConnection.Start.RemoveOutput(innerConnection);
                foreach (var m in GetMergedConnections(innerConnection, true))
                {
                    foreach (var input in innerConnection.Inputs.Cast<MachinationsConnection>())
                    {
                        graph.CreateStateConnection(input.Start, m, input.Color, input.Label.Text);
                        input.Start.RemoveOutput(input);
                    }
                }
            }
        }

        private void ProcessOuterConnections(MachinationsConnection outerConnection, bool zipped, IGraph graph)
        {
            if (zipped)
            {
                if (Input)
                    outerConnection.Start?.AddOutput(outerConnection);
                else
                    outerConnection.End?.AddInput(outerConnection);
                graph.AddElement(outerConnection);
                foreach (var m in GetMergedConnections(outerConnection, false))
                {
                    foreach (var input in outerConnection.Inputs.Cast<MachinationsConnection>())
                    {
                        if (!graph.Elements.Contains(input))
                        {
                            input.Start.AddOutput(input);
                            graph.AddElement(input);
                        }
                        
                    }
                }
            }
            else
            {
                if (Input)
                    outerConnection.Start?.RemoveOutput(outerConnection);
                else
                    outerConnection.End?.RemoveInput(outerConnection);
                graph.RemoveElement(outerConnection);
                foreach (var m in GetMergedConnections(outerConnection, false))
                {
                    foreach (var input in outerConnection.Inputs.Cast<MachinationsConnection>())
                    {
                        graph.CreateStateConnection(input.Start, m, input.Color, input.Label.Text);
                        input.Start.RemoveOutput(input);
                        graph.RemoveElement(input);
                    }
                }
            }
        }

        private void ProcessMergedConnection(MachinationsConnection mergedConnection, bool zipped, IGraph graph)
        {
            if (mergedConnection.End.Id == 433)
            {
                
            }
            if (zipped)
            {
                mergedConnection.Start?.RemoveOutput(mergedConnection);
                mergedConnection.End?.RemoveInput(mergedConnection);
                graph.RemoveElement(mergedConnection);
                foreach (var input in mergedConnection.Inputs.Cast<MachinationsConnection>())
                {
                    graph.RemoveElement(input);
                    input.Start.RemoveOutput(input);
                }
            }
            else
            {
                mergedConnection.Start?.AddOutput(mergedConnection);
                mergedConnection.End?.AddInput(mergedConnection);
                graph.AddElement(mergedConnection);
                if (mergedConnection is ResourceConnection)
                    ((ResourceConnection) mergedConnection).CheckInhibition(true);
                (mergedConnection.Start as MachinationsNode)?.CheckInhibition();
                (mergedConnection.End as MachinationsNode)?.CheckInhibition();
                if (mergedConnection is StateConnection)
                    ((StateConnection)mergedConnection).CheckInhibition();
            }
        }

    }
}
