module Point exposing(Point, decoder, downsample, duration, subseq, startTime, centre)

import Json.Decode as D
import Pos exposing (Pos)


type alias Point =
    { time : Float
    , pos : Pos
    , cadence : Maybe Int
    , power : Maybe Int
    , heartRate : Maybe Int
    }

posDecoder : D.Decoder Pos
posDecoder = D.map3 Pos
             (D.field "lat" D.float)
             (D.field "lon" D.float)
             (D.field "ele" (D.maybe D.float))


decoder : D.Decoder Point
decoder = D.map5 Point
          (D.field "time" D.float)
          (D.field "pos" posDecoder)
          (D.field "cadence" (D.maybe D.int))
          (D.field "power" (D.maybe D.int))
          (D.field "heartRate" (D.maybe D.int))


last x xs =
    case xs of
        [] -> x
        (x_::xs_) -> last x_ xs_

-- divide the points into n equal time buckets and return the first
-- point in each
downsample : Int -> List Point -> List Point
downsample n points =
    let
        nextpoint step prev points_ =
            case points_ of
                (next::rest) ->
                    if next.time - prev.time > step
                    then prev :: (nextpoint step next rest)
                    else nextpoint step prev rest
                _ -> [prev]
    in
        case points of
            (first::rest) ->
                let step = (duration points) / (toFloat n)
                in nextpoint step first rest
            [] -> []

duration : List Point -> Float
duration points =
    case points of
        (p::ps) -> (last p ps).time - p.time
        _ -> 0

startTime : List Point -> Maybe Float
startTime points =
    case points of
        (p::_) -> Just p.time
        _ -> Nothing

type Bound = Bound Pos Pos | NoBound

extendBound : Pos -> Bound -> Bound
extendBound pos b =
    let {lat, lon} = pos
    in case b of
           (Bound p1 p2) ->
               Bound
               (Pos (min lat p1.lat)  (min lon p1.lon) Nothing)
               (Pos (max lat p2.lat)  (max lon p2.lon) Nothing)
           NoBound ->
               Bound pos pos

bounds : List Point -> Bound
bounds points =
    List.foldr extendBound NoBound (List.map .pos points)

centre : List Point -> Pos
centre points =
    case bounds points of
        Bound min max -> Pos
                         ((max.lat + min.lat) / 2)
                         ((max.lon + min.lon) / 2)
                             Nothing
        NoBound -> Pos 0 0 Nothing

subseq : List Point -> Float -> Float -> List Point
subseq points start dur =
    case points of
        [] -> []
        (p::ps) ->
            if p.time < start
            then subseq ps start dur
            else if p.time >= (start + dur)
                 then []
                 else p::(subseq ps start dur)
