open Plotly.NET
open Dash.NET
open Dash.NET.DCC
open Dash.NET.Giraffe
open Dash.NET.DashTable
open FSharp.Data

let csv =
    CsvFile
        .Load("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")
        .Cache()

module CsvFile =
    let toSameColumnOrder : (string * obj) [] -> (string * obj) [] =
        Array.sortBy (
            fst
            >> fun columnName ->
                csv.Headers
                |> Option.map (Array.findIndex ((=) columnName))
        )

    let parse (data : seq<(string * obj) []>) =
        let c =
            CsvFile.Parse(
                data
                |> Seq.head
                |> Seq.map fst
                |> fun colunns -> System.String.Join(',', colunns)
            )
        data
        |> Seq.map (fun cs -> CsvRow(c, cs |> Array.map (snd >> string)))
        |> c.Append

let layout =
    Html.div [
        Attr.children [
            Clipboard.clipboard "datatable_copy" [
                Clipboard.Attr.style [
                    StyleProperty ("fontSize", 18)
                    StyleProperty ("color", "white")
                    StyleProperty ("backgroundColor", "grey")
                    StyleProperty ("height", 38)
                ]
            ]
            RadioItems.radioItems "copy_selected" [
                RadioItems.Attr.options [
                    RadioItemsOption.init("Copy All", "all")
                    RadioItemsOption.init("Copy Selected", "some")
                ]
                RadioItems.Attr.value "all"
                RadioItems.Attr.style [ StyleProperty ("display", "inline-block") ]
            ]
            DataTable.dataTable "datatable" [
                DataTable.Attr.startCell (DataTable.Cell.init(0L))
                DataTable.Attr.endCell (DataTable.Cell.init(0L))
                DataTable.Attr.derivedVirtualData Seq.empty
                DataTable.Attr.columns (
                    csv.Headers
                    |> Option.map (Array.map (fun h -> DataTable.Column.init(h, h)))
                    |> Option.defaultValue Array.empty
                )
                DataTable.Attr.data (
                    csv.Headers
                    |> Option.map (fun hs ->
                        csv.Rows
                        |> Seq.map (fun r ->
                            [ for i in 0..csv.NumberOfColumns - 1 -> hs.[i], box r.[i] ]
                            |> Map.ofList
                        )
                    )
                    |> Option.defaultValue Seq.empty
                )
            ]
        ]
    ]

let customCopyCallback =
    let outputTarget = "datatable_copy" @. CustomProperty "content"
    Callback.singleOut (
        "datatable_copy" @. N_Clicks,
        outputTarget,
        (fun (_: int) (startCell: DataTable.Cell) (endCell: DataTable.Cell) (data: seq<Map<string, obj>>) (copySelected: string) ->
            let copyData =
                data
                |> Seq.map (Map.toArray >> CsvFile.toSameColumnOrder)
                |> (match copySelected with
                    | "some" ->
                        Array.ofSeq
                        >> fun rows -> rows.[int (startCell?row :?> int64)..int (endCell?row :?> int64)]
                        >> Array.map (fun columns -> columns.[int (startCell?column :?> int64)..int (endCell?column :?> int64)])
                        >> Array.toSeq
                    | _ -> id
                    >> CsvFile.parse)
            outputTarget => (copyData.SaveToString())
        ),
        State = [
            CallbackState.create("datatable", "start_cell")
            CallbackState.create("datatable", "end_cell")
            CallbackState.create("datatable", "derived_virtual_data")
            CallbackState.create("copy_selected", "value")
        ]
    )

[<EntryPoint>]
let main argv =
    DashApp.initDefault()
    |> DashApp.withLayout layout
    |> DashApp.addCallback customCopyCallback
    |> DashApp.run [||] (DashGiraffeConfig.initDefault "localhost")
