-- Copyright: 2007-2011 Dino Morelli
-- License: BSD3 (see LICENSE)
-- Author: Dino Morelli <dino@ui3.info>

module Photoname.Opts
   ( Options (..)
   , parseOpts, usageText
   )
   where

import Control.Applicative
import System.Console.GetOpt
import System.Directory


data Options = Options
   { optConfig :: String
   , optHelp :: Bool
   , optMove :: Bool
   , optNoAction :: Bool
   , optNoConfig :: Bool
   , optNoDirs :: Bool
   , optOldStyle :: Bool
   , optParentDir :: String
   , optQuiet :: Bool
   , optSuffix :: String
   }

defaultConfig :: String
defaultConfig = "~/.config/photoname.conf"

defaultOptions :: Options
defaultOptions = Options
   { optConfig = defaultConfig
   , optHelp = False
   , optMove = False
   , optNoAction = False
   , optNoConfig = False
   , optNoDirs = False
   , optOldStyle = False
   , optParentDir = "."
   , optQuiet = False
   , optSuffix = ""
   }


options :: [OptDescr (Options -> Options)]
options =
   [ Option ['c'] ["config"]
      (ReqArg (\c opts -> opts { optConfig = c }) "FILE")
      ("Defaults to " ++ defaultConfig ++ ". See CONFIG")
   , Option ['C'] ["no-config"]
      (NoArg (\opts -> opts { optNoConfig = True } )) 
      "Do not load config file"
   , Option ['D'] ["no-dirs"]
      (NoArg (\opts -> opts { optNoDirs = True } )) 
      "No subdirectory hierarchy. Just do DIR/NEWFILE"
   , Option ['h'] ["help"] 
      (NoArg (\opts -> opts { optHelp = True } ))
      "This help text"
   , Option []    ["move"] 
      (NoArg (\opts -> opts { optMove = True } ))
      "Move the files, don't just hard-link to the new locations"
   , Option ['n'] ["no-action"]
      (NoArg (\opts -> opts { optNoAction = True } )) 
      "Display what would be done, but do nothing"
   , Option ['o'] ["old-style"]
      (NoArg (\opts -> opts { optOldStyle = True } )) 
      "Use older name format with serial. See FILENAME FORMAT"
   , Option ['p'] ["parent-dir"]
      (ReqArg (\d opts -> opts { optParentDir = d } ) "DIR") 
      "Top-level directory where new links are created. Default: ."
   , Option ['q'] ["quiet"] 
      (NoArg (\opts -> opts { optQuiet = True } )) 
      "Suppress normal output of what's being done"
   , Option ['s'] ["suffix"]
      (ReqArg (\s opts -> opts { optSuffix = s } ) "SUF") 
      "Add optional suffix to each name. See SUFFIX"
   ]


{- Try to load a config file, converting its lines into a [String] 
   of long options to be parsed
-}
loadConfig :: FilePath -> IO [String]
loadConfig path = do
   confExists <- doesFileExist path

   if confExists
      then (map ("--" ++) . lines) <$> readFile path
      else return []


{- Perform the actual parse of a [String]
-}
parseOpts' :: [String] -> IO (Options, [String])
parseOpts' args =
   case getOpt Permute options args of
      (o,n,[]  ) -> return (foldl (flip id) defaultOptions o, n)
      (_,_,errs) -> ioError $ userError (concat errs ++ usageText)


{- The set of steps to parse args from both the command-line and a 
   config file
-}
parseOpts :: [String] -> IO (Options, [String])
parseOpts cliArgs = do
   -- Parse argv first time to get -c and/or -C
   (prelimOpts, _) <- parseOpts' cliArgs

   -- Load config file
   confArgs <- if (optNoConfig prelimOpts)
      then return []
      else loadConfig $ optConfig prelimOpts

   -- Parse second time with all args
   parseOpts' (confArgs ++ cliArgs)


usageText :: String
usageText = (usageInfo header options) ++ "\n" ++ footer
   where
      header = init $ unlines
         [ "Usage: photoname [OPTIONS] FILES"
         , "Rename and move photo files based on EXIF data"
         , ""
         , "Options:"
         ]
      footer = init $ unlines
         [ "This software is for renaming and storing your digital photos. It will attempt to construct a meaningful filename based on the EXIF shoot date in the file and optionally some other information."
         , ""
         , "CONFIG"
         , ""
         , "The program will attempt to load a config file from this location: " ++ defaultConfig ++ "  A different path may be specified with the --config switch"
         , "Entries in this file should be the long switches above minus the -- and including any arguments they may have. To completely ignore an existing config file, use the --no-config switch."
         , ""
         , "Example config contents:"
         , ""
         , "   move"
         , "   old-style"
         , "   parent-dir=~/mypics"
         , "   suffix=_dwm"
         , ""
         , ""
         , "FILENAME FORMAT"
         , ""
         , "Normal operation builds a subdirectory hierarchy consisting of directories for the years, then subdirs within those for the day photos were shot. These day dirs contain the image files, named as follows:"
         , ""
         , "A photo shot on 2002-May-02 01:23:07 PM:"
         , "   img_1790.jpg -> <PARENTDIR>/2002/2002-05-02/20020502-132307.jpg"
         , ""
         , "The EXIF date/time stamp used for naming is the first of these fields to be found: DateTimeDigitized, DateTimeOriginal, DateTime"
         , ""
         , "The <PARENTDIR> is the one given by the --parent-dir switch and represents the top-level of where you're storing photos."
         , ""
         , "The --no-dirs switch will suppress the directory-hierarchy-creating part of this, instead placing the new links directly in <PARENTDIR>. So you get files like:"
         , ""
         , "   <PARENTDIR>/20020502-132307.jpg"
         , ""
         , "Default behavior is to create hard links to the new paths and leave the original links as they were. You can use the --move switch to not leave the original links."
         , ""
         , "The --old-style switch specifies the prior behavior of photoname where the name is a date followed by the last three digits of the camera-assigned number from the original filename:"
         , ""
         , "   img_1790.jpg -> <PARENTDIR>/2002/2002-05-02/20020502_790.jpg"
         , ""
         , "The code is basically looking for three digits before the file extension to use as a 'serial' number for the day's photos. This is a seemingly common occurrance with cameras that we can pick numbers off the end of the filename. Examples of the two that I have:"
         , ""
         , "                    vvv (use these digits)"
         , "   Panasonic:  P###0###.jpg"
         , "   Canon:      img_####.jpg"
         , "                    ^^^"
         , "                    ^^^"
         , ""
         , "The --old-style behavior in photoname remains for backwards compatibility. Many cameras today (and particularly camera phones) don't generate a name we can use in this way, and so something needed to change in this software."
         , ""
         , "Another nagging problem is the old-style naming is non-deterministic. Using photoname --old-style on a file may not always give you the same name, say if the serial info in the name is ever lost. Naming based on the EXIF data alone is more reliable."
         , ""
         , ""
         , "SUFFIX"
         , ""
         , "The optional --suffix switch can be used to provide a string placed between the date/time and extension. Use it for anything you like. An example is photographer initials or series info. Example:"
         , ""
         , "photoname invoked with --suffix=_dwm :"
         , "   20020502-132307_dwm.jpg"
         , ""
         , "Version 3.0.0  Dino Morelli <dino@ui3.info>"
         ]

