{-# OPTIONS_GHC -fno-warn-orphans #-}
module Darcs.Patch.Prim.V1.Read () where

import Prelude ()
import Darcs.Prelude

import Darcs.Patch.Prim.Class ( PrimRead(..), hunk, binary )
import Darcs.Patch.Prim.V1.Core
    ( Prim(..)
    , DirPatchType(..)
    , FilePatchType(..)
    )

import Darcs.Util.Path ( fn2fp )
import Darcs.Patch.Format ( FileNameFormat )
import Darcs.Patch.Read ( readFileName )
import Darcs.Patch.ReadMonads
    ( ParserM, takeTillChar, string, int
    , option, choice, anyChar, char, myLex'
    , skipSpace, skipWhile, linesStartingWith
    )

import Darcs.Patch.Witnesses.Sealed ( seal )

import Darcs.Util.ByteString ( fromHex2PS )

import Control.Monad ( liftM )
import qualified Data.ByteString       as B  ( ByteString, init, tail, concat )
import qualified Data.ByteString.Char8 as BC ( unpack, pack )


instance PrimRead Prim where
  readPrim fmt
     = skipSpace >> choice
       [ return' $ readHunk fmt
       , return' $ readAddFile fmt
       , return' $ readAddDir fmt
       , return' $ readMove fmt
       , return' $ readRmFile fmt
       , return' $ readRmDir fmt
       , return' $ readTok fmt
       , return' $ readBinary fmt
       , return' readChangePref
       ]
    where
    return'  = liftM seal

hunk' :: B.ByteString
hunk' = BC.pack "hunk"

replace :: B.ByteString
replace = BC.pack "replace"

binary' :: B.ByteString
binary' = BC.pack "binary"

addfile :: B.ByteString
addfile = BC.pack "addfile"

adddir :: B.ByteString
adddir = BC.pack "adddir"

rmfile :: B.ByteString
rmfile = BC.pack "rmfile"

rmdir :: B.ByteString
rmdir = BC.pack "rmdir"

move :: B.ByteString
move = BC.pack "move"

changepref :: B.ByteString
changepref = BC.pack "changepref"

readHunk :: ParserM m => FileNameFormat -> m (Prim wX wY)
readHunk fmt = do
  string hunk'
  fi <- myLex'
  l <- int
  have_nl <- skipNewline
  if have_nl
    then do
      _ <- linesStartingWith ' ' -- skipping context
      old <- linesStartingWith '-'
      new <- linesStartingWith '+'
      _ <- linesStartingWith ' ' -- skipping context
      return $ hunk (fn2fp $ readFileName fmt fi) l old new
    else return $ hunk (fn2fp $ readFileName fmt fi) l [] []

skipNewline :: ParserM m => m Bool
skipNewline = option False (char '\n' >> return True)

readTok :: ParserM m => FileNameFormat -> m (Prim wX wY)
readTok fmt = do
  string replace
  f <- myLex'
  regstr <- myLex'
  o <- myLex'
  n <- myLex'
  return $ FP (readFileName fmt f) $ TokReplace (BC.unpack (drop_brackets regstr))
                          (BC.unpack o) (BC.unpack n)
    where drop_brackets = B.init . B.tail


-- * Binary file modification
--
-- | Modify a binary file
--
-- > binary FILENAME
-- > oldhex
-- > *HEXHEXHEX
-- > ...
-- > newhex
-- > *HEXHEXHEX
-- > ...
readBinary :: ParserM m => FileNameFormat -> m (Prim wX wY)
readBinary fmt = do
  string binary'
  fi <- myLex'
  _ <- myLex'
  skipSpace
  old <- linesStartingWith '*'
  _ <- myLex'
  skipSpace
  new <- linesStartingWith '*'
  return $ binary (fn2fp $ readFileName fmt fi)
                  (fromHex2PS $ B.concat old)
                  (fromHex2PS $ B.concat new)

readAddFile :: ParserM m => FileNameFormat -> m (Prim wX wY)
readAddFile fmt = do
  string addfile
  f <- myLex'
  return $ FP (readFileName fmt f) AddFile

readRmFile :: ParserM m => FileNameFormat -> m (Prim wX wY)
readRmFile fmt = do
  string rmfile
  f <- myLex'
  return $ FP (readFileName fmt f) RmFile

readMove :: ParserM m => FileNameFormat -> m (Prim wX wY)
readMove fmt = do
  string move
  d <- myLex'
  d' <- myLex'
  return $ Move (readFileName fmt d) (readFileName fmt d')

readChangePref :: ParserM m => m (Prim wX wY)
readChangePref = do
  string changepref
  p <- myLex'
  skipWhile (== ' ')
  _ <- anyChar -- skip newline
  f <- takeTillChar '\n'
  _ <- anyChar -- skip newline
  t <- takeTillChar '\n'
  return $ ChangePref (BC.unpack p) (BC.unpack f) (BC.unpack t)

readAddDir :: ParserM m => FileNameFormat -> m (Prim wX wY)
readAddDir fmt = do
  string adddir
  f <- myLex'
  return $ DP (readFileName fmt f) AddDir

readRmDir :: ParserM m => FileNameFormat -> m (Prim wX wY)
readRmDir fmt = do
  string rmdir
  f <- myLex'
  return $ DP (readFileName fmt f) RmDir
