module Hbro.Config (
-- * Default configuration
    defaultConfig,
    defaultHooks,
    defaultKeyHandler,
    defaultKeyBindings,
    defaultLinkClickedHook,
    defaultMIMEDisposition,
--    defaultNewWindowHook,
    defaultTitleChangedHook,
    defaultCommandsList
) where

-- {{{ Import
import Hbro.Core
--import Hbro.Keys
import Hbro.Gui
import qualified Hbro.Prompt as Prompt
import Hbro.Types
import Hbro.Util

import Control.Monad.Reader hiding(mapM_)

import Data.Foldable
import Data.Functor
import qualified Data.Map as M

import Graphics.UI.Gtk.Abstract.Widget
import Graphics.UI.Gtk.General.General
import Graphics.UI.Gtk.Entry.Entry
import Graphics.UI.Gtk.WebKit.WebPolicyDecision
import Graphics.UI.Gtk.WebKit.WebView
import Graphics.UI.Gtk.Windows.Window

import Prelude hiding(mapM_)

import Network.URI

import System.Console.CmdArgs (whenLoud)
import System.FilePath
import System.Glib.Attributes
-- }}}


-- | Default configuration.
-- Homepage: DuckDuckGo, socket directory: system's temporary directory,
-- UI file: ~/.config/hbro/, Webkit's default websettings, no key/command binding.
defaultConfig :: Config
defaultConfig = Config {
    mHomePage          = "https://duckduckgo.com/",
    mSocketDir         = mTemporary,
    mUIFile            = (</> "ui.xml") . mConfiguration,
    mWebSettings       = [],
    mCommands          = defaultCommandsList,
    mHooks             = defaultHooks
}

-- | Pack of default hooks
defaultHooks :: Hooks
defaultHooks = Hooks {
    mBackForward     = (\_ decision -> io $ webPolicyDecisionUse decision),
    mDownload        = defaultDownloadHook,
    mFormResubmitted = (\_ decision -> io $ webPolicyDecisionUse decision),
    mFormSubmitted   = (\_ decision -> io $ webPolicyDecisionUse decision),
    mKeyPressed      = void . (defaultKeyHandler defaultKeyBindings),
    mLinkClicked     = defaultLinkClickedHook,
    mLoadFinished    = return (),
    mMIMEDisposition = defaultMIMEDisposition,
    mNewWindow       = const $ return (), --defaultNewWindowHook,
    mOtherNavigation = (\_ decision -> io $ webPolicyDecisionUse decision),
    mReload          = (\_ decision -> io $ webPolicyDecisionUse decision),
    mStartUp         = return (),
    mTitleChanged    = defaultTitleChangedHook
}

-- | Warn user about missing download hook.
defaultDownloadHook :: URI -> String -> Int -> K ()
defaultDownloadHook _ _ _ = notify 5000 "No download hook defined."

