package application.controller

import application.model.*
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.fxml.FXML
import javafx.fxml.Initializable
import javafx.scene.control.ListCell
import javafx.scene.control.ListView
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.charts.dataviewer.api.config.DataViewerConfiguration
import org.charts.dataviewer.api.data.PlotData
import org.charts.dataviewer.api.trace.ScatterTrace
import org.charts.dataviewer.javafx.JavaFxDataViewer
import org.charts.dataviewer.utils.TraceColour
import org.charts.dataviewer.utils.TraceVisibility
import java.net.URL
import java.util.*


class DataPanelController : Initializable, SelectedVesselListener {
    private var dataList: ObservableList<String> = FXCollections.observableArrayList()
    private lateinit var timeData: ArrayList<String>

    private val latitude: ArrayList<Double> = arrayListOf()
    private val longitude: ArrayList<Double> = arrayListOf()
    private val speedOverGround: ArrayList<Double> = arrayListOf()
    private val courseOverGround: ArrayList<Double> = arrayListOf()
    private val heading: ArrayList<Double> = arrayListOf()
    private val vesselName: ArrayList<String> = arrayListOf()
    private val imo: ArrayList<String> = arrayListOf()
    private val callSign: ArrayList<String> = arrayListOf()
    private val vesselType: ArrayList<Double> = arrayListOf()
    private val status: ArrayList<String> = arrayListOf()
    private val length: ArrayList<Double> = arrayListOf()
    private val width: ArrayList<Double> = arrayListOf()
    private val draft: ArrayList<Double> = arrayListOf()
    private val cargo: ArrayList<Double> = arrayListOf()

    private var selectedItem: String? = null

    @FXML
    var dataListView = ListView<String>()

    @FXML
    var dataViewer = JavaFxDataViewer()

    private val plotData = PlotData()
    private val config = DataViewerConfiguration()
    override fun initialize(location: URL?, resources: ResourceBundle?) {
        setObservableSelectedVesselListener()
        dataListView.items = dataList



        config.showLegend(false)
        config.setLegendInsidePlot(false)

        setObservableCurrentTimeListener()

        dataListView.setCellFactory {
            object : ListCell<String?>() {
                override fun updateItem(item: String?, empty: Boolean) {
                    super.updateItem(item, empty)
                    text = if (empty) {
                        null
                    } else {
                        item
                    }
                }
            }
        }

        dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
            selectedItem = newValue
            updateDataList(observableSelectedVessel.value)
            plot(newValue)
        }

