{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}

module Data.Aviation.Stratux.Types.Traffic(
  Traffic(..)
, HasTraffic(..)
) where

import Control.Applicative(Applicative((<*>)))
import Control.Category(Category((.)))
import Control.Lens(makeClassy, lens)
import Data.Aviation.Stratux.Types.EmitterCategory(EmitterCategory)
import Data.Aviation.Stratux.Types.IcaoAddr(IcaoAddr, HasIcaoAddr(icaoAddr))
import Data.Aviation.Stratux.Types.TargetType(TargetType)
import Data.Aviation.Stratux.Types.UTCTimes(HasUTCTimes(utcTimes))
import Data.Aviation.Stratux.Types.Word8s(HasWord8s(word8s))
import Data.Aeson(FromJSON(parseJSON), ToJSON(toJSON), (.:), withObject, (.=), object)
import Data.Bool(Bool)
import Data.Eq(Eq)
import Data.Int(Int)
import Data.Functor((<$>))
import Data.Ord(Ord)
import Data.String(String)
import Data.Time(UTCTime)
import Prelude(Double, Show)

-- $setup
-- >>> :set -XOverloadedStrings
-- >>> import Control.Lens
-- >>> import Data.Aeson(decode, encode)
-- >>> import Data.Maybe(Maybe)
-- >>> import Prelude
-- >>> import Data.Aviation.Stratux.Types.EmitterCategory
-- >>> import Data.Aviation.Stratux.Types.IcaoAddr
-- >>> import Data.Aviation.Stratux.Types.TargetType
-- >>> import Data.Time


-- https://i.imgur.com/nOqGbfr.png
data Traffic =
  Traffic {
    icaoAddrTraffic :: IcaoAddr
  , _tail :: String -- tail
  , _emitterCategory :: EmitterCategory
  , _onGround :: Bool -- on-ground
  , _addressType :: Int -- address type
  , _targetType :: TargetType
  , _signalLevel :: Double -- signal level
  , _positionValid :: Bool -- position valid
  , _latitude :: Double -- latitude
  , _longitude :: Double -- longitude
  , _altitude :: Int -- altitude
  , _gnssDiffFromBaroAlt :: Int -- GNSS altitude above WGS84 datum
  , _altIsGnss :: Bool -- AltIsGNSS,  Pressure alt = 0; GNSS alt = 1
  , _navigationIntegrityCategory :: Int -- Navigation Integrity Category.
  , _navigationAccuracyCategoryForPosition :: Int -- Navigation Accuracy Category for Position
  , _track :: Int -- Track degrees true
  , _speed :: Int -- speed (knots)
  , _speedValid :: Bool -- speed valid
  , _verticalVelocity :: Int -- Vertical velocity (feet/minute)
  , _timestamp :: UTCTime -- Timestamp
  , _age :: Double -- age
  , _lastSeen :: UTCTime -- last seen
  , _lastAltitude :: UTCTime -- last altitude
  , _lastGnssDiff :: UTCTime -- last GNSS diff
  , _lastGnssDiffAltitude :: Int -- last GNSS diff altitude
  , _lastSpeed :: UTCTime -- last speed
  , _lastSource :: Int -- last source
  , _extrapolatedPosition :: Bool -- extrapolated position
  , _bearing :: Double -- Bearing in degrees true to traffic from ownship, if it can be calculated.
  , _distanceToTrafficFromOwnship :: Double -- Distance to traffic from ownship, if it can be calculated.
  } deriving (Eq, Ord, Show)

makeClassy ''Traffic

