using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System.Xml;
using System.Xml.Linq;
namespace QICrabUI
{
///
/// Swipable and zoomable plane
/// Allows you to place components in a plane
/// and connect them with lines like a graph or scheme
///
public class CUIMap : CUIComponent
{
#region CUIMapLink
#endregion
public class CUIMapLink
{
internal static void InitStatic()
{
CUI.OnInit += () => Default = new CUIMapLink(null, null);
CUI.OnDispose += () => Default = null;
}
public static CUIMapLink Default;
public CUIComponent Start;
public CUIComponent End;
//TODO all this crap wasn't designed for nested AKA
public string StartAKA;
public string EndAKA;
public float LineWidth;
public Color LineColor;
public XElement ToXML()
{
XElement connection = new XElement("Connection");
if (LineWidth != Default.LineWidth)
{
connection.SetAttributeValue("LineWidth", LineWidth);
}
connection.SetAttributeValue("Start", StartAKA ?? "");
connection.SetAttributeValue("End", EndAKA ?? "");
return connection;
}
public CUIMapLink(CUIComponent start, CUIComponent end, Color? lineColor = null, float lineWidth = 2f)
{
LineColor = lineColor ?? new Color(128, 128, 128);
LineWidth = lineWidth;
Start = start;
End = end;
StartAKA = start?.AKA;
EndAKA = end?.AKA;
}
}
#region LinksContainer
#endregion
public class LinksContainer : CUIComponent
{
public List Connections = new List();
public override void Draw(SpriteBatch spriteBatch)
{
base.Draw(spriteBatch);
foreach (CUIMapLink link in Connections)
{
Vector2 midPoint = new Vector2(link.End.Real.Center.X, link.Start.Real.Center.Y);
GUI.DrawLine(spriteBatch,
link.Start.Real.Center,
midPoint,
link.LineColor, width: link.LineWidth
);
GUI.DrawLine(spriteBatch,
midPoint,
link.End.Real.Center,
link.LineColor, width: link.LineWidth
);
}
}
public LinksContainer()
{
UnCullable = true;
BackgroundColor = Color.Transparent;
Border.Color = Color.Transparent;
}
}
#region CUIMap
#endregion
public LinksContainer linksContainer;
public List Connections => linksContainer.Connections;
public CUIComponent Add(CUIComponent c) => Append(c, c.AKA);
public CUIComponent Connect(CUIComponent startComponent, CUIComponent endComponent, Color? color = null)
{
if (startComponent != null && endComponent != null)
{
if (color == null && (!startComponent.Disabled || !endComponent.Disabled)) color = new Color(0, 0, 255);
linksContainer.Connections.Add(new CUIMapLink(startComponent, endComponent, color));
}
return startComponent;
}
public CUIComponent Connect(CUIComponent startComponent, int end = -2, Color? color = null)
{
end = MathUtils.PositiveModulo(end, Children.Count);
CUIComponent endComponent = Children.ElementAtOrDefault(end);
return Connect(startComponent, endComponent, color);
}
//TODO DRY
public CUIComponent Connect(string start, string end, Color? color = null)
{
CUIComponent startComponent = this[start];
CUIComponent endComponent = this[end];
if (startComponent != null && endComponent != null)
{
if (color == null && (!startComponent.Disabled || !endComponent.Disabled)) color = new Color(0, 0, 255);
linksContainer.Connections.Add(new CUIMapLink(startComponent, endComponent, color)
{
StartAKA = start,
EndAKA = end,
});
}
return startComponent;
}
public CUIComponent Connect(int start, int end, Color? color = null)
{
start = MathUtils.PositiveModulo(start, Children.Count);
end = MathUtils.PositiveModulo(end, Children.Count);
CUIComponent startComponent = Children.ElementAtOrDefault(start);
CUIComponent endComponent = Children.ElementAtOrDefault(end);
return Connect(startComponent, endComponent, color);
}
public CUIComponent ConnectTo(CUIComponent Host, params CUIComponent[] children)
{
foreach (CUIComponent child in children) { Connect(Host, child); }
return Host;
}
public override XElement ToXML(CUIAttribute propAttribute = CUIAttribute.CUISerializable)
{
Type type = GetType();
XElement element = new XElement(type.Name);
PackProps(element, propAttribute);
XElement connections = new XElement("Connections");
element.Add(connections);
foreach (CUIMapLink link in Connections)
{
connections.Add(link.ToXML());
}
XElement children = new XElement("Children");
element.Add(children);
foreach (CUIComponent child in Children)
{
if (child == linksContainer) continue;
children.Add(child.ToXML());
}
return element;
}
public override void FromXML(XElement element, string baseFolder = null)
{
foreach (XElement childElement in element.Element("Children").Elements())
{
Type childType = CUIReflection.GetComponentTypeByName(childElement.Name.ToString());
if (childType == null) continue;
CUIComponent child = (CUIComponent)Activator.CreateInstance(childType);
child.FromXML(childElement);
this.Append(child, child.AKA);
}
foreach (XElement link in element.Element("Connections").Elements())
{
CUIComponent startComponent = this[link.Attribute("Start").Value];
CUIComponent endComponent = this[link.Attribute("End").Value];
if (startComponent == null || endComponent == null)
{
CUIDebug.Error("startComponent == null || endComponent == null");
continue;
}
Connect(link.Attribute("Start").Value, link.Attribute("End").Value);
}
//TODO: think, this is potentially very bugged,
// Some props might need to be assigned before children, and some after
ExtractProps(element);
}
public CUIMap() : base()
{
Swipeable = true;
ConsumeMouseClicks = true;
CullChildren = true;
BackgroundColor = Color.Transparent;
//without container links won't be culled
//TODO linksContainer should be special and not just first child
this["links"] = linksContainer = new LinksContainer();
OnScroll += (m) =>
{
CUIProps.ChildrenOffset.SetValue(
ChildrenOffset.Zoom(
m.MousePosition - Real.Position,
(-m.Scroll / 500f)
)
);
};
}
}
}