{-# LANGUAGE ForeignFunctionInterface #-}

-- Copyright 2010 Evgeniy Vodolazskiy (waterlaz@gmail.com)
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-- Lesser General Public License for more details.


module Network.XMMS.Playlist( 
    retrieveChange,
    Change (..),
    currentPos,
    currentActive,
    list,
    create,
    shuffle,
    sort,
    clear,
    remove,
    listEntries,
    insertID,
    insertURL,
    rInsert,
    rInsertEncoded,
    insertEncoded,
    addID,
    addURL,
    rAdd,
    rAddEncoded,
    addEncoded,
    moveEntry,
    removeEntry,
    broadcastChanged,
    broadcastCurrentPos,
    setNext,
    setNextRel,
    load
)where

import Foreign
import Foreign.Ptr
import Foreign.C.Types
import Network.XMMS.UTF8Strings
import Foreign.ForeignPtr
import Foreign.Marshal.Alloc

import Network.XMMS.Utilities
import Network.XMMS.Constants
import Network.XMMS.Types
import Network.XMMS.Result
import Network.XMMS.Value

import Data.Map (Map)
import qualified Data.Map as Map


data Change = Add { position :: Int, name :: String, id :: Int }
            | Insert { position :: Int, name :: String, id :: Int }
            | Remove { position :: Int, name :: String}
            | Clear { name :: String }
            | Move { position :: Int, newPosition :: Int, name :: String, id :: Int }
            | Sort { name :: String }
            | Shuffle { name :: String }
            | Update { name :: String }
            | NoChange

retrieveChange :: XMMSCV -> Change
retrieveChange (XMMSDict dict) 
    |changeType == playlistChangedAdd       = Add position name id
    |changeType == playlistChangedInsert    = Insert position name id
    |changeType == playlistChangedRemove    = Remove position name
    |changeType == playlistChangedClear     = Clear name 
    |changeType == playlistChangedMove      = Move position newPosition name id
    |changeType == playlistChangedSort      = Sort name
    |changeType == playlistChangedShuffle   = Shuffle name 
    |changeType == playlistChangedUpdate    = Update name 
    |otherwise                              = NoChange
  where convFromJust (Just x) = x
        convFromJust _        = error $ "Failed on getting playlist change from dict: " ++ (show dict)
        
        name            = xmmsString $ convFromJust $ Map.lookup "name" dict
        position        = xmmsInt $ convFromJust $ Map.lookup "position" dict
        newPosition     = xmmsInt $ convFromJust $ Map.lookup "newposition" dict
        id              = xmmsInt $ convFromJust $ Map.lookup "id" dict
        changeType      = fromIntegral $ xmmsInt $ convFromJust $ Map.lookup "type" dict
        

--xmmsc_result_t * xmmsc_playlist_current_pos  (xmmsc_connection_t  *c, const char *playlist)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_current_pos" 
    xmmsc_playlist_current_pos :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |Retrive the current position in the playlist.
currentPos :: Connection -> String -> IO Result
currentPos connection name = 
    withString name (\ c_name -> 
        wrapCallResult (\c -> xmmsc_playlist_current_pos c c_name) connection)
    
    
--xmmsc_result_t * 	xmmsc_playlist_current_active (xmmsc_connection_t *c)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_current_active" 
    xmmsc_playlist_current_active :: Ptr C_xmmsc_connection -> IO (Ptr C_xmmsc_result)
-- |Retrive the name of the active playlist.
currentActive :: Connection -> IO Result
currentActive = wrapCallResult xmmsc_playlist_current_active


--xmmsc_result_t * 	xmmsc_playlist_list (xmmsc_connection_t *c)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_list" 
    xmmsc_playlist_list :: Ptr C_xmmsc_connection -> IO (Ptr C_xmmsc_result)
-- |List the existing playlists.
list :: Connection -> IO Result
list = wrapCallResult xmmsc_playlist_list


--xmmsc_result_t * 	xmmsc_playlist_create (xmmsc_connection_t *c, const char *playlist)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_create" 
    xmmsc_playlist_create :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |Create a new empty playlist.
create :: Connection -> String -> IO Result
create connection name = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_create c c_name) connection)


--xmmsc_result_t * 	xmmsc_playlist_shuffle (xmmsc_connection_t *c, const char *playlist)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_shuffle" 
    xmmsc_playlist_shuffle :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |Shuffles the current playlist.
