module Web.Data.Yahoo.Request where

import Data.List (intercalate)
import Data.Maybe (catMaybes)
import Data.Time.Calendar (Day, fromGregorian)
import Text.Printf (printf)

import Web.Data.Yahoo.Utils (dayAsEpoch)

data Ticker = Ticker String

class YahooParam a where
    key    :: a -> String
    symbol :: a -> String

paramToString :: YahooParam a => a -> String
paramToString :: a -> String
paramToString a
p = String -> String -> String -> String
forall r. PrintfType r => String -> r
printf String
"%s=%s" (a -> String
forall a. YahooParam a => a -> String
key a
p) (a -> String
forall a. YahooParam a => a -> String
symbol a
p)

data Interval = 
    Daily
    | Weekly
    | Monthly
    deriving (Int -> Interval -> String -> String
[Interval] -> String -> String
Interval -> String
(Int -> Interval -> String -> String)
-> (Interval -> String)
-> ([Interval] -> String -> String)
-> Show Interval
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [Interval] -> String -> String
$cshowList :: [Interval] -> String -> String
show :: Interval -> String
$cshow :: Interval -> String
showsPrec :: Int -> Interval -> String -> String
$cshowsPrec :: Int -> Interval -> String -> String
Show, Interval -> Interval -> Bool
(Interval -> Interval -> Bool)
-> (Interval -> Interval -> Bool) -> Eq Interval
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Interval -> Interval -> Bool
$c/= :: Interval -> Interval -> Bool
== :: Interval -> Interval -> Bool
$c== :: Interval -> Interval -> Bool
Eq)

instance YahooParam Interval where
    key :: Interval -> String
key Interval
_ = String
"interval"
    symbol :: Interval -> String
symbol Interval
Daily = String
"1d"
    symbol Interval
Weekly = String
"1wk"
    symbol Interval
Monthly = String
"1mo"

data Events =
    HistoricalPrices
    | Dividends
    | Splits

instance YahooParam Events where
    key :: Events -> String
key Events
_ = String
"events"
    symbol :: Events -> String
symbol Events
HistoricalPrices = String
"history"
    symbol Events
Dividends = String
"div"
    symbol Events
Splits = String
"split"

data FromEndpoint = FromEndpoint Day

instance YahooParam FromEndpoint where
    key :: FromEndpoint -> String
key FromEndpoint
_ = String
"period1"
    symbol :: FromEndpoint -> String
symbol (FromEndpoint Day
day) = Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ Day -> Integer
dayAsEpoch Day
day

data ToEndpoint = ToEndpoint Day

instance YahooParam ToEndpoint where
    key :: ToEndpoint -> String
key ToEndpoint
_ = String
"period2"
    symbol :: ToEndpoint -> String
symbol (ToEndpoint Day
day) = Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ Day -> Integer
dayAsEpoch Day
day

data TimeRange =
    After Day
    | Before Day
    | Range Day Day

timeRangeToEndpoints :: TimeRange -> (FromEndpoint, ToEndpoint)
timeRangeToEndpoints :: TimeRange -> (FromEndpoint, ToEndpoint)
timeRangeToEndpoints (After Day
day)     = (Day -> FromEndpoint
FromEndpoint Day
day, Day -> ToEndpoint
ToEndpoint (Day -> ToEndpoint) -> Day -> ToEndpoint
forall a b. (a -> b) -> a -> b
$ Integer -> Int -> Int -> Day
fromGregorian Integer
2050 Int
12 Int
31)
timeRangeToEndpoints (Before Day
day)    = (Day -> FromEndpoint
FromEndpoint (Day -> FromEndpoint) -> Day -> FromEndpoint
forall a b. (a -> b) -> a -> b
$ Integer -> Int -> Int -> Day
fromGregorian Integer
1970 Int
1 Int
1, Day -> ToEndpoint
ToEndpoint Day
day)
timeRangeToEndpoints (Range Day
from Day
to) = (Day -> FromEndpoint
FromEndpoint Day
from, Day -> ToEndpoint
ToEndpoint Day
to)

data YahooRequest = YahooRequest {
    YahooRequest -> Ticker
ticker   :: Ticker,
    YahooRequest -> Maybe Interval
interval :: Maybe Interval,
    YahooRequest -> Maybe TimeRange
period   :: Maybe TimeRange
}

requestUrl :: YahooRequest -> String
requestUrl :: YahooRequest -> String
requestUrl (YahooRequest { ticker :: YahooRequest -> Ticker
ticker = (Ticker String
t), interval :: YahooRequest -> Maybe Interval
interval = Maybe Interval
i, period :: YahooRequest -> Maybe TimeRange
period = Maybe TimeRange
p }) = 
    String -> String -> String -> String -> String
forall r. PrintfType r => String -> r
printf String
"%s/%s%s" String
baseUrl String
t ([String] -> String
queryString [String]
queryParams)
    where
        baseUrl :: String
        baseUrl :: String
baseUrl = String
"http://query1.finance.yahoo.com/v7/finance/download"

        rangeEndpoints :: Maybe (FromEndpoint, ToEndpoint)
        rangeEndpoints :: Maybe (FromEndpoint, ToEndpoint)
rangeEndpoints = (TimeRange -> (FromEndpoint, ToEndpoint))
-> Maybe TimeRange -> Maybe (FromEndpoint, ToEndpoint)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap TimeRange -> (FromEndpoint, ToEndpoint)
timeRangeToEndpoints Maybe TimeRange
p

        fromEndpoint :: Maybe FromEndpoint
        fromEndpoint :: Maybe FromEndpoint
fromEndpoint = ((FromEndpoint, ToEndpoint) -> FromEndpoint)
-> Maybe (FromEndpoint, ToEndpoint) -> Maybe FromEndpoint
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (FromEndpoint, ToEndpoint) -> FromEndpoint
forall a b. (a, b) -> a
fst Maybe (FromEndpoint, ToEndpoint)
rangeEndpoints

        toEndpoint :: Maybe ToEndpoint
        toEndpoint :: Maybe ToEndpoint
toEndpoint = ((FromEndpoint, ToEndpoint) -> ToEndpoint)
-> Maybe (FromEndpoint, ToEndpoint) -> Maybe ToEndpoint
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (FromEndpoint, ToEndpoint) -> ToEndpoint
forall a b. (a, b) -> b
snd Maybe (FromEndpoint, ToEndpoint)
rangeEndpoints

        queryParams :: [String]
        queryParams :: [String]
queryParams = [Maybe String] -> [String]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe String] -> [String]) -> [Maybe String] -> [String]
forall a b. (a -> b) -> a -> b
$ [
                (Interval -> String) -> Maybe Interval -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Interval -> String
forall a. YahooParam a => a -> String
paramToString Maybe Interval
i,
                (FromEndpoint -> String) -> Maybe FromEndpoint -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FromEndpoint -> String
forall a. YahooParam a => a -> String
paramToString Maybe FromEndpoint
fromEndpoint,
                (ToEndpoint -> String) -> Maybe ToEndpoint -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ToEndpoint -> String
forall a. YahooParam a => a -> String
paramToString Maybe ToEndpoint
toEndpoint
            ]

        queryString :: [String] -> String
        queryString :: [String] -> String
queryString [] = String
""
        queryString [String]
ps = String -> String -> String
forall r. PrintfType r => String -> r
printf String
"?%s" (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"&" [String]
ps)