open Microsoft.Extensions.Logging
open Giraffe
open Dash.NET.Giraffe

open Plotly.NET
open Dash.NET
open Dash.NET.DCC

open FSharp.Data

let externalStylesheets = [ "https://codepen.io/chriddyp/pen/bWLwgP.css" ]

let [<Literal>] Csv = "https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv"
let csv = CsvFile.Load(Csv).Cache()

let availableCountries = csv.Rows |> Seq.map (fun r -> r.["country"]) |> Seq.distinct

let layout =
    Html.div [
        Attr.children [
            Graph.graph "clientside-graph" []
            Store.store "clientside-figure-store" [ ]
            Html.text "Indicator"
            Dropdown.dropdown "clientside-graph-indicator" [
                Dropdown.Attr.options [
                    DropdownOption.create "Population" "pop" false "Population"
                    DropdownOption.create "Life Expectancy" "lifeExp" false "Life Expectancy"
                    DropdownOption.create "GDP per Capita" "gdpPercap" false "GDP per Capita"
                ]
                Dropdown.Attr.value "pop"
            ]
            Html.text "Country"
            Dropdown.dropdown "clientside-graph-country" [
                Dropdown.Attr.options (
                    availableCountries
                    |> Seq.map (fun country -> DropdownOption.create country country false country)
                )
                Dropdown.Attr.value "Canada"
            ]
            Html.text "Graph scale"
            RadioItems.radioItems "clientside-graph-scale" [
                RadioItems.Attr.options (
                    [ "linear"; "log" ]
                    |> List.map (fun x -> RadioItemsOption.create x x false)
                )
                RadioItems.Attr.value "linear"
            ]
            Html.hr []
            Html.details [
                Attr.children [
                    Html.summary [ Attr.children "Contents of figure storage" ]
                    Markdown.markdown "clientside-figure-json" []
                ]
            ]
        ]
    ]

let updateStoreData =
    let outputTarget = "clientside-figure-store" @. CustomProperty "data"
    Callback.singleOut (
        [ "clientside-graph-indicator" @. Value
          "clientside-graph-country" @. Value ],
        outputTarget,
        (fun (indicator: string) (country: string) ->
            let filtered = csv.Rows |> Seq.filter (fun r -> r.["country"] = country)
            let fig = 
                Chart.Scatter(
                    filtered |> Seq.map (fun r -> int r.["year"]), 
                    filtered |> Seq.map (fun r -> decimal r.[indicator]),
                    StyleParam.Mode.Markers
                )
                |> Chart.withX_Axis(Axis.LinearAxis.init(Title = "year"))
                |> Chart.withY_Axis(Axis.LinearAxis.init(Title = indicator))
                |> GenericChart.toFigure
            outputTarget => fig
        ),
        PreventInitialCall = false
    )

let setScale =
    Callback.singleOut (
        [ "clientside-figure-store" @. CustomProperty "data"
          "clientside-graph-scale" @. Value ],
        "clientside-graph" @. CustomProperty "figure",
        // This function is just a placeholder - it will not be called
        (fun (figure: obj) (scale: string) ->
            let dummyResult =
                CallbackResultBinding.create (Dependency.create(null, null)) null
            dummyResult
        )
        , ClientSideFunction = {
            Namespace = "figure"
            FunctionName = "set_scale"
        },
        PreventInitialCall = false
    )

let generatedFigureJson =
    let outputTarget = "clientside-figure-json" @. Children
    Callback.singleOut (
        "clientside-figure-store" @. CustomProperty "data",
        outputTarget,
        (fun (figure: obj) -> outputTarget => (string figure)),
        PreventInitialCall = false
    )

[<EntryPoint>]
let main argv =
  let dashApp =
    DashApp.initDefault()
    |> DashApp.appendCSSLinks externalStylesheets
    |> DashApp.appendScripts [ "./assets/clientside.js" ]
    |> DashApp.withLayout layout
    |> DashApp.addCallback generatedFigureJson
    |> DashApp.addCallback setScale
    |> DashApp.addCallback updateStoreData

  let config = 
    { HostName = "localhost"
      LogLevel = LogLevel.Debug
      ErrorHandler = (fun ex -> text ex.Message) }

  DashApp.run [||] config dashApp