shuffle :: Connection -> String -> IO Result
shuffle connection name = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_shuffle c c_name) connection)
    

--xmmsc_result_t * 	xmmsc_playlist_sort (xmmsc_connection_t *c, const char *playlist, xmmsv_t *properties)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_sort" 
    xmmsc_playlist_sort :: Ptr C_xmmsc_connection -> CString -> Ptr C_xmmsc_value -> IO (Ptr C_xmmsc_result)
-- |Sorts the playlist according to the list of properties 
sort :: Connection -> String -> [String] -> IO Result
sort connection name properties = 
    withString name (\ c_name -> do
        let xmmsProperties = XMMSList $ map XMMSString properties
        c_properties <- convertValueHstoC xmmsProperties
        withForeignPtr c_properties (\c_properties_ptr -> 
            wrapCallResult (\c -> xmmsc_playlist_sort c c_name c_properties_ptr) connection))


--xmmsc_result_t *xmmsc_playlist_clear (xmmsc_connection_t *c, const char *playlist)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_clear" 
    xmmsc_playlist_clear :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |Clears the current playlist.
clear :: Connection -> String -> IO Result
clear connection name = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_clear c c_name) connection)

--xmmsc_result_t * 	xmmsc_playlist_remove (xmmsc_connection_t *c, const char *playlist)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_remove" 
    xmmsc_playlist_remove :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |Remove the given playlist.
remove :: Connection -> String -> IO Result
remove connection name = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_remove c c_name) connection)
 	
        
--xmmsc_result_t * 	xmmsc_playlist_list_entries (xmmsc_connection_t *c, const char *playlist)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_list_entries" 
    xmmsc_playlist_list_entries :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |List current playlist.
listEntries :: Connection -> String -> IO Result
listEntries connection name = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_list_entries c c_name) connection)
    
    
--xmmsc_result_t *xmmsc_playlist_insert_id (xmmsc_connection_t *c, const char *playlist, int pos, int id)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_insert_id" 
    xmmsc_playlist_insert_id :: Ptr C_xmmsc_connection -> CString -> CInt -> CInt -> IO (Ptr C_xmmsc_result)
-- |Insert a medialib id at given position in playlist.
insertID :: Connection -> String -> Int -> Int -> IO Result
insertID connection name pos id = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_insert_id c c_name (fromIntegral pos) (fromIntegral pos)) connection)


--xmmsc_result_t * 	xmmsc_playlist_insert_url (xmmsc_connection_t *c, const char *playlist, int pos, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_insert_url" 
    xmmsc_playlist_insert_url :: Ptr C_xmmsc_connection -> CString -> Int -> CString -> IO (Ptr C_xmmsc_result)
-- |Insert entry at given position in playlist.
insertURL :: Connection -> String -> Int -> String -> IO Result
insertURL connection name pos url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_insert_url c c_name (fromIntegral pos) c_url) connection
        free c_url
        return res)
    
    
--xmmsc_result_t * 	xmmsc_playlist_rinsert (xmmsc_connection_t *c, const char *playlist, int pos, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_rinsert" 
    xmmsc_playlist_rinsert :: Ptr C_xmmsc_connection -> CString -> Int -> CString -> IO (Ptr C_xmmsc_result)
-- |Insert a directory recursivly at a given position in the playlist.
rInsert :: Connection -> String -> Int -> String -> IO Result
rInsert connection name pos url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_rinsert c c_name (fromIntegral pos) c_url) connection
        free c_url
        return res)

    
--xmmsc_result_t * 	xmmsc_playlist_rinsert_encoded (xmmsc_connection_t *c, const char *playlist, int pos, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_rinsert_encoded" 
    xmmsc_playlist_rinsert_encoded :: Ptr C_xmmsc_connection -> CString -> Int -> CString -> IO (Ptr C_xmmsc_result)
-- |Insert a directory recursivly at a given position in the playlist.
rInsertEncoded :: Connection -> String -> Int -> String -> IO Result
rInsertEncoded connection name pos url = do
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_rinsert_encoded c c_name (fromIntegral pos) c_url) connection
        free c_url
        return res)


