Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Values
Imports Nexamas.UI.Visualization
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Public Nexamas UI chart control for small business dashboards and reports.
    ''' The control exposes a compact SDK surface while chart semantics remain owned by the Chart / Visualization System foundation.
    ''' </summary>
    Partial Public NotInheritable Class MASChart
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

        Private Shared ReadOnly _contract As MASResolvedComponentContract =
            MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASChart))

        Private ReadOnly _series As New List(Of MASChartSeriesDraft)()
        Private ReadOnly _renderer As New MASChartRenderer()
        Private _chartKind As MASChartKind = MASChartKind.Line
        Private _title As String = "Chart"
        Private _revision As Integer

        Public Event ChartChanged As EventHandler

        Public Sub New()
        End Sub

        Public Shared Function Create() As MASChart
            Return New MASChart()
        End Function

        Public Property Title As String
            Get
                Return _title
            End Get
            Set(value As String)
                Dim normalized As String = NormalizeText(value, "Chart")
                If String.Equals(_title, normalized, StringComparison.Ordinal) Then Return
                _title = normalized
                MarkChartChanged()
            End Set
        End Property

        Public ReadOnly Property ChartKindName As String
            Get
                Return _chartKind.ToString()
            End Get
        End Property

        Public ReadOnly Property HasData As Boolean
            Get
                Return _series.Any(Function(seriesItem) seriesItem IsNot Nothing AndAlso seriesItem.Points.Count > 0)
            End Get
        End Property

        Public Function WithTitle(title As String) As MASChart
            Me.Title = title
            Return Me
        End Function

        Public Function WithLineChart(title As String) As MASChart
            Return ConfigureKind(MASChartKind.Line, title)
        End Function

        Public Function WithBarChart(title As String) As MASChart
            Return ConfigureKind(MASChartKind.Bar, title)
        End Function

        Public Function WithPieChart(title As String) As MASChart
            Return ConfigureKind(MASChartKind.Pie, title)
        End Function

        Public Function WithDonutChart(title As String) As MASChart
            Return ConfigureKind(MASChartKind.Donut, title)
        End Function

        Public Function WithKpiChart(title As String) As MASChart
            Return ConfigureKind(MASChartKind.Kpi, title)
        End Function

        Public Function WithSparklineChart(title As String) As MASChart
            Return ConfigureKind(MASChartKind.Sparkline, title)
        End Function

        Public Function ClearSeries() As MASChart
            If _series.Count = 0 Then Return Me
            _series.Clear()
            MarkChartChanged()
            Return Me
        End Function

        Public Function AddPoint(key As String, label As String, value As Double) As MASChart
            Return AddPoint("default", "Value", key, label, value)
        End Function

        Public Function AddPoint(seriesKey As String, seriesLabel As String, key As String, label As String, value As Double) As MASChart
            If Not MASChartDataPoint.IsFinite(value) Then Throw New ArgumentOutOfRangeException(NameOf(value), "Chart point value must be finite.")

            Dim normalizedSeriesKey As String = NormalizeText(seriesKey, "default")
            Dim normalizedSeriesLabel As String = NormalizeText(seriesLabel, normalizedSeriesKey)
            Dim normalizedPointKey As String = NormalizeText(key, "point" & (_revision + 1).ToString(System.Globalization.CultureInfo.InvariantCulture))
            Dim normalizedPointLabel As String = NormalizeText(label, normalizedPointKey)
            Dim draft As MASChartSeriesDraft = GetOrCreateSeries(normalizedSeriesKey, normalizedSeriesLabel)
            draft.Points.Add(New MASChartPointDraft(normalizedPointKey, normalizedPointLabel, value, 0.0R, False))
            MarkChartChanged()
            Return Me
        End Function

        Public Function AddTrendPoint(key As String, label As String, value As Double, trendValue As Double) As MASChart
            If Not MASChartDataPoint.IsFinite(value) Then Throw New ArgumentOutOfRangeException(NameOf(value), "Chart KPI value must be finite.")
            If Not MASChartDataPoint.IsFinite(trendValue) Then Throw New ArgumentOutOfRangeException(NameOf(trendValue), "Chart KPI trend value must be finite.")

            Dim draft As MASChartSeriesDraft = GetOrCreateSeries("kpi", "KPI")
            draft.Points.Add(New MASChartPointDraft(NormalizeText(key, "kpi"), NormalizeText(label, "KPI"), value, trendValue, True))
            MarkChartChanged()
            Return Me
        End Function

        Friend ReadOnly Property Revision As Integer
            Get
                Return _revision
            End Get
        End Property

        Friend Function BuildDefinition() As MASChartDefinition
            Dim builtSeries As List(Of MASChartSeries) = BuildSeries()
            Return New MASChartDefinition(_chartKind, _title, builtSeries)
        End Function

        Private Function ConfigureKind(kind As MASChartKind, title As String) As MASChart
            If Not IsSupportedControlKind(kind) Then Throw New ArgumentOutOfRangeException(NameOf(kind))

            Dim normalizedTitle As String = NormalizeText(title, "Chart")
            If _chartKind = kind AndAlso String.Equals(_title, normalizedTitle, StringComparison.Ordinal) Then Return Me
            _chartKind = kind
            _title = normalizedTitle
            MarkChartChanged()
            Return Me
        End Function

        Private Function BuildSeries() As List(Of MASChartSeries)
            Dim result As New List(Of MASChartSeries)()
            For Each draft As MASChartSeriesDraft In _series
                If draft Is Nothing OrElse draft.Points.Count = 0 Then Continue For

                Dim points As New List(Of MASChartDataPoint)()
                For Each point As MASChartPointDraft In draft.Points
                    If point Is Nothing Then Continue For
                    If point.HasTrend Then
                        points.Add(MASChartDataPoint.WithTrend(point.Key, point.Label, point.Value, point.TrendValue))
                    Else
                        points.Add(MASChartDataPoint.WithLabel(point.Key, point.Label, point.Value))
                    End If
                Next

                If points.Count > 0 Then result.Add(New MASChartSeries(draft.SeriesKey, draft.Label, points))
            Next

            Return result
        End Function

        Private Function GetOrCreateSeries(seriesKey As String, seriesLabel As String) As MASChartSeriesDraft
            Dim normalizedSeriesKey As String = NormalizeText(seriesKey, "default")
            For Each draft As MASChartSeriesDraft In _series
                If String.Equals(draft.SeriesKey, normalizedSeriesKey, StringComparison.OrdinalIgnoreCase) Then Return draft
            Next

            Dim created As New MASChartSeriesDraft(normalizedSeriesKey, NormalizeText(seriesLabel, normalizedSeriesKey))
            _series.Add(created)
            Return created
        End Function

        Private Sub MarkChartChanged()
            _revision += 1
            RaiseEvent ChartChanged(Me, EventArgs.Empty)
            InvalidateVisual()
        End Sub

        Private Shared Function IsSupportedControlKind(kind As MASChartKind) As Boolean
            Return kind = MASChartKind.Line OrElse
                   kind = MASChartKind.Bar OrElse
                   kind = MASChartKind.Pie OrElse
                   kind = MASChartKind.Donut OrElse
                   kind = MASChartKind.Kpi OrElse
                   kind = MASChartKind.Sparkline
        End Function

        Private Shared Function NormalizeText(value As String, fallback As String) As String
            Dim normalized As String = If(value, String.Empty).Trim()
            If normalized.Length = 0 Then Return If(fallback, String.Empty).Trim()
            Return normalized
        End Function

        Private NotInheritable Class MASChartSeriesDraft
            Friend Sub New(seriesKey As String, label As String)
                Me.SeriesKey = seriesKey
                Me.Label = label
                Me.Points = New List(Of MASChartPointDraft)()
            End Sub

            Friend ReadOnly Property SeriesKey As String
            Friend ReadOnly Property Label As String
            Friend ReadOnly Property Points As List(Of MASChartPointDraft)
        End Class

        Private NotInheritable Class MASChartPointDraft
            Friend Sub New(key As String, label As String, value As Double, trendValue As Double, hasTrend As Boolean)
                Me.Key = key
                Me.Label = label
                Me.Value = value
                Me.TrendValue = trendValue
                Me.HasTrend = hasTrend
            End Sub

            Friend ReadOnly Property Key As String
            Friend ReadOnly Property Label As String
            Friend ReadOnly Property Value As Double
            Friend ReadOnly Property TrendValue As Double
            Friend ReadOnly Property HasTrend As Boolean
        End Class

    End Class

End Namespace
