{-# LANGUAGE CPP #-}
{- |

   Module      :  System.Win32.HardLink

   Copyright   :  2013 shelarcy

   License     :  BSD-style



   Maintainer  :  shelarcy@gmail.com

   Stability   :  Provisional

   Portability :  Non-portable (Win32 API)



   Handling hard link using Win32 API. [NTFS only]



   Note: You should worry about file system type when use this module's function in your application:



     * NTFS only supprts this functionality.



     * ReFS doesn't support hard link currently.

-}
module System.Win32.HardLink
  ( module System.Win32.HardLink
  ) where
import System.Win32.File   ( LPSECURITY_ATTRIBUTES, failIfFalseWithRetry_ )
import System.Win32.String ( LPCTSTR, withTString )
import System.Win32.Types  ( BOOL, nullPtr )

#include "windows_cconv.h"


-- | NOTE: createHardLink is /flipped arguments/ to provide compatiblity for Unix.

-- 

-- If you want to create hard link by Windows way, use 'createHardLink'' instead.

createHardLink :: FilePath -- ^ Target file path

               -> FilePath -- ^ Hard link name

               -> IO ()
createHardLink = flip createHardLink'

createHardLink' :: FilePath -- ^ Hard link name

                -> FilePath -- ^ Target file path

                -> IO ()
createHardLink' link target =
   withTString target $ \c_target ->
   withTString link   $ \c_link ->
        failIfFalseWithRetry_ (unwords ["CreateHardLinkW",show link,show target]) $
          c_CreateHardLink c_link c_target nullPtr

foreign import WINDOWS_CCONV unsafe "windows.h CreateHardLinkW"
  c_CreateHardLink :: LPCTSTR -- ^ Hard link name

                   -> LPCTSTR -- ^ Target file path

                   -> LPSECURITY_ATTRIBUTES -- ^ This parameter is reserved. You should pass just /nullPtr/.

                   -> IO BOOL

{-

-- We plan to check file system type internally.



-- We are thinking about API design, currently...

data VolumeInformation = VolumeInformation

      { volumeName         :: String

      , volumeSerialNumber :: DWORD

      , maximumComponentLength :: DWORD

      , fileSystemFlags    :: DWORD

      , fileSystemName     :: String

      } deriving Show



getVolumeInformation :: Maybe String -> IO VolumeInformation

getVolumeInformation drive =

   maybeWith withTString drive $ \c_drive ->

   withTStringBufferLen 256    $ \(vnBuf, vnLen) ->

   alloca $ \serialNum ->

   alloca $ \maxLen ->

   alloca $ \fsFlags ->

   withTStringBufferLen 256 $ \(fsBuf, fsLen) -> do

       failIfFalse_ (unwords ["GetVolumeInformationW", drive]) $

         c_GetVolumeInformation c_drive vnBuf (fromIntegral vnLen)

                                serialNum maxLen fsFlags

                                fsBuf (fromIntegral fsLen)

       return VolumeInformation

         <*> peekTString vnBuf

         <*> peek serialNum

         <*> peek maxLen

         <*> peek fsFlags

         <*> peekTString fsBuf



-- Which is better?

getVolumeFileType :: String -> IO String

getVolumeFileType drive = fileSystemName <$> getVolumeInformation drive



getVolumeFileType :: String -> IO String

getVolumeFileType drive =

   withTString drive        $ \c_drive ->

   withTStringBufferLen 256 $ \(buf, len) -> do

       failIfFalse_ (unwords ["GetVolumeInformationW", drive]) $

         c_GetVolumeInformation c_drive nullPtr 0 nullPtr nullPtr nullPtr buf (fromIntegral len)

       peekTString buf



foreign import WINDOWS_CCONV unsafe "windows.h GetVolumeInformationW"

  c_GetVolumeInformation :: LPCTSTR -> LPTSTR -> DWORD -> LPDWORD -> LPDWORD -> LPDWORD -> LPTSTR -> DWORD -> IO BOOL

-}