{-xmmsc_result_t * 	xmmsc_playlist_insert_args (xmmsc_connection_t *c, const char *playlist, int pos, const char *url, int numargs, const char **args)
 	Insert entry at given position in playlist with args.
xmmsc_result_t * 	xmmsc_playlist_insert_full (xmmsc_connection_t *c, const char *playlist, int pos, const char *url, xmmsv_t *args)
 	Insert entry at given position in playlist with args. 
xmmsc_result_t * 	xmmsc_playlist_insert_collection (xmmsc_connection_t *c, const char *playlist, int pos, xmmsv_coll_t *coll, xmmsv_t *order)
 	Queries the medialib for media and inserts the matching ones to the current playlist at the given position.    
xmmsc_result_t * 	xmmsc_playlist_add_args (xmmsc_connection_t *c, const char *playlist, const char *url, int nargs, const char **args)
 	Add the url to the playlist with arguments.
xmmsc_result_t * 	xmmsc_playlist_add_full (xmmsc_connection_t *c, const char *playlist, const char *url, xmmsv_t *args)
 	Add the url to the playlist with arguments.    
xmmsc_result_t * 	xmmsc_playlist_add_idlist (xmmsc_connection_t *c, const char *playlist, xmmsv_coll_t *coll)
 	Adds media in idlist to a playlist.
xmmsc_result_t * 	xmmsc_playlist_add_collection (xmmsc_connection_t *c, const char *playlist, xmmsv_coll_t *coll, xmmsv_t *order)
 	Queries the medialib for media and adds the matching ones to the current playlist.

-}
    
--xmmsc_result_t * 	xmmsc_playlist_insert_encoded (xmmsc_connection_t *c, const char *playlist, int pos, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_insert_encoded" 
    xmmsc_playlist_insert_encoded :: Ptr C_xmmsc_connection -> CString -> Int -> CString -> IO (Ptr C_xmmsc_result)
-- |Insert entry at given position in playlist.
insertEncoded :: Connection -> String -> Int -> String -> IO Result
insertEncoded connection name pos url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_insert_encoded c c_name (fromIntegral pos) c_url) connection
        free c_url
        return res)
    

--xmmsc_result_t * 	xmmsc_playlist_add_id (xmmsc_connection_t *c, const char *playlist, int id)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_add_id" 
    xmmsc_playlist_add_id :: Ptr C_xmmsc_connection -> CString -> Int -> IO (Ptr C_xmmsc_result)
-- |Add a medialib id to the playlist.
addID :: Connection -> String -> Int -> IO Result
addID connection name id = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_add_id c c_name (fromIntegral id)) connection)
    
    
    
    
--xmmsc_result_t * 	xmmsc_playlist_add_url (xmmsc_connection_t *c, const char *playlist, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_add_url" 
    xmmsc_playlist_add_url :: Ptr C_xmmsc_connection -> CString -> CString -> IO (Ptr C_xmmsc_result)
-- |Add the url to the playlist.
addURL :: Connection -> String -> String -> IO Result
addURL connection name url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_add_url c c_name c_url) connection
        free c_url
        return res)
    

--xmmsc_result_t * 	xmmsc_playlist_radd (xmmsc_connection_t *c, const char *playlist, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_radd" 
    xmmsc_playlist_radd :: Ptr C_xmmsc_connection -> CString -> CString -> IO (Ptr C_xmmsc_result)
-- |Adds a directory recursivly to the playlist.
rAdd :: Connection -> String -> String -> IO Result
rAdd connection name url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_radd c c_name c_url) connection
        free c_url
        return res)
    
--xmmsc_result_t *xmmsc_playlist_radd_encoded (xmmsc_connection_t *c, const char *playlist, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_radd_encoded" 
    xmmsc_playlist_radd_encoded :: Ptr C_xmmsc_connection -> CString -> CString -> IO (Ptr C_xmmsc_result)
-- |Adds a directory recursivly to the playlist.
rAddEncoded :: Connection -> String -> String -> IO Result
rAddEncoded connection name url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_radd_encoded c c_name c_url) connection
        free c_url
        return res)



--xmmsc_result_t* xmmsc_playlist_add_encoded (xmmsc_connection_t *c, const char *playlist, const char *url)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_add_encoded" 
    xmmsc_playlist_add_encoded :: Ptr C_xmmsc_connection -> CString -> CString -> IO (Ptr C_xmmsc_result)
-- |Add the url to the playlist.
addEncoded :: Connection -> String -> String -> IO Result
addEncoded connection name url = 
    withString name (\ c_name -> do
        c_url <- newCString url
        res <- wrapCallResult (\c -> xmmsc_playlist_add_encoded c c_name c_url) connection
        free c_url
        return res)


