--------------------------------------------------------------------
-- |
-- Module      : Flickr.Places
-- Description : flickr.places - geo locating photos.
-- Copyright   : (c) Sigbjorn Finne, 2008
-- License     : BSD3
--
-- Maintainer  : Sigbjorn Finne <sof@forkIO.com>
-- Stability   : provisional
-- Portability : portable
--
-- flickr.places API, locating photos by places and geo.
--------------------------------------------------------------------
module Flickr.Places
       ( find                        -- :: String -> FM (PlaceQuery, [Place])
       , findByLatLon                -- :: Latitude -> Longitude -> Maybe Accuracy -> FM (PlaceQuery, [Place)]
       , getChildrenWithPhotosPublic -- :: Either PlaceID WhereOnEarthID -> FM (PlaceQuery, [Place])
       , getInfo              -- :: Either PlaceID WhereOnEarthID -> FM LocationPlace
       , getInfoByUrl         -- :: URLString -> FM LocationPlace
       , getPlaceTypes        -- :: FM [PlaceType]

       , placesForBoundingBox -- :: BoundingBox -> Maybe PlaceTypeName -> Maybe PlaceTypeId -> FM [Place]
       , placesForContacts    -- :: PlaceType -> Maybe WhereOnEarthID
                              -- -> Maybe PlaceTypeId -> Maybe Threshold
                  -- -> Maybe String -> FM [Place]
       , placesForTags   -- :: PlaceType
                         -- -> Maybe WhereOnEarthID
                     -- -> Maybe PlaceTypeId
                     -- -> Maybe Threshold
                     -- -> Maybe [Tag]
                     -- -> Maybe String -- tag-mode
                     -- -> Maybe MachineTag
                     -- -> FM [Place]
       , placesForUser   -- :: PlaceTypeName
                         -- -> Maybe WhereOnEarthID
                     -- -> Maybe PlaceTypeId
                     -- -> Maybe Threshold
                     -- -> FM [Place]
       , resolvePlaceId  -- :: PlaceID -> FM LocationPlace
       , resolvePlaceURL -- :: URLString -> FM LocationPlace

       , tagsForPlace    -- :: Either PlaceID WhereOnEarthID -> DateDetails -> FM [TagInfo]
       ) where

import Flickr.Monad
import Flickr.Types
import Flickr.Types.Import

import Data.List ( intercalate )

-- | Return a list of place IDs for a query string.
-- The flickr.places.find method is not a geocoder.
-- It will round up to the nearest place type to
-- which place IDs apply. For example, if you pass
-- it a street level address it will return the city
-- that contains the address rather than the street,
-- or building, itself.
find :: String -> FM (PlaceQuery, [Place])
find q =
  flickTranslate toPlaces $
    flickrCall "flickr.places.find"
               [("query", q)]

-- | Return a place ID for a latitude, longitude and accuracy triple.
--
-- The flickr.places.findByLatLon method is not meant to be
-- a (reverse) geocoder in the traditional sense. It is designed
-- to allow users to find photos for "places" and will round
-- up to the nearest place type to which corresponding place IDs
-- apply.
--
-- For example, if you pass it a street level coordinate it will
-- return the city that contains the point rather than the street,
-- or building, itself.
--
-- It will also truncate latitudes and longitudes to three
-- decimal points.
findByLatLon :: Latitude -> Longitude -> Maybe Accuracy -> FM (PlaceQuery, [Place])
findByLatLon la lon acc =
  flickTranslate toPlaces $
    flickCall "flickr.places.findByLatLon"
              (mbArg "accuracy" (fmap show acc) $
             [("lat", la),("lon", lon)])

-- | Return a list of locations with public photos that are parented by a Where on Earth (WOE) or Places ID.
getChildrenWithPhotosPublic :: Either PlaceID WhereOnEarthID
                            -> FM (PlaceQuery, [Place])
getChildrenWithPhotosPublic pw =
  flickTranslate toPlaces $
    flickrCall "flickr.places.getChildrenWithPhotosPublic"
               (eiArg "place_id" "woe_id" pw [])

-- | Lookup information about a place, by its flickr.com/places URL.
getInfoByUrl :: URLString -> FM LocationPlace
getInfoByUrl url =
  flickTranslate toLocationPlace $
    flickrCall "flickr.places.getInfoByUrl"
               [ ("url", url) ]

-- | Get informations about a place.
getInfo :: Either PlaceID WhereOnEarthID -> FM LocationPlace
getInfo pw =
  flickTranslate toLocationPlace $
    flickrCall "flickr.places.getInfo"
               (eiArg "place_id" "woe_id" pw [])

-- | Fetches a list of available place types for Flickr.
getPlaceTypes :: FM [PlaceType]
getPlaceTypes =
  flickTranslate toPlaceTypes $
     flickrCall "flickr.places.getPlaceTypes" []

-- | return all the locations of a matching place type for a bounding box.
placesForBoundingBox :: BoundingBox
                     -> Maybe PlaceTypeName
             -> Maybe PlaceTypeId
             -> FM [Place]
placesForBoundingBox bbox pn pid =
 flickTranslate toPlacesList $
   flickCall "flickr.places.placesForBoundingBox"
             (mbArg "place_type" pn  $
          mbArg "place_type_id" pid   $
            [("bbox", intercalate "," $ map show $
                    [ bboxMinLongitude bbox
                , bboxMinLatitude  bbox
                , bboxMaxLongitude bbox
                , bboxMaxLatitude  bbox
                ])])

