using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.IO;
using System.Xml.Linq;
using System.Globalization;
using MindFusion.Diagramming;
using MindFusion.Diagramming.Layout;
using MindFusion.Diagramming.WebForms;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            diagramView.Behavior = Behavior.Modify;

            diagramView.Diagram.DefaultShape = Shapes.Rectangle;
            diagramView.Diagram.ContainersFoldable = false;
            diagramView.Diagram.ContainerCaptionHeight = 6;
            diagramView.Diagram.Bounds = new RectangleF(0, 0, 250, 195);
        }

        scale = new Color[]
			{
				Color.FromArgb(0xFF, 0x20, 0xC3, 0x00),
				Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00),
				Color.FromArgb(0xFF, 0xFF, 0x80, 0x00),
			};

        CreateTreeMap();
        AssignWeights();
        AssignColors();
        ArrangeDiagram();
    }

    private void CreateTreeMap()
    {
        diagramView.Diagram.ClearAll();

        var doc = XDocument.Load(Server.MapPath("~/Data.xml"));
        foreach (var continentElement in doc.Element("continents").Elements("continent"))
        {
            var container = diagramView.Diagram.Factory.CreateContainerNode(0, 0, 10, 10);
            container.Tag = new float[] { float.Parse(continentElement.Attribute("area").Value, CultureInfo.InvariantCulture), float.Parse(continentElement.Attribute("population").Value, CultureInfo.InvariantCulture) };
            container.Caption = continentElement.Attribute("name").Value;

            foreach (var countryElement in continentElement.Element("countries").Elements("country"))
            {
                var shape = diagramView.Diagram.Factory.CreateShapeNode(0, 0, 10, 10);
                shape.Tag = new float[] { float.Parse(countryElement.Attribute("area").Value, CultureInfo.InvariantCulture), float.Parse(countryElement.Attribute("population").Value, CultureInfo.InvariantCulture) };
                shape.Text = countryElement.Attribute("name").Value;

                var area = double.Parse(countryElement.Attribute("area").Value, CultureInfo.InvariantCulture);
                var population = double.Parse(countryElement.Attribute("population").Value, CultureInfo.InvariantCulture);
                string areaQuantifier = "million sq. km.";
                if (area < 1)
                {
                    area *= 1000;
                    areaQuantifier = "thousand sq. km.";
                }
                string populationQuantifier = "billions";
                if (population < 1)
                {
                    population *= 1000;
                    populationQuantifier = "millions";
                }

                shape.ToolTip = string.Format("{0}{1}Area: ~{2} {3}{4}Population: ~{5} {6}.",
                    shape.Text, "<br />",
                    area, areaQuantifier, "<br />",
                    population, populationQuantifier);
                container.Add(shape);
            }
        }
    }

    private void AssignWeights()
    {
        foreach (var node in diagramView.Diagram.Nodes)
        {
            // Ignore containers and container captions
            if (node is ContainerNode || node.Locked)
                continue;

            if (node is ShapeNode)
            {
                var values = node.Tag as float[];
                if (values == null)
                    continue;

                if (ddlWeight.SelectedValue == "Area")
                    node.Weight = values[0];
                if (ddlWeight.SelectedValue == "Population")
                    node.Weight = values[1];
            }
        }
    }

    private void AssignColors()
    {
        var largestArea = new Dictionary<ContainerNode, double>();
        var largestPopulation = new Dictionary<ContainerNode, double>();
        foreach (var node in diagramView.Diagram.Nodes)
        {
            if (node is ContainerNode)
            {
                var container = (ContainerNode)node;

                double area = 0.0;
                double population = 0.0;
                foreach (DiagramNode child in container.SubordinateGroup.AttachedNodes)
                {
                    var values = child.Tag as float[];
                    if (values == null)
                        continue;

                    area = Math.Max(area, values[0]);
                    population = Math.Max(population, values[1]);
                }

                largestArea[container] = area;
                largestPopulation[container] = population;
            }
        }

        foreach (var node in diagramView.Diagram.Nodes)
        {
            // Ignore containers and container captions
            if (node is ContainerNode || node.Locked)
                continue;

            if (node is ShapeNode)
            {
                var values = node.Tag as float[];
                if (values == null)
                    continue;

                double value = 0;
                double total = 0;
                if (ddlColor.SelectedValue == "Area")
                {
                    value = values[0];
                    total = largestArea[ContainerNode.GetContainer(node)];
                }
                if (ddlColor.SelectedValue == "Population")
                {
                    value = values[1];
                    total = largestPopulation[ContainerNode.GetContainer(node)];
                }
                node.Brush = new MindFusion.Drawing.SolidBrush(GetScaleColor(value, total));
            }
        }
    }

    private Color GetScaleColor(double value, double total)
    {
        double factor = value / total;
        if (factor < 0)
            factor = 0;
        if (factor > 1)
            factor = 1;

        double step = 1.0 / (scale.Length - 1);
        Color start = scale[(int)(factor / step)];
        Color end = scale[Math.Min(scale.Length - 1, (int)(factor / step) + 1)];

        double factorBase = factor - factor % step;

        return Color.FromArgb(0xFF,
            (byte)(start.R + (int)((factor - factorBase) * (end.R - start.R) / step)),
            (byte)(start.G + (int)((factor - factorBase) * (end.G - start.G) / step)),
            (byte)(start.B + (int)((factor - factorBase) * (end.B - start.B) / step)));
    }

    private void ArrangeDiagram()
    {
        var layout = new MindFusion.Diagramming.Layout.TreeMapLayout();
        layout.Padding = 0;
        layout.ContainerPadding = 1;
        layout.LayoutArea = new RectangleF(0, 0, 250, 195);
        layout.Arrange(diagramView.Diagram);

        // Remove the text of smaller nodes
        foreach (var node in diagramView.Diagram.Nodes)
        {
            var shape = node as ShapeNode;
            if (shape == null || shape.Locked)
                continue;

            if (node.Bounds.Width < 10 || node.Bounds.Height < 10)
                shape.Text = "";
        }

        // Resize caption nodes
        foreach (var node in diagramView.Diagram.Nodes)
        {
            if (node.Locked && node.MasterGroup != null && node.MasterGroup.MainItem != null)
            {
                var bounds = node.Bounds;
                bounds.Width = node.MasterGroup.MainItem.GetBounds().Width;
                bounds.Height = Math.Min(30, node.MasterGroup.MainItem.GetBounds().Height);
                node.Bounds = bounds;
            }
        }

        Zoom();
    }

    private void Zoom()
    {
        diagramView.ZoomFactor = 100;
        RectangleF rc = diagramView.DocToClient(diagramView.Diagram.GetContentBounds(false, true));

        float ctrlWidth = (float)diagramView.Width.Value;
        float ctrlHeight = (float)diagramView.Height.Value;
        float cntWidth = rc.Width;
        float cntHeight = rc.Height;

        if (ctrlWidth > 0 && ctrlHeight > 0 && cntWidth > 0 && cntHeight > 0)
        {
            float rx = cntWidth / ctrlWidth;
            float ry = cntHeight / ctrlHeight;
            float ratio = Math.Max(rx, ry);

            float newZoom = 100.0f / ratio;
            if (newZoom > 10)
                newZoom -= 2;

            diagramView.ZoomFactor = newZoom;
        }
    }

    private Color[] scale;

}

