Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.IO
Imports System.Net
Imports System.Reflection
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents
Imports System.Windows.Ink
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Shapes

Namespace VectorLayer
    Public Interface IValueToBrush
        Function ValueToBrush(value As Double) As Brush
    End Interface

    Public Class Country
        Private _fill As Brush = Nothing
        Private _value As Double
        Friend Parent As Countries

        Public Property Name() As String
            Get
                Return m_Name
            End Get
            Set(value As String)
                m_Name = Value
            End Set
        End Property
        Private m_Name As String

        Public Property Value() As Double
            Get
                Return _value
            End Get
            Set(value As Double)
                _value = value
                _fill = Nothing
            End Set
        End Property

        Public ReadOnly Property Fill() As Brush
            Get
                If _fill Is Nothing Then
                    If Parent IsNot Nothing Then
                        _fill = Parent.ValueToBrush(Value)
                    End If
                End If
                Return _fill
            End Get
        End Property
    End Class

    Public Class Countries
        Inherits Collection(Of Country)
        Implements IValueToBrush
        Private _dict As New Dictionary(Of String, Country)()

        Public Property Converter() As IValueToBrush
            Get
                Return m_Converter
            End Get
            Set(value As IValueToBrush)
                m_Converter = Value
            End Set
        End Property
        Private m_Converter As IValueToBrush

        Public Function ValueToBrush(value As Double) As Brush Implements IValueToBrush.ValueToBrush
            If Converter IsNot Nothing Then
                Return Converter.ValueToBrush(value)
            End If

            Return Nothing
        End Function

        Default Public ReadOnly Property Item(name As String) As Country
            Get
                If _dict.ContainsKey(name) Then
                    Return _dict(name)
                End If
                Return Nothing
            End Get
        End Property

        Public Function GetMin() As Double
            Dim min As Double = Double.NaN

            For Each country As Country In Me
                If Double.IsNaN(min) OrElse country.Value < min Then
                    min = country.Value
                End If
            Next

            Return min
        End Function

        Public Function GetMax() As Double
            Dim max As Double = Double.NaN

            For Each country As Country In Me
                If Double.IsNaN(max) OrElse country.Value > max Then
                    max = country.Value
                End If
            Next

            Return max
        End Function

        Protected Overrides Sub InsertItem(index As Integer, item As Country)
            MyBase.InsertItem(index, item)
            item.Parent = Me
            _dict.Add(item.Name, item)
        End Sub

        Protected Overrides Sub ClearItems()
            For Each item As Country In Me
                item.Parent = Nothing
            Next

            MyBase.ClearItems()
            _dict.Clear()
        End Sub

        Protected Overrides Sub RemoveItem(index As Integer)
            Dim item As Country = Me(index)
            MyBase.RemoveItem(index)

            _dict.Remove(item.Name)
            item.Parent = Nothing
        End Sub

        Public Shared Function CreateTestCollection() As Countries
            Dim countries As New Countries()
            'countries.Add(new Country() { Name = "United States", Value = 100 });
            'countries.Add(new Country() { Name = "Russia", Value = 50 });
            'countries.Add(new Country() { Name = "China", Value = 0 });
            'countries.Add(new Country() { Name = "Germany", Value = 75 });

            Using stream As Stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("VectorLayer.Resources.gdp-ppp.txt")
                Using sr As New StreamReader(stream)
                    While Not sr.EndOfStream
                        Dim s As String = sr.ReadLine()

                        Dim ss As String() = s.Split(New Char() {ControlChars.Tab}, StringSplitOptions.RemoveEmptyEntries)

                        countries.Add(New Country() With {
                            .Name = ss(1).Trim(),
                            .Value = Double.Parse(ss(2))
                        })
                    End While
                End Using
            End Using

            'countries.Converter = new ColorRange() 
            '{ Min = countries.GetMin(), Max = countries.GetMax(), 
            '  ColorMin = Colors.Blue, ColorMax = Colors.Red };

            Dim cvals As New ColorValues()
            cvals.Add(New ColorValue() With {
                .Color = Colors.Red,
                .Value = 0
            })

            cvals.Add(New ColorValue() With {
                .Color = Colors.Green,
                .Value = 2000000
            })
            cvals.Add(New ColorValue() With {
                .Color = Colors.Blue,
                .Value = 1.001 * countries.GetMax()
            })

            countries.Converter = cvals

            Return countries
        End Function
    End Class

    Public Class ColorValue
        Public Property Color() As Color
            Get
                Return m_Color
            End Get
            Set(value As Color)
                m_Color = Value
            End Set
        End Property
        Private m_Color As Color
        Public Property Value() As Double
            Get
                Return m_Value
            End Get
            Set(value As Double)
                m_Value = Value
            End Set
        End Property
        Private m_Value As Double
    End Class

    Public Class ColorValues
        Inherits Collection(Of ColorValue)
        Implements IValueToBrush
        Public Function ValueToBrush(value As Double) As Brush Implements IValueToBrush.ValueToBrush
            Dim brush As SolidColorBrush = Nothing

            Dim greater As ColorValue = Nothing
            Dim less As ColorValue = Nothing

            For Each cval As ColorValue In Me
                If cval.Value < value Then
                    If less Is Nothing OrElse (value - cval.Value < value - less.Value) Then
                        less = cval
                    End If
                End If
                If cval.Value > value Then
                    If greater Is Nothing OrElse (value - cval.Value > value - greater.Value) Then
                        greater = cval
                    End If
                End If
            Next

            If less IsNot Nothing AndAlso greater IsNot Nothing Then
                Dim clr1 As Color = less.Color
                Dim clr2 As Color = greater.Color

                Dim rval As Double = (value - less.Value) / (greater.Value - less.Value)

                Dim a As Byte = CByte(CDbl(clr1.A) + rval * CDbl(clr2.A) - CDbl(clr1.A))
                Dim r As Byte = CByte(CDbl(clr1.R) + rval * CDbl(clr2.R) - CDbl(clr1.R))
                Dim g As Byte = CByte(CDbl(clr1.G) + rval * CDbl(clr2.G) - CDbl(clr1.G))
                Dim b As Byte = CByte(CDbl(clr1.B) + rval * CDbl(clr2.B) - CDbl(clr1.B))
                Dim clr As Color = Color.FromArgb(
                    a,
                    r,
                    g,
                    b)
                brush = New SolidColorBrush(clr)
            End If

            Return brush
        End Function
    End Class

    Public Class ColorRange
        Implements IValueToBrush
        Public Property Min() As Double
            Get
                Return m_Min
            End Get
            Set(value As Double)
                m_Min = Value
            End Set
        End Property
        Private m_Min As Double
        Public Property Max() As Double
            Get
                Return m_Max
            End Get
            Set(value As Double)
                m_Max = Value
            End Set
        End Property
        Private m_Max As Double

        Public Property ColorMin() As Color
            Get
                Return m_ColorMin
            End Get
            Set(value As Color)
                m_ColorMin = Value
            End Set
        End Property
        Private m_ColorMin As Color
        Public Property ColorMax() As Color
            Get
                Return m_ColorMax
            End Get
            Set(value As Color)
                m_ColorMax = Value
            End Set
        End Property
        Private m_ColorMax As Color

        Public Function ValueToBrush(value As Double) As Brush Implements IValueToBrush.ValueToBrush
            Dim brush As SolidColorBrush = Nothing

            value = (value - Min) / (Max - Min)

            'value = (Math.Log10(value) - Math.Log10(Min)) / (Math.Log10(Max) - Math.Log10(Min));

            value = Math.Log(value + 1, 2)

            If value >= 0.0 AndAlso value <= 1.0 Then
                Dim clr As Color = Color.FromArgb(CByte(ColorMin.A + value * (ColorMax.A - ColorMin.A)), CByte(ColorMin.R + value * (ColorMax.R - ColorMin.R)), CByte(ColorMin.G + value * (ColorMax.G - ColorMin.G)), CByte(ColorMin.B + value * (ColorMax.B - ColorMin.B)))

                brush = New SolidColorBrush(clr)
            End If

            Return brush
        End Function
    End Class
End Namespace
