{-# LANGUAGE NoMonomorphismRestriction#-}
{-# LANGUAGE NoImplicitPrelude #-}

module Bamboo.Helper where

import Bamboo.Helper.ByteString
import Bamboo.Helper.PreludeEnv hiding (at)
import Bamboo.Helper.Translation
import Bamboo.Type
import Bamboo.Type.Reader
import Bamboo.Type.StaticWidget hiding (name, body, reader)
import Control.Monad (liftM2, when)
import Data.Default
import Data.Maybe
import Hack.Contrib.Utils (script_name)
import Hack (Env)
import System.Directory
import System.FilePath.Posix hiding ((<.>))
import System.IO as IO
import Text.XHtml.Strict hiding (p, meta, body, select, name)
import qualified Bamboo.Type.Theme as Theme
import qualified Prelude as P
import qualified Text.XHtml.Strict as Html

gt :: (Ord a) => a -> a -> Bool
gt = (P.>)

ffmap :: (Functor f, Functor f1) => 
  (a -> b) -> f1 (f a) -> f1 (f b)
ffmap f = fmap (fmap f)
(^^) :: (Functor f, Functor f1) => f1 (f a) -> (a -> b) -> f1 (f b)
(^^) x f = fmap (fmap f) x

whenM :: (Monad m) => m Bool -> m () -> m ()
whenM b x = b >>= flip when x

parse_config :: String -> IO Assoc
parse_config x = do
  s <- read_file x
  s
    .filter_comment
    .lines.map strip
    .map (split "\\s*=\\s*")
    .map fill_snd_blank
    .map tuple2
    .return
  where
    fill_snd_blank [y] = [y,""]
    fill_snd_blank xs = xs


write_config :: FilePath -> Assoc -> IO ()
write_config s xs = 
  xs.map(\(x, y) -> x ++ " = " ++ y) .join "\n" .write_file s

-- html
empty_html :: Html
empty_html = toHtml ""

-- generic
show_data :: (Show a) => a -> [Char]
show_data = show > snake_case

ifM :: (Monad m) => m Bool -> m b -> m b -> m b
ifM p t f  = p >>= (\p' -> if p' then t else f)

parse_boolean :: String -> Bool
parse_boolean = belongs_to ["true", "1", "y", "yes", "yeah"]

mkdir :: String -> IO ()
mkdir = u2b > createDirectory

-- type SC = String -> String
type SIO = String -> IO ()

take_directory :: SC
take_directory = u2b > takeDirectory > b2u

with_file :: String -> IOMode -> (Handle -> IO a) -> IO a
with_file s m f = IO.withFile (s.u2b) m f

id_to_type :: SC
id_to_type x     = x.split "/" .first

id_to_resource :: SC
id_to_resource x = x.split "/" .tail.join "/"

read_data :: (Read a) => String -> a
read_data s   = s.camel_case.read

read_data_list :: (Read a) => [String] -> [a]
read_data_list xs = xs.map read_data

take_extension :: SC
take_extension = takeExtension --  > split "\\." > last

take_known_extension :: SC
take_known_extension s
  | ext.belongs_to exts = ext
  | otherwise = ""
  where 
    ext = s.take_extension
    exts = readers.only_snd.join'

drop_known_extension :: SC
drop_known_extension s
  | s.take_extension.belongs_to exts = dropExtension s
  | otherwise = s
  where exts = readers.only_snd.join'

remove_trailing_slash :: SC
remove_trailing_slash s = if s.last.is '/' then s.init else s

-- config
parse_list :: String -> [String]
parse_list s = s.split "," .map strip .reject null

static_config :: Config
static_config = purify $ do
  return def
  >>= set_blog_title               ( for_s BlogTitle                       )
  >>= set_blog_subtitle            ( for_s BlogSubtitle                    )
  >>= set_host_name                ( for_s HostName                        )
  >>= set_author_email             ( for_s AuthorEmail                     )
  >>= set_per_page                 ( for_i PerPage                         )
  >>= set_navigation               ( for_l Navigation ^^ (home_nav :)      )
  >>= set_bamboo_url               ( for_s BambooUrl                       )
  >>= set_sidebar                  ( for_l Sidebar >>= load_sidebar        )
  >>= set_footer                   ( for_s Footer >>= load_footer          )
  >>= set_favicon                  ( for_s Favicon                         )
  >>= set_analytics_account_id     ( for_s AnalyticsAccountId              )
  >>= set_extensions               ( for_l Extensions ^^ read_data_list    )
  >>= set_theme_config             ( for_s Theme >>= get_theme_config      )
  >>= set_post_date_format         ( for_s PostDateFormat                  )
  >>= set_comment_date_format      ( for_s CommentDateFormat               )
  >>= set_url_date_format          ( for_s UrlDateFormat                   )
  >>= set_url_date_matcher         ( for_s UrlDateMatcher                  )
  >>= set_url_title_subs           ( for_s UrlTitleSubs ^^ (as_l > read)   )
  >>= set_url_date_title_seperator ( for_s UrlDateTitleSeperator           )
  >>= set_cut                      ( for_s Cut                             )
  >>= set_summary_for_root         ( for_b SummaryForRoot                  )
  >>= set_summary_for_tag          ( for_b SummaryForTag                   )
  >>= set_summary_for_rss          ( for_b SummaryForRss                   )
  >>= set_picture_prefix           ( for_s PicturePrefix                   )
  >>= set_number_of_latest_posts   ( for_i NumberOfLatestPosts             )
  >>= set_use_cache                ( for_b UseCache                        )
  where
    user_config = parse_config $ def.config_uri
    
    for x = user_config ^ lookup (x.show_data)
    
    for_s = for
    for_i = for > (^^ read)
    for_l = for > (^^ parse_list)
    for_b = for > (^^ parse_boolean)

    load_widget x = do
      exists <- x.file_exist
      if exists
        then do
          w <- read_static_widget def x
          return $ (Just w)
        else
          return Nothing

    load_sidebar_item = (def.footer_uri / ) > load_widget
    
    load_sidebar Nothing = return Nothing
    load_sidebar (Just xs) = 
      xs
        . mapM load_sidebar_item
        ^ filter isJust
        ^ map fromJust
        ^ Just
    
    load_footer Nothing = return Nothing
    load_footer (Just s) = (def.footer_uri / s) .load_widget ^ Just
    
    as_l s = "[" ++ s ++ "]"

    get_theme_config Nothing = return Nothing
    get_theme_config (Just user_theme_name) = do
      let user_theme_uri  = (def.theme_uri / user_theme_name) ++ ".txt"
      
      exists <- user_theme_uri.file_exist
      if exists
        then
          parse_config user_theme_uri 
            ^ (("name", user_theme_name) : )
            ^ to_theme
            ^ Just
        else return Nothing

    -- helper
    c (Just _) _ y = y
    c Nothing x _ = x

    p = fromJust
    r = return
    
    set_analytics_account_id     v' x = v' >>= \v -> r $ c v x $ x { analytics_account_id     = p v}
    set_author_email             v' x = v' >>= \v -> r $ c v x $ x { author_email             = p v}
    set_bamboo_url               v' x = v' >>= \v -> r $ c v x $ x { bamboo_url               = p v}
    set_blog_subtitle            v' x = v' >>= \v -> r $ c v x $ x { blog_subtitle            = p v}
    set_blog_title               v' x = v' >>= \v -> r $ c v x $ x { blog_title               = p v}
    set_comment_date_format      v' x = v' >>= \v -> r $ c v x $ x { comment_date_format      = p v}
    set_cut                      v' x = v' >>= \v -> r $ c v x $ x { cut                      = p v}
    set_extensions               v' x = v' >>= \v -> r $ c v x $ x { extensions               = p v}
    set_favicon                  v' x = v' >>= \v -> r $ c v x $ x { favicon                  = p v}
    set_footer                   v' x = v' >>= \v -> r $ c v x $ x { footer                   = p v}
    set_host_name                v' x = v' >>= \v -> r $ c v x $ x { host_name                = p v}
    set_navigation               v' x = v' >>= \v -> r $ c v x $ x { navigation               = p v}
    set_number_of_latest_posts   v' x = v' >>= \v -> r $ c v x $ x { number_of_latest_posts   = p v}
    set_per_page                 v' x = v' >>= \v -> r $ c v x $ x { per_page                 = p v}
    set_picture_prefix           v' x = v' >>= \v -> r $ c v x $ x { picture_prefix           = p v}
    set_post_date_format         v' x = v' >>= \v -> r $ c v x $ x { post_date_format         = p v}
    set_sidebar                  v' x = v' >>= \v -> r $ c v x $ x { sidebar                  = p v}
    set_summary_for_root         v' x = v' >>= \v -> r $ c v x $ x { summary_for_root         = p v}
    set_summary_for_rss          v' x = v' >>= \v -> r $ c v x $ x { summary_for_rss          = p v}
    set_summary_for_tag          v' x = v' >>= \v -> r $ c v x $ x { summary_for_tag          = p v}
    set_theme_config             v' x = v' >>= \v -> r $ c v x $ x { theme_config             = p v}
    set_url_date_format          v' x = v' >>= \v -> r $ c v x $ x { url_date_format          = p v}
    set_url_date_matcher         v' x = v' >>= \v -> r $ c v x $ x { url_date_matcher         = p v}
    set_url_date_title_seperator v' x = v' >>= \v -> r $ c v x $ x { url_date_title_seperator = p v}
    set_url_title_subs           v' x = v' >>= \v -> r $ c v x $ x { url_title_subs           = p v}
    set_use_cache                v' x = v' >>= \v -> r $ c v x $ x { use_cache                = p v}

db_uri       :: Config -> String
flat_uri     :: Config -> String
public_uri   :: Config -> String
image_uri    :: Config -> String
config_uri   :: Config -> String
sidebar_uri  :: Config -> String
footer_uri   :: Config -> String
post_uri     :: Config -> String
tag_uri      :: Config -> String
comment_uri  :: Config -> String
theme_uri    :: Config -> String
album_uri    :: Config -> String
topic_uri    :: Config -> String
stat_uri     :: Config -> String
cache_uri    :: Config -> String

db_uri      x   =   x.db_id
flat_uri    x   =   x.db_uri        / x.flat_id
public_uri  x   =   x.db_uri        / x.public_id
image_uri   x   =   x.public_uri    / x.image_id
config_uri  x   =   x.flat_uri      / x.config_id     / x.config_file_id
sidebar_uri x   =   x.flat_uri      / x.config_id     / x.sidebar_id
footer_uri  x   =   x.flat_uri      / x.config_id
post_uri    x   =   x.flat_uri      / x.post_id
tag_uri     x   =   x.flat_uri      / x.tag_id
comment_uri x   =   x.flat_uri      / x.comment_id
theme_uri   x   =   x.flat_uri      / x.config_id     / x.theme_id
album_uri   x   =   x.image_uri     / x.album_id
topic_uri   x   =   x.flat_uri      / x.topic_id
stat_uri    x   =   x.flat_uri      / x.stat_id
cache_uri   x   =   x.flat_uri      / x.cache_id


-- Widget
read_static_widget :: Reader -> String -> IO StaticWidget
read_static_widget user_reader s = liftM2 (StaticWidget name) body (return reader) where
  body   = s.read_bytestring
  reader = s.take_extension.guess_reader.fromMaybe user_reader
  name   = s.takeFileName.drop_known_extension

-- Theme
to_theme :: Assoc -> Theme.ThemeConfig
to_theme xs = Theme.ThemeConfig
  { Theme.name        = at Theme.Name
  , Theme.css         = at Theme.Css .css_list
  , Theme.js          = at Theme.Js  .js_list
  }
  where
    at s = xs.lookup (s.show_data) .fromJust
    css_list s = s.parse_list.map 
      (\x -> "theme/" ++ at Theme.Name ++ "/css/" ++ x ++ ".css")
    js_list s  = s.parse_list.map 
      (\x -> "theme/" ++ at Theme.Name ++ "/js/" ++ x ++ ".js")

slashed_script_name :: Env -> String
slashed_script_name env = "/" / env.script_name