-- | Look for a callback associated to the given keystrokes and trigger it, if any.
defaultKeyHandler :: KeysList -> String -> K (String, Bool)
defaultKeyHandler keysList keystrokes = do
    io . whenLoud . putStr $ "Key pressed: " ++ keystrokes
    (log', isMapped) <- case M.lookup keystrokes (M.fromList keysList) of
        Just callback -> callback >> return (" (mapped)",   True) 
        _             ->             return (" (unmapped)", False)
    io . whenLoud . putStrLn $ log'
    return (keystrokes, isMapped)

-- | Default key bindings.
defaultKeyBindings :: KeysList
defaultKeyBindings = [
-- Browse
    ("M-<Left>",      goBack),
    ("M-<Right>",     goForward),
    ("C-<Escape>",    stopLoading),
    ("<F5>",          reload),
    ("C-r",           reload),
    ("C-<F5>",        reloadBypassCache),
    ("M-r",           reloadBypassCache),
    ("C-^",           scroll Horizontal (Absolute 0)),
    ("C-$",           scroll Horizontal (Absolute 100)),
    ("C-<Home>",      scroll Vertical   (Absolute 0)),
    ("C-<End>",       scroll Vertical   (Absolute 100)),
    ("M-<Home>",      goHome),
-- Display
    ("C-+",           zoomIn),
    ("C--",           zoomOut),
    -- ("<F11>",         with (mWindow . mGUI) windowFullscreen),
    -- ("<Escape>",      with (mWindow . mGUI) windowUnfullscreen),
    ("C-b",           with (mStatusBar . mGUI) toggleVisibility),
    ("C-u",           toggleSourceMode),
-- Prompt
    ("C-o",           Prompt.readURI "Open URI" [] loadURI),
    ("M-o",           withURI $ \uri -> Prompt.readURI "Open URI " (show uri) loadURI),
-- Search
    ("/",             Prompt.iread "Search " [] $ void . searchText CaseInsensitive Forward Wrap),
    ("C-f",           Prompt.iread "Search " [] $ void . searchText CaseInsensitive Forward Wrap),
    ("?",             Prompt.iread "Search " [] $ void . searchText CaseInsensitive Backward Wrap),
    ("C-n",           withK (mEntry . mPromptBar . mGUI) $ (io . entryGetText) >=> void . searchText CaseInsensitive Forward Wrap),
    ("C-N",           withK (mEntry . mPromptBar . mGUI) $ (io . entryGetText) >=> void . searchText CaseInsensitive Backward Wrap),
-- Misc
    ("<Escape>",      with (mBox . mPromptBar . mGUI) widgetHide),
    ("C-i",           showWebInspector),
    ("C-p",           printPage),
    ("C-t",           io $ spawn "hbro" []),
    ("C-w",           io mainQuit)
    ]

-- | Left click loads the new page in current window, middle click loads the new page in a new window, right click does nothing.
defaultLinkClickedHook :: Button -> URI -> WebPolicyDecision -> K ()
defaultLinkClickedHook ButtonL _uri decision = io $ webPolicyDecisionUse    decision
defaultLinkClickedHook ButtonM  uri decision = io $ webPolicyDecisionIgnore decision >> spawn "hbro" ["-u", show uri]
defaultLinkClickedHook _       _uri decision = io $ webPolicyDecisionIgnore decision

-- /!\ NetworkRequest's Haskell binding is missing the function "webkit_network_request_get_message", which makes it rather useless...
-- | Display content if webview can show the given MIME type, otherwise download it.
defaultMIMEDisposition :: URI -> String -> WebPolicyDecision -> K ()
defaultMIMEDisposition _uri mimetype decision = with (mWebView . mGUI) $ \view -> do
    canShow <- webViewCanShowMimeType view mimetype

    case canShow of
        True -> webPolicyDecisionUse      decision
        _    -> webPolicyDecisionDownload decision

--- | Default behavior when a new window is requested: load URI in current window.
--defaultNewWindowHook :: URI -> K ()
--defaultNewWindowHook uri = loadURI uri

-- | Update the main window's title
defaultTitleChangedHook :: String -> K ()            
defaultTitleChangedHook title = with (mWindow . mGUI) (`set` [ windowTitle := ("hbro | " ++ title)])

-- | List of default supported requests.
defaultCommandsList :: CommandsList
defaultCommandsList = [
    -- Get information
    ("GET_URI",           \_arguments -> (maybe "ERROR" show) <$> mapK postGUISync getURI),
    ("GET_TITLE",         \_arguments -> (maybe "ERROR" show) <$> mapK postGUISync getTitle),
    ("GET_FAVICON_URI",   \_arguments -> (maybe "ERROR" show) <$> mapK postGUISync getFaviconURI),
    ("GET_LOAD_PROGRESS", \_arguments -> show <$> mapK postGUISync getLoadProgress),
    -- Trigger actions
    ("LOAD_URI",          \arguments -> case arguments of 
        uri:_ -> ((mapK postGUIAsync) . (mapM_ loadURI)) (parseURIReference uri) >> return "OK"
        _     -> return "ERROR Argument needed."),
    ("STOP_LOADING",      \_arguments -> mapK postGUIAsync stopLoading >> return "OK"),
    ("RELOAD",            \_arguments -> mapK postGUIAsync reload >> return "OK"),
    ("GO_BACK",           \_arguments -> mapK postGUIAsync goBack >> return "OK"),
    ("GO_FORWARD",        \_arguments -> mapK postGUIAsync goForward >> return "OK"),
    ("ZOOM_IN",           \_arguments -> mapK postGUIAsync zoomIn >> return "OK"),
    ("ZOOM_OUT",          \_arguments -> mapK postGUIAsync zoomOut >> return "OK")]