module Frontend.Api where

import Prelude

import Affjax as AX
import Affjax.ResponseFormat as ResponseFormat
import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:))
import Data.Bifunctor (lmap)
import Data.DateTime (DateTime)
import Data.Either (Either(..))
import Data.Newtype (unwrap)
import Data.Time.Duration (class Duration, Seconds, convertDuration)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Frontend.Tracklets (TrackletSOA)
import Frontend.Types (Config, Info, Instant, MaybeInstant, Tracklets)
import Frontend.Util (datetimeToSeconds, loadImage)
import Graphics.Canvas (CanvasImageSource)
import Web.File.Url (createObjectURL)

fetchInfo :: Aff (Either String Info)
fetchInfo = getJson "/info"

fetchConfig :: Aff (Either String Config)
fetchConfig = getJson "/config"

fetchInfoImage :: String -> Aff (Either String CanvasImageSource)
fetchInfoImage url = do
  result <- lmap AX.printError <$> AX.get ResponseFormat.blob ("/info/" <> url)
  case result of
    Left err -> pure $ Left err
    Right response -> do
      blobUrl <- liftEffect $ createObjectURL response.body
      image <- loadImage blobUrl
      pure (Right image)


fetchTracklets :: DateTime -> DateTime -> Int -> Aff (Either String Tracklets)
fetchTracklets from to stride = let
  from' = show $ datetimeToSeconds from
  to' = show $ datetimeToSeconds to
  stride' = show stride
  in getJson $ "/tracklet_pos?from=" <> from' <> "&to=" <> to' <> "&stride=" <> stride'


fetchTrackletsSOA :: DateTime -> DateTime -> Int -> Aff (Either String (Array TrackletSOA))
fetchTrackletsSOA from to stride = let
  from' = show $ datetimeToSeconds from
  to' = show $ datetimeToSeconds to
  stride' = show stride
  in getJson $ "/tracklet_pos?from=" <> from' <> "&to=" <> to' <> "&stride=" <> stride'

fetchInstantInterval :: forall d. Duration d => DateTime -> DateTime -> d -> Aff (Either String (Array MaybeInstant))
fetchInstantInterval from to stride = let
  from' = show $ datetimeToSeconds from
  to' = show $ datetimeToSeconds to
  stride' = show $ unwrap (convertDuration stride :: Seconds)
  in getJson $ "/time_range_t?from=" <> from' <> "&to=" <> to' <> "&stride=" <> stride'

fetchInstantRange :: DateTime -> DateTime -> Number -> Aff (Either String (Array Instant))
fetchInstantRange from to stride = let
  from' = show $ datetimeToSeconds from
  to' = show $ datetimeToSeconds to
  stride' = show stride
  in getJson $ "/time_range_n?from=" <> from' <> "&to=" <> to' <> "&stride=" <> stride'

getJson :: forall a. DecodeJson a => String -> Aff (Either String a)
getJson url = do
  result <- lmap AX.printError <$> AX.get ResponseFormat.json url
  pure $ do
    response <- result
    o <- lmap show $ decodeJson response.body
    ok <- lmap show $ o .: "Ok"
    lmap show $ decodeJson ok