--xmmsc_result_t *xmmsc_playlist_move_entry (xmmsc_connection_t *c, const char *playlist, int cur_pos, int new_pos)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_move_entry" 
    xmmsc_playlist_move_entry :: Ptr C_xmmsc_connection -> CString -> CInt -> CInt -> IO (Ptr C_xmmsc_result)
-- |Move a playlist entry to a new position (absolute move).
moveEntry :: Connection -> String -> Int -> Int -> IO Result
moveEntry connection name curPos newPos = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_move_entry c c_name (fromIntegral curPos) (fromIntegral newPos)) connection)


--xmmsc_result_t * 	xmmsc_playlist_remove_entry (xmmsc_connection_t *c, const char *playlist, int pos)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_remove_entry" 
    xmmsc_playlist_remove_entry :: Ptr C_xmmsc_connection -> CString -> CInt -> IO (Ptr C_xmmsc_result)
-- |Remove an entry from the playlist.
removeEntry :: Connection -> String -> Int -> IO Result
removeEntry connection name pos = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_remove_entry c c_name (fromIntegral pos)) connection)
        
    
--xmmsc_result_t *xmmsc_broadcast_playlist_changed (xmmsc_connection_t *c)
foreign import ccall safe "xmmsclient/xmmsclient.h xmmsc_broadcast_playlist_changed" 
    xmmsc_broadcast_playlist_changed :: Ptr C_xmmsc_connection -> IO (Ptr C_xmmsc_result)
-- |Request the playlist changed broadcast from the server.
broadcastChanged :: Connection -> IO Result
broadcastChanged = wrapCallResult xmmsc_broadcast_playlist_changed
    

--xmmsc_result_t * 	xmmsc_broadcast_playlist_current_pos (xmmsc_connection_t *c)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_broadcast_playlist_current_pos" 
    xmmsc_broadcast_playlist_current_pos :: Ptr C_xmmsc_connection -> IO (Ptr C_xmmsc_result)
-- |Request the playlist current pos broadcast.
broadcastCurrentPos :: Connection -> IO Result
broadcastCurrentPos = wrapCallResult xmmsc_broadcast_playlist_current_pos


--xmmsc_result_t *xmmsc_playlist_set_next (xmmsc_connection_t *c, int pos)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_set_next" 
    xmmsc_playlist_set_next :: Ptr C_xmmsc_connection -> CInt -> IO (Ptr C_xmmsc_result)
-- |Set next entry in the playlist.
setNext :: Connection -> Int -> IO Result
setNext connection pos = do
    wrapCallResult (\c -> xmmsc_playlist_set_next c (fromIntegral pos)) connection


--xmmsc_result_t * 	xmmsc_playlist_set_next_rel (xmmsc_connection_t *c, int pos)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_set_next_rel" 
    xmmsc_playlist_set_next_rel :: Ptr C_xmmsc_connection -> CInt -> IO (Ptr C_xmmsc_result)
-- |Same as setNextRel but relative to the current postion.
setNextRel :: Connection -> Int -> IO Result
setNextRel connection pos = do
    wrapCallResult (\c -> xmmsc_playlist_set_next_rel c (fromIntegral pos)) connection


--xmmsc_result_t *xmmsc_playlist_load (xmmsc_connection_t *c, const char *name)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_playlist_load" 
    xmmsc_playlist_load :: Ptr C_xmmsc_connection -> CString -> IO (Ptr C_xmmsc_result)
-- |Load a playlist as the current active playlist.
load :: Connection -> String -> IO Result
load connection name = 
    withString name (\ c_name ->
        wrapCallResult (\c -> xmmsc_playlist_load c c_name) connection)
    

--xmmsc_result_t *xmmsc_broadcast_playlist_loaded (xmmsc_connection_t *c)
foreign import ccall unsafe "xmmsclient/xmmsclient.h xmmsc_broadcast_playlist_loaded" 
    xmmsc_broadcast_playlist_loaded :: Ptr C_xmmsc_connection -> IO (Ptr C_xmmsc_result)
-- |Request the playlist_loaded broadcast. 
broadcastLoaded :: Connection -> IO Result
broadcastLoaded = wrapCallResult xmmsc_broadcast_playlist_loaded