        plotData.allTraces.clear()
        config.setxAxisTitle("")
        config.setyAxisTitle("")
        config.plotTitle = ""
        dataViewer.updateConfiguration(config)
        dataViewer.updatePlot(plotData)
        initDataList()

    }

    private fun plot() {
        if (selectedItem != null && observableSelectedVessel.value.messages.size != 0) {
            GlobalScope.launch {
                plot(selectedItem)
            }
        }
    }

    private fun plot(data: String?) {
        if (data == null) {
            plotData.allTraces.clear()
            config.setxAxisTitle("")
            config.setyAxisTitle("")
            dataViewer.updateConfiguration(config)

            dataViewer.resetPlot()

            return
        }

        val scatterTrace = ScatterTrace<Any>()
        scatterTrace.traceColour = TraceColour.RED
        scatterTrace.traceVisibility = TraceVisibility.TRUE

        val serieStringX: Array<String> = timeData.toTypedArray()
//            val serieDoubleX: Array<Double> = arrayListDoubleX.toTypedArray()
        var serieStringY: Array<String> = arrayOf()
        var serieDoubleY: Array<Double> = arrayOf()
        when (data) {
            "Latitude" -> {
                serieDoubleY = latitude.toTypedArray()
            }
            "Longitude" -> {
                serieDoubleY = longitude.toTypedArray()
            }
            "Speed Over Ground" -> {
                serieDoubleY = speedOverGround.toTypedArray()
            }
            "Course Over Ground" -> {
                serieDoubleY = courseOverGround.toTypedArray()
            }
            "Heading" -> {
                serieDoubleY = heading.toTypedArray()
            }
            "Vessel Name" -> {
                serieStringY = vesselName.toTypedArray()
            }
            "IMO" -> {
                serieStringY = imo.toTypedArray()
            }
            "Call Sign" -> {
                serieStringY = callSign.toTypedArray()
            }
            "Vessel Type" -> {
                serieDoubleY = vesselType.toTypedArray()
            }
            "Status" -> {
                serieStringY = status.toTypedArray()
            }
            "Length" -> {
                serieDoubleY = length.toTypedArray()
            }
            "Width" -> {
                serieDoubleY = width.toTypedArray()
            }
            "Draft" -> {
                serieDoubleY = draft.toTypedArray()
            }
            "Cargo" -> {
                serieDoubleY = cargo.toTypedArray()
            }
        }

        if (serieStringY.isNotEmpty()) {
            scatterTrace.setxArray(serieStringX)
            scatterTrace.setyArray(serieStringY)
        } else {
            scatterTrace.setxArray(serieStringX)
            scatterTrace.setyArray(serieDoubleY)
        }

        config.plotTitle = ""
        config.setxAxisTitle("Time (s)")
        config.setyAxisTitle(data)
        dataViewer.resetPlot()
        plotData.allTraces.clear()
        plotData.addTrace(scatterTrace)
        dataViewer.updateConfiguration(config)
        dataViewer.updatePlot(plotData)

    }

    private fun setObservableSelectedVesselListener() {
        observableSelectedVessel.listeners.add(this)
    }

    private fun populateTime(vessel: Vessel): ArrayList<String> {
        return if (observableIsReplayState.value) {
            vessel.getAllTimeBeforeSelectedTime()
        } else {
            vessel.getAllTime()
        }
    }

    private fun populateLatitude(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllLatitudeBeforeSelectedTime()
        } else {
            vessel.getAllLatitude()
        }
    }

    private fun populateLongitude(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllLongitudeBeforeSelectedTime()
        } else {
            vessel.getAllLongitude()
        }
    }

    private fun populateSpeedOverGround(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllSpeedOverGroundBeforeSelectedTime()
        } else {
            vessel.getAllSpeedOverGround()
        }
    }

    private fun populateCourseOverGround(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllCourseOverGroundBeforeSelectedTime()
        } else {
            vessel.getAllCourseOverGround()
        }
    }

    private fun populateHeading(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllHeadingBeforeSelectedTime()
        } else {
            vessel.getAllHeading()
        }
    }

    private fun populateVesselName(vessel: Vessel): ArrayList<String> {
        return if (observableIsReplayState.value) {
            vessel.getAllVesselNameBeforeSelectedTime()
        } else {
            vessel.getAllVesselName()
        }
    }

    private fun populateIMO(vessel: Vessel): ArrayList<String> {
        return if (observableIsReplayState.value) {
            vessel.getAllIMOBeforeSelectedTime()
        } else {
            vessel.getAllIMO()
        }
    }

    private fun populateCallSign(vessel: Vessel): ArrayList<String> {
        return if (observableIsReplayState.value) {
            vessel.getAllCallSignBeforeSelectedTime()
        } else {
            vessel.getAllCallSign()
        }
    }

    private fun populateVesselType(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllVesselTypeBeforeSelectedTime()
        } else {
            vessel.getAllVesselType()
        }
    }

    private fun populateStatus(vessel: Vessel): ArrayList<String> {
        return if (observableIsReplayState.value) {
            vessel.getAllStatusBeforeSelectedTime()
        } else {
            vessel.getAllStatus()
        }
    }

    private fun populateLength(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllLengthBeforeSelectedTime()
        } else {
            vessel.getAllLength()
        }
    }

    private fun populateWidth(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllWidthBeforeSelectedTime()
        } else {
            vessel.getAllWidth()
        }
    }

    private fun populateDraft(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllDraftBeforeSelectedTime()
        } else {
            vessel.getAllDraft()
        }
    }

    private fun populateCargo(vessel: Vessel): ArrayList<Double> {
        return if (observableIsReplayState.value) {
            vessel.getAllCargoBeforeSelectedTime()
        } else {
            vessel.getAllCargo()
        }
    }

    private fun initDataList() {
        val data = arrayListOf<String>()

        data.add("Latitude")
        data.add("Longitude")
        data.add("Speed Over Ground")
        data.add("Course Over Ground")
        data.add("Heading")
        data.add("Vessel Name")
        data.add("IMO")
        data.add("Call Sign")
        data.add("Vessel Type")
        data.add("Status")
        data.add("Length")
        data.add("Width")
        data.add("Draft")
        data.add("Cargo")

        dataList.addAll(data)
    }

    private fun updateDataList(vessel: Vessel) {
        timeData = populateTime(vessel)

        if (dataListView.selectionModel.selectedItem == null) return

        when (dataListView.selectionModel.selectedItem) {
            "Latitude" -> {
                latitude.clear()
                latitude.addAll(populateLatitude(vessel))
            }
            "Longitude" -> {
                longitude.clear()
                longitude.addAll(populateLongitude(vessel))
            }
            "Speed Over Ground" -> {
                speedOverGround.clear()
                speedOverGround.addAll(populateSpeedOverGround(vessel))
            }
            "Course Over Ground" -> {
                courseOverGround.clear()
                courseOverGround.addAll(populateCourseOverGround(vessel))
            }
            "Heading" -> {
                heading.clear()
                heading.addAll(populateHeading(vessel))
            }
            "Vessel Name" -> {
                vesselName.clear()
                vesselName.addAll(populateVesselName(vessel))
            }
            "IMO" -> {
                imo.clear()
                imo.addAll(populateIMO(vessel))
            }
            "Call Sign" -> {
                callSign.clear()
                callSign.addAll(populateCallSign(vessel))
            }
            "Vessel Type" -> {
                vesselType.clear()
                vesselType.addAll(populateVesselType(vessel))
            }
            "Status" -> {
                status.clear()
                status.addAll(populateStatus(vessel))
            }
            "Length" -> {
                length.clear()
                length.addAll(populateLength(vessel))
            }
            "Width" -> {
                width.clear()
                width.addAll(populateWidth(vessel))
            }
            "Draft" -> {
                draft.clear()
                draft.addAll(populateDraft(vessel))
            }
            "Cargo" -> {
                cargo.clear()
                cargo.addAll(populateCargo(vessel))
            }
        }
    }

    private fun setObservableCurrentTimeListener() {
        observableCurrentTime.listeners.add(object : CurrentTime {
            override fun onValueChanged(newValue: Int) {
                updateDataList(observableSelectedVessel.value)
                plot()
            }
        })
    }

    override fun onValueChanged(newValue: Vessel) {
        updateDataList(newValue)
        plot()
    }

}