-- |
--
-- >>> decode "{\"Icao_addr\":8153826,\"Tail\":\"SVY22\",\"Emitter_category\":1,\"OnGround\":false,\"Addr_type\":0,\"TargetType\":1,\"SignalLevel\":-30.920514783877277,\"Position_valid\":true,\"Lat\":-27.501154,\"Lng\":153.21422,\"Alt\":12500,\"GnssDiffFromBaroAlt\":650,\"AltIsGNSS\":false,\"NIC\":8,\"NACp\":9,\"Track\":89,\"Speed\":205,\"Speed_valid\":true,\"Vvel\":0,\"Timestamp\":\"2016-05-24T01:13:18.189Z\",\"Age\":40.8,\"Last_seen\":\"0001-01-01T03:30:38.2Z\",\"Last_alt\":\"0001-01-01T03:30:38.55Z\",\"Last_GnssDiff\":\"0001-01-01T03:30:36.75Z\",\"Last_GnssDiffAlt\":12500,\"Last_speed\":\"0001-01-01T03:30:36.75Z\",\"Last_source\":1,\"ExtrapolatedPosition\":false,\"Bearing\":0,\"Distance\":0}" :: Maybe Traffic
-- Just (Traffic {icaoAddrTraffic = IcaoAddr {_icaoAddrWord0 = 124, _icaoAddrWord1 = 106, _icaoAddrWord2 = 226}, _tail = "SVY22", _emitterCategory = Light, _onGround = False, _addressType = 0, _targetType = Adsb, _signalLevel = -30.920514783877277, _positionValid = True, _latitude = -27.501154, _longitude = 153.21422, _altitude = 12500, _gnssDiffFromBaroAlt = 650, _altIsGnss = False, _navigationIntegrityCategory = 8, _navigationAccuracyCategoryForPosition = 9, _track = 89, _speed = 205, _speedValid = True, _verticalVelocity = 0, _timestamp = 2016-05-24 01:13:18.189 UTC, _age = 40.8, _lastSeen = 0001-01-01 03:30:38.2 UTC, _lastAltitude = 0001-01-01 03:30:38.55 UTC, _lastGnssDiff = 0001-01-01 03:30:36.75 UTC, _lastGnssDiffAltitude = 12500, _lastSpeed = 0001-01-01 03:30:36.75 UTC, _lastSource = 1, _extrapolatedPosition = False, _bearing = 0.0, _distanceToTrafficFromOwnship = 0.0})
instance FromJSON Traffic where
  parseJSON =
    withObject "Traffic" (\x ->
      Traffic <$>
        x .: "Icao_addr" <*>
        x .: "Tail" <*>
        x .: "Emitter_category" <*>
        x .: "OnGround" <*>
        x .: "Addr_type" <*>
        x .: "TargetType" <*>
        x .: "SignalLevel" <*>
        x .: "Position_valid" <*>
        x .: "Lat" <*>
        x .: "Lng" <*>
        x .: "Alt" <*>
        x .: "GnssDiffFromBaroAlt" <*>
        x .: "AltIsGNSS" <*>
        x .: "NIC" <*>
        x .: "NACp" <*>
        x .: "Track" <*>
        x .: "Speed" <*>
        x .: "Speed_valid" <*>
        x .: "Vvel" <*>
        x .: "Timestamp" <*>
        x .: "Age" <*>
        x .: "Last_seen" <*>
        x .: "Last_alt" <*>
        x .: "Last_GnssDiff" <*>
        x .: "Last_GnssDiffAlt" <*>
        x .: "Last_speed" <*>
        x .: "Last_source" <*>
        x .: "ExtrapolatedPosition" <*>
        x .: "Bearing" <*>
        x .: "Distance")

-- |
--
-- >>> encode (Traffic (IcaoAddr 124 106 226) "SVY22" Light False 0 Adsb (-30.920514783877277) True (-27.501154) 153.21422 12500 650 False 8 9 89 205 True 0 (UTCTime (fromGregorian 1 1 1) 3597) 40.8 (UTCTime (fromGregorian 1 1 1) 259) (UTCTime (fromGregorian 1 1 1) 23) (UTCTime (fromGregorian 1 1 1) 597) 12500 (UTCTime (fromGregorian 2016 5 24) 237) 1 False 0.0 0.0)
-- "{\"OnGround\":false,\"Bearing\":0,\"ExtrapolatedPosition\":false,\"NIC\":8,\"Last_alt\":\"0001-01-01T00:00:23Z\",\"Track\":89,\"Last_speed\":\"2016-05-24T00:03:57Z\",\"GnssDiffFromBaroAlt\":650,\"Last_seen\":\"0001-01-01T00:04:19Z\",\"Icao_addr\":8153826,\"SignalLevel\":-30.920514783877277,\"Distance\":0,\"Age\":40.8,\"Speed_valid\":true,\"TargetType\":1,\"Lat\":-27.501154,\"Vvel\":0,\"NACp\":9,\"Addr_type\":0,\"Speed\":205,\"AltIsGNSS\":false,\"Lng\":153.21422,\"Tail\":\"SVY22\",\"Last_GnssDiffAlt\":12500,\"Position_valid\":true,\"Last_source\":1,\"Timestamp\":\"0001-01-01T00:59:57Z\",\"Last_GnssDiff\":\"0001-01-01T00:09:57Z\",\"Alt\":12500,\"Emitter_category\":1}"

