-- | This module provides the JourneyCharts and HTMLGenerator 
-- modules with statistics for the charts, and the journey statistics
module Data.GPS.Gps2HtmlReport.JourneyStats where

import Data.GPS -- hiding (speed)
import Data.Maybe
import Data.Time.LocalTime
import Data.Time.Clock
import Data.Time.Calendar
import Text.XML.XSD.DateTime

-- | Takes all WayPoints, and creates a list of tuples containing (TimeStamp,Elevation)
ptsElevation :: [WptType] -> [(LocalTime,Double)]
ptsElevation = map (\point -> (utcToLocalTime dfltTZ (Text.XML.XSD.DateTime.toUTCTime (fromJust $ time point)) , fromJust $ ele point))

-- | Takes all WayPoints, and creates a list of tuples containing (TimeStamp,AvrSpeedAtThisPoint)
avrSpeedOverTime :: [(LocalTime,Speed)] -> Double -> Double -> [(LocalTime,Speed)] -> [(LocalTime,Speed)]
avrSpeedOverTime [] _ _ _ = []
avrSpeedOverTime [spd] totalSpeed numPoints iteratedAvr = iteratedAvr ++ [(fst spd, (snd spd + totalSpeed) / (numPoints+1))]
avrSpeedOverTime (spd:spds) totalSpeed numPoints iteratedAvr = avrSpeedOverTime [spd] totalSpeed numPoints iteratedAvr ++ avrSpeedOverTime spds (snd spd + totalSpeed) (numPoints+1) iteratedAvr

-- | Takes all WayPoints, and creates a list of tuples containing (TimeStamp,SpeedAtThisPoint) 
speedAtPoints :: [WptType] -> [(LocalTime,Speed)]
speedAtPoints [] = []
speedAtPoints (point:points) = (lclTime $ fromJust (time point),0.0) : speedAtPoints' point points

speedAtPoints' :: WptType -> [WptType] -> [(LocalTime, Speed)]
speedAtPoints' _ [] = []
speedAtPoints' prev [x]
         | isJust (time x) = [(lclTime $ fromJust (time x), fromJust $ speed prev x)]
         | otherwise = []
speedAtPoints' prev (x:xs)
         | isJust (time x) = (lclTime $ fromJust (time x), fromJust $ speed prev x) : speedAtPoints' x xs
         | otherwise = [] ++ speedAtPoints' x xs

-- | Takes all WayPoints, and creates a list of tuples containing (TimeStamp,JourneyDistanceAtPoint)
accumDistance :: [WptType] -> Double -> [(LocalTime,Distance)]
accumDistance [] _ = []
accumDistance [x] _ = [(lclTime $ fromJust (time x),0.0)]
accumDistance (x:xs) acc = 
   let dist = distance x (head xs)           
   in (lclTime $ fromJust (time x), dist + acc ) : accumDistance xs (dist + acc)

-- | Takes all WayPoints, an element in wptType, and an Eq function, returning a single WayPoint
findPoint :: [WptType] -> WptType -> (WptType -> Maybe Double) -> (Double -> Double -> Bool) -> Maybe (LocalTime,Double)
findPoint [] _ _ _ = Nothing
findPoint (point:points) currSelected wayPointElement equalityF
   | equalityF (fromJust $ wayPointElement point) (fromJust $ wayPointElement currSelected) = findPoint points point wayPointElement equalityF
   | otherwise = if null points
                      then Just (lclTime (fromJust $ time currSelected), fromJust $ ele currSelected)
                      else findPoint points currSelected wayPointElement equalityF

-- | Calculates the total journey distance
journeyDistance :: (Lat a, Lon a) => [a] -> Distance
journeyDistance [] = 0.0
journeyDistance [_] = 0.0
journeyDistance (point:points) = distance point (head points) + journeyDistance points

-- | Calculates the average speed of the journey
meanJourneySpeed :: (Lat a, Lon a, Time a) => [a] -> Distance
meanJourneySpeed points = journeyDistance points / realToFrac (journeyTime points)

-- | Calculates the maximum speed
maxSpeed :: [WptType] -> Speed
maxSpeed points =
            let speedTuple = speedAtPoints points
                speedList = map snd speedTuple
                maxSpeed = foldr max 0.0 speedList
            in maxSpeed

-- | Calculates the average elevation throughout the journey
meanElevation :: Ele a => [a] -> Double
meanElevation points = 
            let elevationVals = map (fromJust . ele) points
                totalElevation = foldr (+) 0.0 elevationVals
                theMean = totalElevation / fromIntegral (length points)
            in theMean

-- | Calculates the total journey time
journeyTime :: Time a => [a] -> NominalDiffTime
journeyTime [] = fromInteger 0
journeyTime [_] = fromInteger 0
journeyTime (point:points) = 
             let startTime = toUTCTime (fromJust (time point))
                 endTime = toUTCTime (fromJust (time $ last points))
             in diffUTCTime endTime startTime

-- | Extracts the date of the journey (from the first WayPoint)
dateOfJourney :: Time a => [a] -> Maybe Day
dateOfJourney [] = Nothing
dateOfJourney (point:_) = Just $ utctDay $ toUTCTime $ fromJust (time point)

lclTime :: DateTime -> LocalTime
lclTime dteTime = utcToLocalTime dfltTZ (Text.XML.XSD.DateTime.toUTCTime dteTime)

dfltTZ :: TimeZone
dfltTZ = TimeZone {timeZoneMinutes=0,timeZoneSummerOnly=False,timeZoneName="GMT"}