-- | Return a list of the top 100 unique places clustered by a given placetype for a user's contacts.
placesForContacts :: Either PlaceTypeName WhereOnEarthID
          -> Maybe PlaceTypeId
          -> Maybe Threshold
          -> Maybe String
          -> FM [Place]
placesForContacts eiPW pid th contacts = withReadPerm $
 flickTranslate toPlacesList $
   flickCall "flickr.places.placesForContacts"
             (mbArg "woe_id" woe_id  $
          mbArg "place_type" pnm   $
          mbArg "place_type_id" pid   $
          mbArg "threshold" (fmap show th) $
          mbArg "contacts" contacts $
           [])
 where
   pnm    = either Just (const Nothing) eiPW
   woe_id = either (const Nothing) Just eiPW

-- | Return a list of the top 100 unique places clustered by a given placetype for a user.
placesForUser :: Either PlaceID   WhereOnEarthID
          -> Either PlaceTypeName PlaceTypeId
          -> Maybe Threshold
          -> DateDetails
          -> FM [Place]
placesForUser eiPW eiPP th mbDates = withReadPerm $
 flickTranslate toPlacesList $
   flickCall "flickr.places.placesForUser"
             (mbArg "place_id" place_id   $
          mbArg "woe_id"   woe_id     $
          mbArg "place_type_id" ptid  $
          mbArg "place_type"    pt    $
          mbArg "threshold" (fmap show th) $
          mbArg "min_taken_date" (dateMinTaken mbDates) $
          mbArg "max_taken_date" (dateMaxTaken mbDates) $
          mbArg "min_upload_date" (dateMinUpload mbDates) $
          mbArg "max_upload_date" (dateMaxUpload mbDates)
                [])
 where
   place_id = either Just (const Nothing) eiPW
   woe_id   = either (const Nothing) Just eiPW
   ptid     = either Just (const Nothing) eiPP
   pt       = either (const Nothing) Just eiPP

-- | Return a list of the top 100 unique places clustered by a given placetype for set of tags or machine tags.
placesForTags :: Either PlaceTypeName WhereOnEarthID
          -> Maybe PlaceID
          -> Maybe Threshold
          -> Maybe [Tag]
          -> Maybe TagMode -- tag-mode
          -> Maybe [MachineTag]
          -> Maybe TagMode
          -> DateDetails
          -> FM [Place]
placesForTags eiPW pid th tags tag_mode mt mt_mode mbDates = withReadPerm $
 flickTranslate toPlacesList $
   flickCall "flickr.places.placesForTags"
             (mbArg "woe_id" woe_id  $
          mbArg "place_type" pnm   $
          mbArg "place_type_id" pid   $
          mbArg "threshold" (fmap show th) $
          mbArg "tags" (fmap (intercalate ",") tags) $
          mbArg "tag_mode" tag_mode $
          mbArg "machine_tags" (fmap (intercalate ",") $ fmap (map fromMT) mt) $
          mbArg "machine_tag_mode" mt_mode $
          mbArg "min_taken_date" (dateMinTaken mbDates) $
          mbArg "max_taken_date" (dateMaxTaken mbDates) $
          mbArg "min_upload_date" (dateMinUpload mbDates) $
          mbArg "max_upload_date" (dateMaxUpload mbDates)
           [])
 where
   pnm     = either Just (const Nothing) eiPW
   woe_id  = either (const Nothing) Just eiPW

fromMT :: MachineTag -> [Char]
fromMT mt =
  case mTagValue mt of
   "" -> case (mTagNamespace mt, mTagPredicate mt) of
           ("","") -> ""
           (xs,"") -> xs ++ ":"
           ("",xs) -> "*:" ++ xs
           (as,bs) -> as ++ ':':bs
   t  -> toS (mTagNamespace mt) ++ ':':toS (mTagPredicate mt) ++ '=':t
 where
  toS "" = "*"
  toS x  = x

-- | Find Flickr Places information by Place Id.
resolvePlaceId :: PlaceID -> FM LocationPlace
resolvePlaceId pid =
  flickTranslate toLocationPlace $
      flickrCall "flickr.places.resolvePlaceId" [("place_id", pid)]

-- | Find Flickr Places information by Place URL.
resolvePlaceURL :: URLString -> FM LocationPlace
resolvePlaceURL purl =
  flickTranslate toLocationPlace $
    flickrCall "flickr.places.resolvePlaceURL" [("url", purl)]

-- | Return a list of the top 100 unique tags for a
-- Flickr Places or Where on Earth (WOE) ID.
tagsForPlace :: Either PlaceID WhereOnEarthID -> DateDetails -> FM [TagInfo]
tagsForPlace loc mbDates =
  flickTranslate toTagInfoList $
    flickrCall "flickr.places.tagsForPlace"
               (mbArg "woe_id"   mbw $
            mbArg "place_id" mbp $
        mbArg "min_taken_date" (dateMinTaken mbDates) $
        mbArg "max_taken_date" (dateMaxTaken mbDates) $
        mbArg "min_upload_date" (dateMinUpload mbDates) $
        mbArg "max_upload_date" (dateMaxUpload mbDates)
         [])
 where
   mbp  = either (const Nothing) Just loc
   mbw  = either Just (const Nothing) loc

