{-|
Module: TimezoneDetect
Portability: POSIX

Exposes utilities derived from the excellent ZoneDetect library <https://github.com/BertoldVdb/ZoneDetect>.
To use this module, you need to obtain database files from the aforementioned library's server.

Currently, only one function for looking up the name of a Timezone is provided, but the underlying
library also has richer functions for opening timezone database files, finding applicable zones, etc.
-}

module TimezoneDetect where

import Foreign.ZoneDetect
import System.IO.Unsafe (unsafePerformIO)
import Foreign.C.String (peekCAString, withCAString)
import Foreign (nullPtr)

-- | Alias for clarity, timezones are short strings that follow the IANA conventions
-- documented here:
-- https://data.iana.org/time-zones/tz-link.html
type TimezoneName = String

-- | Given a timezone database, latitude and longitude, try to determine the
-- timezone name. Follow the instructions in the C library's repository
-- to obtain timezone database files (<https://github.com/BertoldVdb/ZoneDetect/tree/05567e367576d7f3efa00083b7661a01e43dc8ca/database>)
-- Once in possesion of said files, the lookup looks as follows:
-- 
-- >>> lookupTimezone "./test/tz_db/timezone21.bin" 40.7831 (-73.9712)
-- Right "America/New_York"
-- 
-- Failure conditions are: invalid database file, or invalid coordinates,
-- both are returned as `Left` values.
lookupTimezone :: FilePath -> Float -> Float -> Either String TimezoneName
lookupTimezone databaseLocation lat lng =
    unsafePerformIO $ do
        zdPtr <- withCAString databaseLocation $ \dbl -> c_ZDOpenDatabase dbl
        if zdPtr == nullPtr then
            pure $ Left (databaseLocation <> " is not a valid timezone database.")
        else do
            tzName <- c_ZDHelperSimpleLookupString zdPtr
                                                   (realToFrac lat)
                                                   (realToFrac lng)
            if tzName == nullPtr then
                pure $ Left "Invalid coordinates."
            else
                peekCAString tzName >>= (pure . Right)