open Dash.NET
open Feliz
open FSharp.Data
open Plotly.NET

let df = CsvFile.Load("https://plotly.github.io/datasets/country_indicators.csv").Rows |> List.ofSeq
let available_indicators = df |> List.map (fun r -> r.Item "Indicator Name") |> List.distinct

let dslLayout = 
    Html.div [
        Attr.children [
            Html.div [
                Attr.children [
                    Dropdown.dropdown "xaxis-column" [ 
                        Dropdown.Attr.options (available_indicators |> List.map (fun i -> {Label = i; Value = i; Disabled = false; Title = i}))
                        Dropdown.Attr.value "Fertility rate, total (births per woman)"
                    ]
                    RadioItems.radioItems "xaxis-type" [
                        RadioItems.Attr.options (["Linear"; "Log"] |> List.map (fun i -> {Label = i; Value = i; Disabled = false}))
                        RadioItems.Attr.value "Linear"
                        RadioItems.Attr.labelStyle [Css.displayInlineBlock]
                    ]
                ]
                Attr.style [Css.width (length.perc 48); Css.displayInlineBlock]
            ]
            Html.div [
                Attr.children [
                    Dropdown.dropdown "yaxis-column" [ 
                        Dropdown.Attr.options (available_indicators |> List.map (fun i -> {Label = i; Value = i; Disabled = false; Title = i}))
                        Dropdown.Attr.value "Life expectancy at birth, total (years)"
                    ]
                    RadioItems.radioItems "yaxis-type" [
                        RadioItems.Attr.options (["Linear"; "Log"] |> List.map (fun i -> {Label = i; Value = i; Disabled = false}))
                        RadioItems.Attr.value "Linear"
                        RadioItems.Attr.labelStyle [Css.displayInlineBlock]
                    ]
                ]
                Attr.style [Css.width (length.perc 48); Css.floatRight; Css.displayInlineBlock]
            ]
            Graph.graph "indicator-graphic" []
            Slider.slider "year-slider" [
                Slider.Attr.min (df |> List.map (fun r -> r.Item "Year" |> int) |> List.min)
                Slider.Attr.max (df |> List.map (fun r -> r.Item "Year" |> int) |> List.max)
                Slider.Attr.value (df |> List.map (fun r -> r.Item "Year" |> int) |> List.min)
                Slider.Attr.marks (
                    df 
                    |> List.map (fun r -> r.Item "Year") 
                    |> List.distinct 
                    |> List.map (fun y -> (y, Slider.MarkValue.String y))
                    |> Map.ofList
                    |> Slider.MarksType
               )
            ]
        ]
    ]

let updateFigureCallback =
    Callback.singleOut (
        [ "xaxis-column" @. Value
          "yaxis-column" @. Value
          "xaxis-type" @. Value
          "yaxis-type" @. Value
          "year-slider" @. Value ],
        "indicator-graphic" @. (CustomProperty "figure"),
        fun xaxisColumnName yaxisColumnName xaxisType yaxisType yearValue -> 
            let filteredDf = df |> List.filter (fun r -> r.Item "Year" = yearValue)

            let xData = filteredDf |> List.filter (fun r -> r.Item "Indicator Name" = xaxisColumnName) |> List.map (fun r -> r.Item "Value")
            let yData = filteredDf |> List.filter (fun r -> r.Item "Indicator Name" = yaxisColumnName) |> List.map (fun r -> r.Item "Value")
            let countryData = filteredDf |> List.map (fun r -> r.Item "Country Name")

            let fig = 
                Chart.Scatter(
                    xData, 
                    yData,
                    StyleParam.Mode.Markers,
                    Showlegend = true,
                    Labels = countryData
                )
                |> Chart.withX_Axis(
                    Axis.LinearAxis.init(
                        AxisType = (if xaxisType = "Linear" then StyleParam.AxisType.Linear else StyleParam.AxisType.Log),
                        Title = xaxisColumnName
                    )
                )
                |> Chart.withY_Axis(
                    Axis.LinearAxis.init(
                        AxisType = (if yaxisType = "Linear" then StyleParam.AxisType.Linear else StyleParam.AxisType.Log),
                        Title = yaxisColumnName
                    )
                )
                |> GenericChart.toFigure

            "indicator-graphic" @. (CustomProperty "figure") => fig
        , PreventInitialCall = false
    )

[<EntryPoint>]
let main args =
    DashApp.initDefault()
    |> DashApp.withLayout dslLayout
    |> DashApp.addCallback updateFigureCallback
    |> DashApp.run args