instance ToJSON Traffic where
  toJSON (Traffic icaoAddr_ tail_ emitterCategory_ onGround_ addressType_ targetType_ signalLevel_ positionValid_ latitude_ longitude_ altitude_ gnssDiffFromBaroAlt_ altIsGnss_ navigationIntegrityCategory_ navigationAccuracyCategoryForPosition_ track_ speed_ speedValid_ verticalVelocity_ timestamp_ age_ lastSeen_ lastAltitude_ lastGnssDiff_ lastGnssDiffAltitude_ lastSpeed_ lastSource_ extrapolatedPosition_ bearing_ distanceToTrafficFromOwnship_) =
    object [
      "Icao_addr" .= icaoAddr_
    , "Tail" .= tail_
    , "Emitter_category" .= emitterCategory_
    , "OnGround" .= onGround_
    , "Addr_type" .= addressType_
    , "TargetType" .= targetType_
    , "SignalLevel" .= signalLevel_
    , "Position_valid" .= positionValid_
    , "Lat" .= latitude_
    , "Lng" .= longitude_
    , "Alt" .= altitude_
    , "GnssDiffFromBaroAlt" .= gnssDiffFromBaroAlt_
    , "AltIsGNSS" .= altIsGnss_
    , "NIC" .= navigationIntegrityCategory_
    , "NACp" .= navigationAccuracyCategoryForPosition_
    , "Track" .= track_
    , "Speed" .= speed_
    , "Speed_valid" .= speedValid_
    , "Vvel" .= verticalVelocity_
    , "Timestamp" .= timestamp_
    , "Age" .= age_
    , "Last_seen" .= lastSeen_
    , "Last_alt" .= lastAltitude_
    , "Last_GnssDiff" .= lastGnssDiff_
    , "Last_GnssDiffAlt" .= lastGnssDiffAltitude_
    , "Last_speed" .= lastSpeed_
    , "Last_source" .= lastSource_
    , "ExtrapolatedPosition" .= extrapolatedPosition_
    , "Bearing" .= bearing_
    , "Distance" .= distanceToTrafficFromOwnship_
    ]

instance HasIcaoAddr Traffic where
  icaoAddr =
    lens
      (\(Traffic x _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _) -> x)
      (\(Traffic _ tail_ emitterCategory_ onGround_ addressType_ targetType_ signalLevel_ positionValid_ latitude_ longitude_ altitude_ gnssDiffFromBaroAlt_ altIsGnss_ navigationIntegrityCategory_ navigationAccuracyCategoryForPosition_ track_ speed_ speedValid_ verticalVelocity_ timestamp_ age_ lastSeen_ lastAltitude_ lastGnssDiff_ lastGnssDiffAltitude_ lastSpeed_ lastSource_ extrapolatedPosition_ bearing_ distanceToTrafficFromOwnship_) x -> Traffic x tail_ emitterCategory_ onGround_ addressType_ targetType_ signalLevel_ positionValid_ latitude_ longitude_ altitude_ gnssDiffFromBaroAlt_ altIsGnss_ navigationIntegrityCategory_ navigationAccuracyCategoryForPosition_ track_ speed_ speedValid_ verticalVelocity_ timestamp_ age_ lastSeen_ lastAltitude_ lastGnssDiff_ lastGnssDiffAltitude_ lastSpeed_ lastSource_ extrapolatedPosition_ bearing_ distanceToTrafficFromOwnship_)

instance HasWord8s Traffic where
  word8s =
    icaoAddr . word8s

instance HasUTCTimes Traffic where
  utcTimes f (Traffic icaoAddr_ tail_ emitterCategory_ onGround_ addressType_ targetType_ signalLevel_ positionValid_ latitude_ longitude_ altitude_ gnssDiffFromBaroAlt_ altIsGnss_ navigationIntegrityCategory_ navigationAccuracyCategoryForPosition_ track_ speed_ speedValid_ verticalVelocity_ timestamp_ age_ lastSeen_ lastAltitude_ lastGnssDiff_ lastGnssDiffAltitude_ lastSpeed_ lastSource_ extrapolatedPosition_ bearing_ distanceToTrafficFromOwnship_) =
    (\timestamp__ lastSeen__ lastAltitude__ lastGnssDiff__ lastSpeed__ ->
      Traffic icaoAddr_ tail_ emitterCategory_ onGround_ addressType_ targetType_ signalLevel_ positionValid_ latitude_ longitude_ altitude_ gnssDiffFromBaroAlt_ altIsGnss_ navigationIntegrityCategory_ navigationAccuracyCategoryForPosition_ track_ speed_ speedValid_ verticalVelocity_ timestamp__ age_ lastSeen__ lastAltitude__ lastGnssDiff__ lastGnssDiffAltitude_ lastSpeed__ lastSource_ extrapolatedPosition_ bearing_ distanceToTrafficFromOwnship_) <$>
      f timestamp_ <*>
      f lastSeen_ <*>
      f lastAltitude_ <*>
      f lastGnssDiff_ <*>
      f lastSpeed_