-- Copyright (C) 2002-2004,2007-2008 David Roundy
-- Copyright (C) 2005 Juliusz Chroboczek
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2, or (at your option)
-- any later version.
--
-- This program 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 General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; see the file COPYING.  If not, write to
-- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-- Boston, MA 02110-1301, USA.

{-# LANGUAGE CPP #-}

#include "gadts.h"

module Darcs.Repository.LowLevel
    ( readPending, readTentativePending
    , writeTentativePending
    -- deprecated interface:
    , readNewPending, writeNewPending
    , pendingName )
    where

import Darcs.Repository.InternalTypes ( RepoType(..), Repository(..) )
import Darcs.Patch ( readPatch, writePatch, RepoPatch, PrimOf )
import Darcs.Patch.Read ( ReadPatch(..), bracketedFL )
import Darcs.Patch.ReadMonads ( ParserM )
import Darcs.Patch.Show ( ShowPatchBasic(..) )
import Darcs.Global ( darcsdir )
import Darcs.Witnesses.Sealed ( Sealed(Sealed), mapSeal )
import Darcs.Witnesses.Ordered ( FL(..), mapFL )
import Darcs.Utils ( catchall )
import ByteStringUtils ( gzReadFilePS )
import Printer ( Doc, ($$), text, vcat )

import Control.Applicative
import qualified Data.ByteString as BS ( ByteString, empty )

pendingName :: RepoType p -> String
pendingName (DarcsRepository _ _) = darcsdir++"/patches/pending"

-- | Read the contents of pending. CWD should be the repository directory.
-- The return type is currently incorrect as it refers to the tentative
-- state rather than the recorded state.
readPending :: RepoPatch p => Repository p C(r u t) -> IO (Sealed (FL (PrimOf p) C(t)))
readPending (Repo _ _ _ tp) =
    readPendingFile (pendingName tp)

-- |Read the contents of tentative pending. CWD should be the repository directory.
readTentativePending :: RepoPatch p => Repository p C(r u t) -> IO (Sealed (FL (PrimOf p) C(t)))
readTentativePending (Repo _ _ _ tp) =
    readPendingFile (pendingName tp ++ ".tentative")

-- |Read the contents of tentative pending. CWD should be the repository directory.
readNewPending :: RepoPatch p => Repository p C(r u t) -> IO (Sealed (FL (PrimOf p) C(t)))
readNewPending (Repo _ _ _ tp) =
    readPendingFile (pendingName tp ++ ".new")

readPendingFile :: ReadPatch prim => String -> IO (Sealed (FL prim C(x)))
readPendingFile name = do
  pend <- gzReadFilePS name `catchall` return BS.empty
  return $ readPendingContents pend

-- Wrapper around FL where printed format uses { } except around singletons
-- Now that the Show behaviour of FL p can be customised (using showFLBehavior),
-- we could instead change the general behaviour of FL Prim; but since the pending
-- code can be kept nicely compartmentalised, it's nicer to do it this way.
newtype FLM p C(x y) = FLM { unFLM :: FL p C(x y) }

instance ReadPatch p => ReadPatch (FLM p) where
   readPatch' = mapSeal FLM <$> readMaybeBracketedFL readPatch' '{' '}'

instance ShowPatchBasic p => ShowPatchBasic (FLM p) where
   showPatch = showMaybeBracketedFL showPatch '{' '}' . unFLM

readPendingContents :: ReadPatch prim => BS.ByteString -> Sealed (FL prim C(x))
readPendingContents = maybe (Sealed NilFL) (mapSeal unFLM) . readPatch

writePendingFile :: ShowPatchBasic prim => String -> FL prim C(x y) -> IO ()
writePendingFile name = writePatch name . FLM

readMaybeBracketedFL :: forall m p C(x) . ParserM m =>
                         (FORALL(y) m (Sealed (p C(y)))) -> Char -> Char -> m (Sealed (FL p C(x)))
readMaybeBracketedFL parser pre post =
  bracketedFL parser pre post <|> (mapSeal (:>:NilFL) <$> parser)

showMaybeBracketedFL :: (FORALL(x y) p C(x y) -> Doc) -> Char -> Char -> FL p C(a b) -> Doc
showMaybeBracketedFL _       pre post NilFL         = text [pre] $$ text [post]
showMaybeBracketedFL printer _   _    (p :>: NilFL) = printer p
showMaybeBracketedFL printer pre post ps            = text [pre] $$ vcat (mapFL printer ps) $$ text [post]

-- |Read the contents of tentative pending. CWD should be the repository directory.
writeTentativePending :: RepoPatch p => Repository p C(r u t) -> FL (PrimOf p) C(t y) -> IO ()
writeTentativePending (Repo _ _ _ tp) pend =
    writePendingFile (pendingName tp ++ ".tentative") pend

-- |Read the contents of new pending. CWD should be the repository directory.
writeNewPending :: RepoPatch p => Repository p C(r u t) -> FL (PrimOf p) C(t y) -> IO ()
writeNewPending (Repo _ _ _ tp) pend =
    writePendingFile (pendingName tp ++ ".new") pend
