using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using TransportGame.Utils;
namespace TransportGame.Model.Road
{
    [XmlRoot("roadNetwork")]
    public class RoadNetwork
    {
        private int lastNodeId = -1, lastSegmentId = -1;
        /// 
        /// Gets the road nodes
        /// 
        [XmlIgnore]
        public Dictionary Nodes { get; private set; }
        /// 
        /// Gets the road segments for the articulation graph
        /// 
        [XmlIgnore]
        public Dictionary ArticulationSegments { get; private set; }
        /// 
        /// Gets the road segments for the intersection graph
        /// 
        [XmlIgnore]
        public Dictionary IntersectionSegments { get; private set; }
        /// 
        /// Gets or sets the nodes
        /// 
        [XmlArray("nodes")]
        public RoadNode[] NodesArray
        {
            get
            {
                return Nodes.Values.ToArray();
            }
            set
            {
                Nodes.Clear();
                foreach (var node in value)
                {
                    node.ParentNetwork = this;
                    Nodes.Add(node.Id, node);
                }
            }
        }
        /// 
        /// Gets or sets the segments
        /// 
        [XmlArray("articulationGraph")]
        public RoadSegment[] ArticulationSegmentsArray
        {
            get
            {
                return ArticulationSegments.Values.ToArray();
            }
            set
            {
                ArticulationSegments.Clear();
                foreach (var segment in value)
                {
                    segment.ParentNetwork = this;
                    ArticulationSegments.Add(segment.Id, segment);
                }
            }
        }
        /// 
        /// Gets or sets the segments
        /// 
        [XmlArray("intersectionGraph")]
        public RoadSegment[] IntersectionSegmentsArray
        {
            get
            {
                return ArticulationSegments.Values.ToArray();
            }
            set
            {
                ArticulationSegments.Clear();
                foreach (var segment in value)
                {
                    segment.ParentNetwork = this;
                    ArticulationSegments.Add(segment.Id, segment);
                }
            }
        }
    
        /// 
        /// Initializes the road network
        /// 
        public RoadNetwork()
        {
            Nodes = new Dictionary();
            ArticulationSegments = new Dictionary();
            IntersectionSegments = new Dictionary();
        }
        /// 
        /// Creates a node and returns it
        /// 
        /// Position
        /// 
        public RoadNode CreateNode(Vector2 pos)
        {
            // Skip IDs that already exist
            while (Nodes.ContainsKey(++lastNodeId)) ;
            // Create node
            RoadNode node = new RoadNode()
            {
                Id = lastNodeId,
                ParentNetwork = this,
                Position = pos
            };
            Nodes.Add(node.Id, node);
            return node;
        }
        /// 
        /// Creates a node and returns it
        /// 
        /// Created node
        public RoadNode CreateNode()
        {
            return CreateNode(Vector2.Zero);
        }
        /// 
        /// Creates a segment and returns it
        /// 
        /// Created segment
        public RoadSegment CreateArticulationSegment()
        {
            return CreateArticulationSegment(null, null);
        }
        /// 
        /// Creates a segment and returns it
        /// 
        /// First terminal
        /// Second terminal
        /// Created segment
        public RoadSegment CreateArticulationSegment(RoadNode term1, RoadNode term2)
        {
            // Skip IDs that already exist
            while (ArticulationSegments.ContainsKey(++lastSegmentId)) ;
            // Create segment
            RoadSegment segment = new RoadSegment()
            {
                Id = lastSegmentId,
                ParentNetwork = this,
                Terminal1 = term1,
                Terminal2 = term2
            };
            // Set links
            if (term1 != null)
                term1.ArticulationSegmentIds.Add(segment.Id);
            
            if (term2 != null)
                term2.ArticulationSegmentIds.Add(segment.Id);
            ArticulationSegments.Add(segment.Id, segment);
            return segment;
        }
        /// 
        /// Creates an articulation segment
        /// 
        /// First terminal
        /// Position of second terminal
        /// Road segment
        public RoadSegment CreateArticulationSegment(RoadNode term1, Vector2 term2pos)
        {
            var term2 = CreateNode(term2pos);
            return CreateArticulationSegment(term1, term2);
        }
        /// 
        /// Creates an articulation segment
        /// 
        /// Position of first terminal
        /// Second terminal
        /// Road segment
        public RoadSegment CreateArticulationSegment(Vector2 term1pos, RoadNode term2)
        {
            var term1 = CreateNode(term1pos);
            return CreateArticulationSegment(term1, term2);
        }
        /// 
        /// Creates an articulation segment
        /// 
        /// Position of first terminal
        /// Position of second terminal
        /// Road segment
        public RoadSegment CreateArticulationSegment(Vector2 term1pos, Vector2 term2pos)
        {
            var term1 = CreateNode(term1pos);
            var term2 = CreateNode(term2pos);
            return CreateArticulationSegment(term1, term2);
        }
        /// 
        /// Creates a segment and returns it
        /// 
        /// Created segment
        public RoadSegment CreateIntersectionSegment()
        {
            // Skip IDs that already exist
            while (IntersectionSegments.ContainsKey(++lastSegmentId)) ;
            // Create segment
            RoadSegment segment = new RoadSegment()
            {
                Id = lastSegmentId,
                ParentNetwork = this
            };
            IntersectionSegments.Add(segment.Id, segment);
            return segment;
        }
        /// 
        /// Splits an articulation segment in two segments
        /// 
        /// 
        /// 
        /// Newly created road node
        public RoadNode SplitArticulationSegment(RoadSegment segment, Vector2 point)
        {
            // Get current terminals
            var term1 = segment.Terminal1;
            var term2 = segment.Terminal2;
            int l1 = segment.LanesTo1, l2 = segment.LanesTo2;
            // Create new terminal
            var newTerm = CreateNode(point);
            // Delete exinsting segment
            term1.ArticulationSegmentIds.Remove(segment.Id);
            term2.ArticulationSegmentIds.Remove(segment.Id);
            ArticulationSegments.Remove(segment.Id);
            // Create split segments
            var seg1 = CreateArticulationSegment(term1, newTerm);
            var seg2 = CreateArticulationSegment(newTerm, term2);
            seg1.LanesTo1 = seg2.LanesTo1 = l1;
            seg1.LanesTo2 = seg2.LanesTo2 = l2;
            return newTerm;
        }
    }
}