setClass(Class="citizenair_stations",
         representation=representation(phenomena = "data.frame", 
                                       stations = "data.frame"))
setClass(Class="citizenair_userdata",
         representation=representation(filename = "character", 
                                       file = "character", 
                                       time = "POSIXct", 
                                       content = "list", 
                                       phenomena = "character"))

citizenair_userdata_empty <- function(){
  result <- list()
  result$filename <- NA_character_
  result$file <- NA_character_
  result$time <- Sys.time()
  result$content <- list()
  result$phenomena <- character()
  class(result) <- "citizenair_userdata"
  result
}

#' @title CitizenAir app data
#' @description CitizenAir app data
#' @docType class
#' @field endpoint object of class \code{Endpoint} as returned by \code{as.Endpoint}
#' @field stations object of class \code{citizenair_stations} as returned by \code{\link{read_stations}}
#' @field data object of class \code{citizenair_userdata} as returned by \code{\link{read_citizenair}}
#' @field closest_station a character string with the id of the closest station.
#' @format The CitizenAir object is an R6Class
#' @section Methods:
#' \describe{
#'   \item{\code{fetch_timeseries(id = "123", time_span)}}{Get timeseries for Station with \code{id} and a certain time_span The argument \code{timespan.} Passed on to \code{sensorweb4R::getData}}
#'   \item{\code{getStations()}}{Get a SpatialPointsDataFrame of all stations}
#'   \item{TODO}{TODO}
#' }
#' @export
#' @examples
#' library(sensorweb4R)
#' e <- as.Endpoint("http://geo.irceline.be/sos/api/v1")
#' ca <- CitizenAir$new(e)
#' ca
#' library(BelgiumMaps.StatBel)
#' data("BE_ADMIN_REGION", package = "BelgiumMaps.StatBel")
#' vlaanderen <- subset(BE_ADMIN_REGION, TX_RGN_DESCR_NL %in% "Vlaams Gewest")
#' ca <- CitizenAir$new(e, stations_area = vlaanderen)
#' ca
#' ca$getPhenomena()
#' ca$getPhenomenaLabel("20")
#' 
#' ## get stations
#' library(sp)
#' x <- ca$getStations()
#' plot(x)
#' plot(ca$getStationsArea())
#' 
#' ## get official measurements
#' library(lubridate)
#' x <- ca$fetch_timeseries(id = "1030")
#' period <- as.interval(as.POSIXct(Sys.Date() - 14), Sys.time())
#' x <- ca$fetch_timeseries(id = "1030", time_span = period)
#' ## set citizen measurements
#' filename <- system.file(package = "citizenair", "data-raw", "citizenair-example.xls")
#' ca$setCitizenData(filename)
#' x <- ca$getCitizenData("meta")
#' x <- ca$getCitizenData("timeseries")
#' x <- ca$getCitizenData("phenomena")
#' x <- ca$getCitizenData("fileinfo")
#' ca$getClosestStation()
#' id <- ca$getClosestStation()
#' ca$setComparisonStation(id)
#' x <- ca$fetch_timeseries(id)
#' x <- ca$fetch_timeseries()
CitizenAir <- R6::R6Class("CitizenAir",
                      public = list(
                        endpoint = NULL,
                        stations = NULL, 
                        data = NULL,
                        closest_station = NA_character_,
                        initialize = function(endpoint, stations_area) {
                          self$endpoint <- endpoint
                          self$stations <- read_stations(endpoint)
                          if(!missing(stations_area)){
                            private$stations_area <- stations_area
                            stations_in_region <- self$getStations()
                            stations_in_region <- stations_in_region[sapply(1:nrow(stations_in_region), 
                                                                            FUN=function(idx) rgeos::gIntersects(stations_area, stations_in_region[idx, ])), ]
                            self$stations$stations <- self$stations$stations[self$stations$stations$id %in% stations_in_region$id, ]
                          }
                          self$data <- citizenair_userdata_empty()
                          self$closest_station <- NA_character_
                        },
                        setCitizenData = function(file, name=basename(file)){
                          x <- read_citizenair(file)
                          self$setComparisonStation()
                          self$data <- x
                        },
                        setComparisonStation = function(id = NA_character_){
                          self$closest_station <- as.character(id)
                        },
                        fetch_timeseries = function(id = self$getComparisonStation(), time_span=NULL) {  
                          stopifnot(length(id) == 1)
                          stopifnot(!is.na(id))
                          ## Find and create a stations object
                          x <- self$stations$stations
                          x <- as.data.frame(x)
                          x <- x[x$id %in% id, ]
                          x <- sp::SpatialPointsDataFrame(coords = x[, c("lon", "lat")], 
                                                          data = x, 
                                                          proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))  
                          st <- sensorweb4R::Station(id = x$id, label = x$label, geometry = x, endpoint = self$endpoint)
                          ## Get all metadata of time series of that station
                          ts <- sensorweb4R::timeseries(self$endpoint, station = st)
                          ts <- sensorweb4R::as.Timeseries(ts)
                          ts <- sensorweb4R::fetch(ts)
                          ## Fetch the data of the specified period
                          result <- list()
                          for(i in seq_along(ts)){
                            x <- ts[i]
                            if(is.null(time_span)){
                              to <- sensorweb4R::time(sensorweb4R::lastValue(x))
                              from <- as.POSIXct(seq.Date(from = as.Date(to), by = "-1 year", length.out = 2)[2], tz = "UTC")
                              timespan <- lubridate::as.interval(from, to)  
                            }else{
                              timespan <- time_span
                            }
                            df <- sensorweb4R::getData(x, timespan = timespan)
                            df <- as.data.frame(df)
                            df$date <- as.Date(df$time)
                            df$phenomena_id <- sensorweb4R::id(sensorweb4R::phenomenon(x))
                            df$phenomena <- self$getPhenomenaLabel(df$phenomena_id)
                            df$phenomena_label <- sensorweb4R::label(x)
                            result[[i]] <- df[, c("phenomena_id", "phenomena", "phenomena_label", "time", "value")]
                          }
                          result <- rbindlist(result)
                          result
                        },
                        getStations = function(){
                          x <- as.data.frame(self$stations$stations)
                          x <- sp::SpatialPointsDataFrame(coords = x[, c("lon", "lat")], 
                                                          data = x, proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))
                          x
                        },
                        getStationsArea = function(){
                          private$stations_area
                        },
                        getPhenomena = function(){
                          self$stations$phenomena$id
                        },
                        getPhenomenaLabel = function(id){
                          stopifnot(all(id %in% self$stations$phenomena$id))
                          txt_recode(id, from = self$stations$phenomena$id, to = self$stations$phenomena$label)
                        },
                        getComparisonStation = function(){
                          self$closest_station
                        },
                        getClosestStation = function(){
                          ## Get locations of own measurements
                          point <- self$getCitizenData(type = "meta")
                          point <- point[!is.na(point$lon) & !is.na(point$lat), ]
                          station <- NA_character_
                          if(nrow(point) > 0){
                            point <- head(point, 1)
                            station <- private$nearest_station(stations = self$getStations(), as.matrix(point[, c("lon", "lat")]))  
                            station <- station$id
                          }
                          station
                        },
                        getCitizenData = function(type = c("meta", "timeseries", "phenomena", "fileinfo")){
                          type <- match.arg(type)
                          if(type == "meta"){
                            if(is.na(self$data$filename)){
                              x <- data.frame(sheet_id = integer(), sheet = character(), device = character(), 
                                              lat = numeric(), lon = numeric(), 
                                              phenomena = character(), stringsAsFactors = FALSE)
                            }else{
                              x <- as.data.frame(self$data, type = "meta")  
                            }
                            
                          }else if(type == "timeseries"){
                            if(is.na(self$data$filename)){
                              x <- data.frame(sheet_id = integer(), time = as.POSIXct(character()), date = as.Date(character()), 
                                              phenomena = character(), value = numeric(), stringsAsFactors = FALSE)
                            }else{
                              x <- as.data.frame(self$data, type = "measurements")
                            }
                          }else if(type == "phenomena"){
                            if(is.na(self$data$filename)){
                              x <- character()
                            }else{
                              x <- self$data$phenomena
                            }
                          }else if(type == "fileinfo"){
                            if(is.na(self$data$filename)){
                              x <- data.frame(name = character(), file = character(), stringsAsFactors = FALSE)
                            }else{
                              x <- data.frame(name = self$data$filename, file = self$data$file, stringsAsFactors = FALSE)
                            }
                          }
                          x
                        }
                      ),
                      private = list(
                        stations_area = NULL,
                        nearest_station = function(stations, x){
                          d <- sp::spDistsN1(stations, x, longlat = TRUE)
                          stations[which.min(d), ]
                        }
                      )
)
