{-# LANGUAGE GADTs,MultiParamTypeClasses,FunctionalDependencies,FlexibleInstances,DeriveDataTypeable,ScopedTypeVariables,StandaloneDeriving #-}
-- | The "Extensions" module contributes two main things.  The first
-- is the definition and implementation of extensible message
-- features.  This means that the 'ExtField' data type is exported but
-- its constructor is (in an ideal world) hidden.
--
-- This first part also includes the keys for the extension fields:
-- the 'Key' data type.  These are typically defined in code generated
-- by 'hprotoc' from '.proto' file definitions.
--
-- The second main part is the 'MessageAPI' class which defines
-- 'getVal' and 'isSet'.  These allow uniform access to normal and
-- extension fields for users.
--
-- Access to extension fields is strictly though keys.  There is not
-- currently any way to query or change or clear any other extension
-- field data.
--
-- This module is likely to get broken up into pieces.
module Text.ProtocolBuffers.Extensions
  ( -- * Query functions for 'Key'
    getKeyFieldId,getKeyFieldType,getKeyDefaultValue
  -- * External types and classes
  , Key(..),ExtKey(..),MessageAPI(..)
  , PackedSeq(..), EP(..)
  -- * Internal types, functions, and classes
  , wireSizeExtField,wirePutExtField,loadExtension,notExtension
  , wireGetKeyToUnPacked, wireGetKeyToPacked
  , GPB,ExtField(..),ExtendMessage(..),ExtFieldValue(..)
  ) where

import Control.Monad.Error.Class(throwError)
import qualified Data.ByteString.Lazy as L
import qualified Data.Foldable as F
import Data.Map(Map)
import qualified Data.Map as M
import Data.Maybe(fromMaybe,isJust)
import Data.Monoid(mappend,mconcat)
import Data.Sequence((|>),(><),viewl,ViewL(..))
import qualified Data.Sequence as Seq(singleton,null,empty)
import Data.Typeable(Typeable,typeOf,cast)
import Data.Data(Data(gfoldl,gunfold,toConstr),Constr,DataType,Fixity(Prefix),mkDataType,mkConstr,constrIndex,dataTypeOf)

import Text.ProtocolBuffers.Basic
import Text.ProtocolBuffers.WireMessage
import Text.ProtocolBuffers.Reflections
import Text.ProtocolBuffers.Get as Get (Result(..),bytesRead)

err :: String -> b
err msg = error $ "Text.ProtocolBuffers.Extensions error\n"++msg

-- | The 'Key' data type is used with the 'ExtKey' class to put, get,
-- and clear external fields of messages.  The 'Key' can also be used
-- with the 'MessagesAPI' to get a possibly default value and to check
-- whether a key has been set in a message.
--
-- The 'Key' type (opaque to the user) has a phantom type of Maybe
-- or Seq that corresponds to Optional or Repeated fields. And a
-- second phantom type that matches the message type it must be used
-- with.  The third type parameter corresonds to the Haskell value
-- type.
--
-- The 'Key' is a GADT that puts all the needed class instances into
-- scope.  The actual content is the 'FieldId' ( numeric key), the
-- 'FieldType' (for sanity checks), and @Maybe v@ (a non-standard
-- default value).
--
-- When code is generated all of the known keys are taken into account
-- in the deserialization from the wire.  Unknown extension fields are
-- read as a collection of raw byte sequences.  If a key is then
-- presented it will be used to parse the bytes.
-- 
-- There is no guarantee for what happens if two Keys disagree about
-- the type of a field; in particular there may be undefined values
-- and runtime errors.  The data constructor for 'Key' has to be
-- exported to the generated code, but is not exposed to the user by
-- "Text.ProtocolBuffers".
--
data Key c msg v where
  Key :: (ExtKey c,ExtendMessage msg,GPB v) => FieldId -> FieldType -> (Maybe v) -> Key c msg v
  deriving (Typeable)


-- | This allows reflection, in this case it gives the numerical
-- 'FieldId' of the key, from 1 to 2^29-1 (excluding 19,000 through
-- 19,999).
getKeyFieldId :: Key c msg v -> FieldId
getKeyFieldId (Key fi _ _) = fi

-- | This allows reflection, in this case it gives the 'FieldType'
-- enumeration value (1 to 18) of the
-- "Text.DescriptorProtos.FieldDescriptorProto.Type" of the field.
getKeyFieldType :: Key c msg v -> FieldType
getKeyFieldType (Key _ ft _) = ft

-- | This will return the default value for a given 'Key', which is
-- set in the '.proto' file, or if unset it is the 'defaultValue' of
-- that type.
getKeyDefaultValue :: Key c msg v -> v
getKeyDefaultValue (Key _ _ md) = fromMaybe defaultValue md

instance (Typeable c, ExtendMessage msg, GPB v) => Show (Key c msg v) where
  show key@(Key fieldId fieldType maybeDefaultValue) =
    concat ["(Key (",show fieldId
           ,") (",show fieldType
           ,") (",show maybeDefaultValue
           ,") :: ",show (typeOf key)
           ,")"]

-- | The 'GPDyn' is my specialization of 'Dynamic'.  It hides the type
-- with an existential.  This is used in 'ExtOptional' for optional fields.
data GPDyn where
    GPDyn :: (GPB a) => !a -> GPDyn
  deriving (Typeable)

-- | The 'GPDynSeq' is another specialization of 'Dynamic' and is used
-- in 'ExtRepeated' for repeated fields.
data GPDynSeq where
    GPDynSeq :: GPB a => !(Seq a) -> GPDynSeq
  deriving (Typeable)

-- | The 'PackedSeq' is needed to distinguish the packed repeated format from the repeated format.
-- This is only used in the phantom type of Key.
newtype PackedSeq a = PackedSeq { unPackedSeq ::  (Seq a) }
  deriving (Typeable)

-- | The WireType is used to ensure the Seq is homogenous.
-- The ByteString is the unparsed input after the tag.
data ExtFieldValue = ExtFromWire !(Seq EP) -- XXX must store wiretype with ByteString
                   | ExtOptional !FieldType !GPDyn
                   | ExtRepeated !FieldType !GPDynSeq
                   | ExtPacked   !FieldType !GPDynSeq
  deriving (Typeable,Ord,Show)
           
-- | For making a Data instance for ExtField
data ExtDataPair = ExtDataPair FieldId (Seq EP)
  deriving (Typeable,Data,Show)

data EP = EP {-# UNPACK #-} !WireType !ByteString
  deriving (Typeable,Data,Eq,Ord,Show)

data DummyMessageType deriving (Typeable)

-- | ExtField is a newtype'd map from the numeric FieldId key to the
-- ExtFieldValue.  This allows for the needed class instances.
newtype ExtField = ExtField (Map FieldId ExtFieldValue)
  deriving (Typeable,Eq,Ord,Show)

-- | Used only in gfoldl for Data instance of ExtField
dataToList :: ExtField -> [ExtDataPair]
dataToList (ExtField ef) = map toEDP . M.toList $ ef where
  toEDP (fi,ExtFromWire eps) = ExtDataPair fi eps
  toEDP (fi,ExtOptional ft (GPDyn d)) =
    let p = wirePutOpt (toWireTag fi ft) ft (Just d)
        ep = EP (toWireType ft) (runPut p)
    in ExtDataPair fi (Seq.singleton ep)
  toEDP (fi,ExtRepeated ft (GPDynSeq s)) =
    let f :: forall w. Wire w => w -> EP
        f = EP (toWireType ft) . runPut . wirePutReq (toWireTag fi ft) ft
    in ExtDataPair fi (fmap f s)
  toEDP (fi,ExtPacked ft (GPDynSeq s)) =
    let p = wirePutPacked (toPackedWireTag fi) ft s
        ep = EP (snd. splitWireTag $ toPackedWireTag fi) (runPut p)
    in ExtDataPair fi (Seq.singleton ep)

-- | Used only in gfoldl and gunfold for Data instance of ExtField
dataFromList :: [ExtDataPair] -> ExtField
dataFromList = ExtField . M.fromList . map fromEDP where
  fromEDP (ExtDataPair fid eps) = (fid,ExtFromWire eps)

-- | Used only in gfoldl and gunfold for Data instance of ExtField
ty_ExtField :: DataType
ty_ExtField = mkDataType "Text.ProtocolBuffers.Extensions.ExtField" [con_ExtField]

-- | Used only in gfoldl and gunfold for Data instance of ExtField
con_ExtField :: Constr
con_ExtField = mkConstr ty_ExtField "ExtField" [] Prefix

instance Data ExtField where
  gfoldl f z m = z dataFromList `f` dataToList m
  gunfold k z c = case constrIndex c of
                    _ -> k (z dataFromList)
  toConstr (ExtField _) = con_ExtField
  dataTypeOf _ = ty_ExtField

  
instance ExtendMessage DummyMessageType where
  getExtField = undefined
  putExtField = undefined
  validExtRanges = undefined

-- I want a complicated comparison here to at least allow testing of
-- setting a field, writing to wire, reading back from wire, and
-- comparing.
--
-- The comparison of ExtFromWire with ExtFromWire is conservative
-- about returning True.  It is entirely possible that if both value
-- were interpreted by the same Key that their resulting values would
-- compare True.
instance Eq ExtFieldValue where
  (==) (ExtFromWire b) (ExtFromWire b') = b==b'
  (==) (ExtOptional a b) (ExtOptional a' b') = a==a' && b==b'
  (==) (ExtRepeated a b) (ExtRepeated a' b') = a==a' && b==b'
  (==) (ExtPacked a b)   (ExtPacked a' b')   = a==a' && b==b'
  (==) x@(ExtOptional ft (GPDyn w)) (ExtFromWire s') =
    let wt = toWireType ft
        makeKeyType :: a -> Key Maybe DummyMessageType a
        makeKeyType _ = undefined
        key = Key 0 ft Nothing `asTypeOf` makeKeyType w
    in case parseWireExtMaybe key wt s' of
         Right (_,y) -> x==y
         _ -> False
  (==) y@(ExtFromWire {}) x@(ExtOptional {})  = x == y
  (==) x@(ExtRepeated ft (GPDynSeq w)) (ExtFromWire s') =
    let wt = toWireType ft
        makeKeyType :: Seq a -> Key Seq DummyMessageType a
        makeKeyType _ = undefined
        key = Key 0 ft Nothing `asTypeOf` makeKeyType w
    in case parseWireExtSeq key wt s' of
         Right (_,y) -> x==y
         _ -> False
  (==) y@(ExtFromWire {}) x@(ExtRepeated {})  = x == y
  (==) x@(ExtPacked ft (GPDynSeq w)) (ExtFromWire s') =
    let wt = 2 -- all packed types have wire type 2, length delimited
        makeKeyType :: Seq a -> Key PackedSeq DummyMessageType a
        makeKeyType _ = undefined
        key = Key 0 ft Nothing `asTypeOf` makeKeyType w
    in case parseWireExtPackedSeq key wt s' of
         Right (_,y) -> x==y
         _ -> False
  (==) y@(ExtFromWire {}) x@(ExtPacked {})  = x == y
  (==) _ _ = False

-- | 'ExtendMessage' abstracts the operations of storing and
-- retrieving the 'ExtField' from the message, and provides the
-- reflection needed to know the valid field numbers.
--
-- This only used internally.
class Typeable msg => ExtendMessage msg where
  getExtField :: msg -> ExtField
  putExtField :: ExtField -> msg -> msg
  validExtRanges :: msg -> [(FieldId,FieldId)]

-- | wireKeyToUnPacked is used to load a repeated packed format into a repeated non-packed extension key
wireGetKeyToUnPacked :: (ExtendMessage msg,GPB v) => Key Seq msg v -> msg -> Get msg
wireGetKeyToUnPacked k@(Key i t mv) msg = do
  let myCast :: Maybe a -> Get (Seq a)
      myCast = undefined
  vv <- wireGetPacked t `asTypeOf` (myCast mv)
  let (ExtField ef) = getExtField msg
  v' <- case M.lookup i ef of
          Nothing -> return $ ExtRepeated t (GPDynSeq vv)
          Just (ExtRepeated t' (GPDynSeq s)) | t/=t' ->
            fail $ "wireGetKeyToUnPacked: Key mismatch! found wrong field type: "++show (k,t,t')
                                                       | otherwise ->
            case cast s of
              Nothing -> fail $ "wireGetKeyToUnPacked: previous Seq value cast failed: "++show (k,typeOf s)
              Just s' -> return $ ExtRepeated t (GPDynSeq (s' >< vv))
          Just (ExtFromWire raw) ->
            case parseWireExtSeq k (toWireType t) raw of -- was wt from ExtFromWire
              Left errMsg -> fail $ "wireGetKeyToUnPacked: Could not parseWireExtSeq: "++show k++"\n"++errMsg
              Right (_,ExtRepeated t' (GPDynSeq s)) | t/=t' ->
                fail $ "wireGetKeyToUnPacked:: Key mismatch! parseWireExtSeq returned wrong field type: "++show (k,t,t')
                                                              | otherwise ->
                case cast s of
                  Nothing -> fail $ "wireGetKey Seq: previous Seq value cast failed: "++show (k,typeOf s)
                  Just s' -> return $ ExtRepeated t (GPDynSeq (s' >< vv))
              wtf -> fail $ "wireGetKeyToUnPacked: Weird parseWireExtSeq return value: "++show (k,wtf)
          Just wtf@(ExtOptional {}) -> fail $ "wireGetKeyToUnPacked: ExtOptional found when ExtRepeated expected: "++show (k,wtf)
          Just wtf@(ExtPacked {}) -> fail $ "wireGetKeyToUnPacked: ExtPacked found when ExtRepeated expected: "++show (k,wtf)
  let ef' = M.insert i v' ef
  seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)

-- | wireKeyToPacked is used to load a repeated unpacked format into a repeated packed extension key
wireGetKeyToPacked :: (ExtendMessage msg,GPB v) => Key PackedSeq msg v -> msg -> Get msg
wireGetKeyToPacked k@(Key i t mv) msg = do
  let wt = toWireType t
      myCast :: Maybe a -> Get a
      myCast = undefined
  v <- wireGet t `asTypeOf` (myCast mv)
  let (ExtField ef) = getExtField msg
  v' <- case M.lookup i ef of
          Nothing -> return $ ExtPacked t (GPDynSeq (Seq.singleton v))
          Just (ExtPacked t' (GPDynSeq s)) | t/=t' ->
            fail $ "wireGetKeyToPacked: Key mismatch! found wrong field type: "++show (k,t,t')
                                                     | otherwise ->
            case cast s of
              Nothing -> fail $ "wireGetKeyToPacked: previous Seq value cast failed: "++show (k,typeOf s)
              Just s' -> seq v $ return $ ExtRepeated t (GPDynSeq (s' |> v))
          Just (ExtFromWire raw) ->
            case parseWireExtPackedSeq k wt raw of
              Left errMsg -> fail $ "wireGetKeyToPacked: Could not parseWireExtPackedSeq: "++show k++"\n"++errMsg
              Right (_,ExtPacked t' (GPDynSeq s)) | t/=t' ->
                fail $ "wireGetKeyToPacked: Key mismatch! parseWireExtPackedSeq returned wrong field type: "++show (k,t,t')
                                                              | otherwise ->
                case cast s of
                  Nothing -> fail $ "wireGetKeyToPacked: previous Seq value cast failed: "++show (k,typeOf s)
                  Just s' -> seq v $ return $ ExtRepeated t (GPDynSeq (s' |> v))
              wtf -> fail $ "wireGetKeyToPacked: Weird parseWireExtPackedSeq return value: "++show (k,wtf)
          Just wtf@(ExtOptional {}) -> fail $ "wireGetKeyToPacked: ExtOptional found when ExtPacked expected: "++show (k,wtf)
          Just wtf@(ExtRepeated {}) -> fail $ "wireGetKeyToPacked: ExtRepeated found when ExtPacked expected: "++show (k,wtf)
  let ef' = M.insert i v' ef
  seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)

-- | The 'ExtKey' class has three functions for user of the API:
-- 'putExt', 'getExt', and 'clearExt'.  The 'wireGetKey' is used in
-- generated code.
--
-- There are two instances of this class, 'Maybe' for optional message
-- fields and 'Seq' for repeated message fields.  This class allows
-- for uniform treatment of these two kinds of extension fields.
class ExtKey c where
  -- | Change or clear the value of a key in a message. Passing
  -- 'Nothing' with an optional key or an empty 'Seq' with a repeated
  -- key clears the value.  This function thus maintains the invariant
  -- that having a field number in the 'ExtField' map means that the
  -- field is set and not empty.
  --
  -- This should be only way to set the contents of a extension field.
  putExt :: Key c msg v -> c v -> msg -> msg
  -- | Access the key in the message.  Optional have type @(Key Maybe
  -- msg v)@ and return type @(Maybe v)@ while repeated fields have
  -- type @(Key Seq msg v)@ and return type @(Seq v)@.
  --
  -- There are a few sources of errors with the lookup of the key:
  --
  --  * It may find unparsed bytes from loading the message. 'getExt'
  --  will attempt to parse the bytes as the key\'s value type, and
  --  may fail.  The parsing is done with the 'parseWireExt' method
  --  (which is not exported to user API).
  --
  --  * The wrong optional-key versus repeated-key type is a failure
  -- 
  --  * The wrong type of the value might be found in the map and
  --  * cause a failure
  --
  -- The failures above should only happen if two different keys are
  -- used with the same field number.
  getExt :: Key c msg v -> msg -> Either String (c v)
  -- 'clearExt' unsets the field of the 'Key' if it is present.
  clearExt :: Key c msg v -> msg -> msg
  -- 'wireGetKey' is used in generated code to load extension fields
  -- which are defined in the same '.proto' file as the message.  This
  -- results in the storing the parsed type instead of the raw bytes
  -- inside the message.
  wireGetKey :: Key c msg v -> msg -> Get msg

-- | The 'Key' and 'GPWitness' GADTs use 'GPB' as a shorthand for many
-- classes.
class (Mergeable a,Default a,Wire a,Show a,Typeable a,Eq a,Ord a) => GPB a 

instance GPB Bool
instance GPB ByteString
instance GPB Utf8
instance GPB Double
instance GPB Float
instance GPB Int32
instance GPB Int64
instance GPB Word32
instance GPB Word64

instance Mergeable ExtField where
--  mergeEmpty = ExtField M.empty
  mergeAppend (ExtField m1) (ExtField m2) = ExtField (M.unionWith mergeExtFieldValue m1 m2)

mergeExtFieldValue :: ExtFieldValue -> ExtFieldValue -> ExtFieldValue
mergeExtFieldValue (ExtFromWire s1) (ExtFromWire s2) = ExtFromWire (mappend s1 s2)

mergeExtFieldValue (ExtOptional ft1 (GPDyn d1))
                   (ExtOptional ft2 (GPDyn d2)) =
  if ft1 /= ft2 then err $ "mergeExtFieldValue : ExtOptional FieldType mismatch "++show (ft1,ft2)
    else case cast d2 of
           Nothing -> err $ "mergeExtFieldValue : ExtOptional cast failed, FieldType "++show (ft2,typeOf d1,typeOf d2)
           Just d2' -> ExtOptional ft2 (GPDyn (mergeAppend d1 d2'))

mergeExtFieldValue (ExtRepeated ft1 (GPDynSeq s1))
                   (ExtRepeated ft2 (GPDynSeq s2)) =
  if ft1 /= ft2 then err $ "mergeExtFieldValue : ExtRepeated FieldType mismatch "++show (ft1,ft2)
    else case cast s2 of
           Nothing -> err $ "mergeExtFieldValue : ExtRepeated cast failed, FieldType "++show (ft2,typeOf s1,typeOf s2)
           Just s2' -> ExtRepeated ft2 (GPDynSeq (mappend s1 s2'))

mergeExtFieldValue (ExtPacked ft1 (GPDynSeq s1))
                   (ExtPacked ft2 (GPDynSeq s2)) =
  if ft1 /= ft2 then err $ "mergeExtFieldValue : ExtPacked FieldType mismatch "++show (ft1,ft2)
    else case cast s2 of
           Nothing -> err $ "mergeExtFieldValue : ExtPacked cast failed, FieldType "++show (ft2,typeOf s1,typeOf s2)
           Just s2' -> ExtPacked ft2 (GPDynSeq (mappend s1 s2'))

mergeExtFieldValue a b = err $ "mergeExtFieldValue : mismatch of constructors "++show (a,b)

instance Default ExtField where
  defaultValue = ExtField M.empty

instance Eq GPDyn where
  (==) a b = fromMaybe False (eqGPDyn a b)

instance Ord GPDyn where
  compare a b = fromMaybe (compare (show a) (show b)) (ordGPDyn a b)

instance Show GPDyn where
  showsPrec _n (GPDyn a) = ("(GPDyn "++) . (" ("++) . shows a . ("))"++)

instance Eq GPDynSeq where
  (==) a b = fromMaybe False (eqGPDynSeq a b)

instance Ord GPDynSeq where
  compare a b = fromMaybe (compare (show a) (show b)) (ordGPDynSeq a b)

instance Show GPDynSeq where
  showsPrec _n (GPDynSeq s) = ("(GPDynSeq "++) . (" ("++) . shows s . ("))"++)

ordGPDyn :: GPDyn -> GPDyn -> Maybe Ordering
ordGPDyn (GPDyn a1) (GPDyn a2) = fmap (compare a1) (cast a2)

eqGPDyn :: GPDyn -> GPDyn -> Maybe Bool
eqGPDyn (GPDyn a1) (GPDyn a2) = fmap (a1==) (cast a2)

ordGPDynSeq :: GPDynSeq -> GPDynSeq -> Maybe Ordering
ordGPDynSeq (GPDynSeq a1) (GPDynSeq  a2) = fmap (compare a1) (cast a2)

eqGPDynSeq :: GPDynSeq -> GPDynSeq -> Maybe Bool
eqGPDynSeq (GPDynSeq a1) (GPDynSeq a2) = fmap (a1==) (cast a2)

instance ExtKey Maybe where
  putExt key Nothing msg = clearExt key msg
  putExt (Key i t _) (Just v) msg =
    let (ExtField ef) = getExtField msg
        v' = ExtOptional t (GPDyn v)
        ef' = M.insert i v' ef
    in seq v' $ seq ef' (putExtField (ExtField ef') msg)

  clearExt (Key i _ _ ) msg =
    let (ExtField ef) = getExtField msg
        ef' = M.delete i ef
    in seq ef' (putExtField (ExtField ef') msg)

  getExt k@(Key i t _) msg =
    let wt = toWireType t
        (ExtField ef) = getExtField msg
    in case M.lookup i ef of
         Nothing -> Right Nothing
         Just (ExtFromWire raw) -> either Left (getExt' . snd) (parseWireExtMaybe k wt raw)
         Just x -> getExt' x
   where getExt' (ExtRepeated t' _) = Left $ "getExt Maybe: ExtField has repeated type: "++show (k,t')
         getExt' (ExtPacked t' _) = Left $ "getExt Maybe: ExtField has packed type: "++show (k,t')
         getExt' (ExtOptional t' (GPDyn d)) | t/=t' =
           Left $ "getExt Maybe: Key's FieldType does not match ExtField's: "++show (k,t')
                                                      | otherwise =
           case cast d of
             Nothing -> Left $ "getExt Maybe: Key's value cast failed: "++show (k,typeOf d)
             Just d' -> Right (Just d')
         getExt' (ExtFromWire {}) = err $ "Impossible? getExt.getExt' Maybe should not have ExtFromWire case (after parseWireExt)!"

  wireGetKey k@(Key i t mv) msg = do
    let wt = toWireType t
        myCast :: Maybe a -> Get a
        myCast = undefined
    v <- wireGet t `asTypeOf` (myCast mv)
    let (ExtField ef) = getExtField msg
    v' <- case M.lookup i ef of
            Nothing -> return $ ExtOptional t (GPDyn v)
            Just (ExtOptional t' (GPDyn vOld)) | t /= t' ->
              fail $ "wireGetKey Maybe: Key mismatch! found wrong field type: "++show (k,t,t')
                                                         | otherwise ->
              case cast vOld of
                Nothing -> fail $ "wireGetKey Maybe: previous Maybe value case failed: "++show (k,typeOf vOld)
                Just vOld' -> return $ ExtOptional t (GPDyn (mergeAppend vOld' v))
            Just (ExtFromWire raw) ->
              case parseWireExtMaybe k wt raw of
                Left errMsg -> fail $ "wireGetKey Maybe: Could not parseWireExtMaybe: "++show k++"\n"++errMsg
                Right (_,ExtOptional t' (GPDyn vOld)) | t/=t' ->
                  fail $ "wireGetKey Maybe: Key mismatch! found wrong field type: "++show (k,t,t')
                                                                | otherwise ->
                  case cast vOld of
                    Nothing -> fail $ "wireGetKey Maybe: previous Maybe value case failed: "++show (k,typeOf vOld)
                    Just vOld' -> return $ ExtOptional t (GPDyn (mergeAppend vOld' v))
                wtf -> fail $ "wireGetKey Maybe: Weird parseGetWireMaybe return value: "++show (k,wtf)
            Just wtf@(ExtRepeated {}) -> fail $ "wireGetKey Maybe: ExtRepeated found with ExtOptional expected: "++show (k,wtf)
            Just wtf@(ExtPacked {}) -> fail $ "wireGetKey Maybe: ExtPacked found with ExtOptional expected: "++show (k,wtf)
    let ef' = M.insert i v' ef
    seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)

-- | used by 'getVal' and 'wireGetKey' for the 'Maybe' instance
parseWireExtMaybe :: Key Maybe msg v -> WireType -> Seq EP -> Either String (FieldId,ExtFieldValue)
parseWireExtMaybe k@(Key fi ft mv)  wt raw | wt /= toWireType ft =
  Left $ "parseWireExt Maybe: Key's FieldType does not match ExtField's wire type: "++show (k,toWireType ft,wt)
                                           | otherwise = do
  let mkWitType :: Maybe a -> FieldType -> EP -> Either String (Seq a) 
      mkWitType = undefined
      chooseGet' = chooseGet `asTypeOf` (mkWitType mv)
  let parsed = map (chooseGet' ft) . F.toList $ raw
      errs = [ m | Left m <- parsed ]
  if null errs 
    then case viewl (mconcat [ a | Right a <- parsed ]) of
           EmptyL -> Left "Text.ProtocolBuffers.Extensions.parseWireExtMaybe: impossible empty parsed list"
           x :< xs -> Right (fi,(ExtOptional ft (GPDyn (F.foldl' mergeAppend x xs))))
    else Left (unlines errs)

-- | 'chooseGet' is an intermediate handler between parseWireExt* and applyGet.  This does not know
-- whether the EP will result in a single r or repeat r, so it always returns a Seq.  It may also
-- realize that there is a mismatch between the desired FieldType and the WireType
chooseGet :: (Wire r) => FieldType -> EP -> Either String (Seq r)
chooseGet ft (EP wt bsIn) =
  if (2==wt) && (isValidPacked ft)
    then applyGet (wireGetPacked ft) bsIn
    else if (wt == toWireType ft)
           then applyGet (fmap Seq.singleton $ wireGet ft) bsIn
           else Left $ "Text.ProtocolBuffers.Extensions.chooseGet: wireType mismatch "++show(wt,ft)

-- | Converts the the 'Result' into an 'Either' type and enforces
-- consumption of entire 'ByteString'.  Used by 'parseWireExtMaybe'
-- and 'parseWireExtSeq' to process raw wire input that has been
-- stored in an 'ExtField'.
applyGet :: Get r -> ByteString -> Either String r
applyGet g bsIn = resolveEOF (runGet g bsIn) where
  resolveEOF :: Result r -> Either String r
  resolveEOF (Failed i s) = Left ("Failed at "++show i++" : "++s)
  resolveEOF (Finished bs _i r) | L.null bs = Right r
                                | otherwise = Left "Not all input consumed"
  resolveEOF (Partial {}) = Left "Not enough input"

instance ExtKey Seq where
  putExt key@(Key i t _) s msg | Seq.null s = clearExt key msg
                               | otherwise =
      let (ExtField ef) = getExtField msg
          v' = ExtRepeated t (GPDynSeq s)
          ef' = M.insert i v' ef
      in seq v' $ seq ef' (putExtField (ExtField ef') msg)

  clearExt (Key i _ _) msg =
    let (ExtField ef) = getExtField msg
        ef' = M.delete i ef
    in seq ef' (putExtField (ExtField ef') msg)

  getExt k@(Key i t _) msg =
    let wt = toWireType t
        (ExtField ef) = getExtField msg
    in case M.lookup i ef of
         Nothing -> Right Seq.empty
         Just (ExtFromWire raw) -> either Left (getExt' . snd) (parseWireExtSeq k wt raw)
         Just x -> getExt' x
   where getExt' (ExtOptional t' _) = Left $ "getExt Seq: ExtField has optional type: "++show (k,t')
         getExt' (ExtPacked t' _) = Left $ "getExt Seq: ExtField has packed type: "++show (k,t')
         getExt' (ExtRepeated t' (GPDynSeq s)) | t'/=t =
           Left $ "getExt Seq: Key's FieldType does not match ExtField's: "++show (k,t')
                                                         | otherwise =
           case cast s of
             Nothing -> Left $ "getExt Seq: Key's Seq value cast failed: "++show (k,typeOf s)
             Just s' -> Right s'
         getExt' (ExtFromWire {}) = err $ "Impossible? getExt.getExt' Seq should not have ExtFromWire case (after parseWireExtSeq)!"
             
  -- This is more complicated than the Maybe instance because the old
  -- Seq needs to be retrieved and perhaps parsed and then appended
  -- to.  All sanity checks are included below.  TODO: do enough
  -- testing to be confident in removing some checks.
  wireGetKey k@(Key i t mv) msg = do
    let wt = toWireType t
        myCast :: Maybe a -> Get a
        myCast = undefined
    v <- wireGet t `asTypeOf` (myCast mv)
    let (ExtField ef) = getExtField msg
    v' <- case M.lookup i ef of
            Nothing -> return $ ExtRepeated t (GPDynSeq (Seq.singleton v))
            Just (ExtRepeated t' (GPDynSeq s)) | t/=t' ->
              fail $ "wireGetKey Seq: Key mismatch! found wrong field type: "++show (k,t,t')
                                                         | otherwise ->
              case cast s of
                Nothing -> fail $ "wireGetKey Seq: previous Seq value cast failed: "++show (k,typeOf s)
                Just s' -> seq v $ return $ ExtRepeated t (GPDynSeq (s' |> v))
            Just (ExtFromWire raw) ->
              case parseWireExtSeq k wt raw of
                Left errMsg -> fail $ "wireGetKey Seq: Could not parseWireExtSeq: "++show k++"\n"++errMsg
                Right (_,ExtRepeated t' (GPDynSeq s)) | t/=t' ->
                  fail $ "wireGetKey Seq: Key mismatch! parseWireExtSeq returned wrong field type: "++show (k,t,t')
                                                                | otherwise ->
                  case cast s of
                    Nothing -> fail $ "wireGetKey Seq: previous Seq value cast failed: "++show (k,typeOf s)
                    Just s' -> seq v $ return $ ExtRepeated t (GPDynSeq (s' |> v))
                wtf -> fail $ "wireGetKey Seq: Weird parseWireExtSeq return value: "++show (k,wtf)
            Just wtf@(ExtOptional {}) -> fail $ "wireGetKey Seq: ExtOptional found when ExtRepeated expected: "++show (k,wtf)
            Just wtf@(ExtPacked {}) -> fail $ "wireGetKey Seq: ExtPacked found when ExtRepeated expected: "++show (k,wtf)
    let ef' = M.insert i v' ef
    seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)

-- | used by 'getVal' and 'wireGetKey' for the 'Seq' instance
parseWireExtSeq :: Key Seq msg v -> WireType -> Seq EP -> Either String (FieldId,ExtFieldValue)
parseWireExtSeq k@(Key i t mv)  wt raw | wt /= toWireType t =
  Left $ "parseWireExtSeq: Key mismatch! Key's FieldType does not match ExtField's wire type: "++show (k,toWireType t,wt)
                                       | otherwise = do
  let mkWitType :: Maybe a -> FieldType -> EP -> Either String (Seq a) 
      mkWitType = undefined
      chooseGet' = chooseGet `asTypeOf` (mkWitType mv)
  let parsed = map (chooseGet' t) . F.toList $ raw
      errs = [ m | Left m <- parsed ]
  if null errs then Right (i,(ExtRepeated t (GPDynSeq (mconcat [ a | Right a <- parsed ]))))
    else Left (unlines errs)

instance ExtKey PackedSeq where
  putExt key@(Key i t _) (PackedSeq s) msg | Seq.null s = clearExt key msg
                                           | otherwise =
      let (ExtField ef) = getExtField msg
          v' = ExtPacked t (GPDynSeq s)
          ef' = M.insert i v' ef
      in seq v' $ seq ef' (putExtField (ExtField ef') msg)
    
  clearExt (Key i _ _) msg =
    let (ExtField ef) = getExtField msg
        ef' = M.delete i ef
    in seq ef' (putExtField (ExtField ef') msg)

  getExt k@(Key i t _) msg =
    let wt = toWireType t
        (ExtField ef) = getExtField msg
    in case M.lookup i ef of
         Nothing -> Right (PackedSeq Seq.empty)
         Just (ExtFromWire raw) -> either Left (getExt' . snd) (parseWireExtPackedSeq k wt raw)
         Just x -> getExt' x
   where getExt' (ExtOptional t' _) = Left $ "getExt PackedSeq: ExtField has optional type: "++show (k,t')
         getExt' (ExtRepeated t' _) = Left $ "getExt PackedSeq: ExtField has repeated type: "++show (k,t')
         getExt' (ExtPacked t' (GPDynSeq s)) | t'/=t =
           Left $ "getExt PackedSeq: Key's FieldType does not match ExtField's: "++show (k,t')
                                                       | otherwise =
           case cast s of
             Nothing -> Left $ "getExt PackedSeq: Key's Seq value cast failed: "++show (k,typeOf s)
             Just s' -> Right (PackedSeq s')
         getExt' (ExtFromWire {}) = err $ "Impossible? getExt.getExt' PackedSeq should not have ExtFromWire case (after parseWireExtSeq)!"

  wireGetKey k@(Key i t mv) msg = do
    let wt = toWireType t
        myCast :: Maybe a -> Get (Seq a)
        myCast = undefined
    vv <- wireGetPacked t `asTypeOf` (myCast mv)
    let (ExtField ef) = getExtField msg
    v' <- case M.lookup i ef of
            Nothing -> return $ ExtPacked t (GPDynSeq vv)
            Just (ExtPacked t' (GPDynSeq s)) | t/=t' ->
              fail $ "wireGetKey PackedSeq: Key mismatch! found wrong field type: "++show (k,t,t')
                                                       | otherwise ->
              case cast s of
                Nothing -> fail $ "wireGetKey PackedSeq: previous Seq value cast failed: "++show (k,typeOf s)
                Just s' -> return $ ExtRepeated t (GPDynSeq (s' >< vv))
            Just (ExtFromWire raw) ->
              case parseWireExtPackedSeq k wt raw of
                Left errMsg -> fail $ "wireGetKey PackedSeq: Could not parseWireExtPackedSeq: "++show k++"\n"++errMsg
                Right (_,ExtPacked t' (GPDynSeq s)) | t/=t' ->
                  fail $ "wireGetKey PackedSeq: Key mismatch! parseWireExtPackedSeq returned wrong field type: "++show (k,t,t')
                                                                | otherwise ->
                  case cast s of
                    Nothing -> fail $ "wireGetKey PackedSeq: previous Seq value cast failed: "++show (k,typeOf s)
                    Just s' -> return $ ExtRepeated t (GPDynSeq (s' >< vv))
                wtf -> fail $ "wireGetKey PackedSeq: Weird parseWireExtPackedSeq return value: "++show (k,wtf)
            Just wtf@(ExtOptional {}) -> fail $ "wireGetKey PackedSeq: ExtOptional found when ExtPacked expected: "++show (k,wtf)
-- XXX XXX XXX 2.3.0 need to add handling to the next line?
            Just wtf@(ExtRepeated {}) -> fail $ "wireGetKey PackedSeq: ExtRepeated found when ExtPacked expected: "++show (k,wtf)
    let ef' = M.insert i v' ef
    seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)

parseWireExtPackedSeq :: Key PackedSeq msg v -> WireType -> Seq EP -> Either String (FieldId,ExtFieldValue)
parseWireExtPackedSeq k@(Key i t mv) wt raw | wt /= 2 {- packed wire type is 2, length delimited -} =
  Left $ "parseWireExtPackedSeq: Key mismatch! Key's FieldType does not match ExtField's wire type: "++show (k,toWireType t,wt)
                                            | otherwise = do
  let mkWitType :: Maybe a -> FieldType -> EP -> Either String (Seq a) 
      mkWitType = undefined
      chooseGet' = chooseGet `asTypeOf` (mkWitType mv)
  let parsed = map (chooseGet' t) . F.toList $ raw
      errs = [ m | Left m <- parsed ]
  if null errs then Right (i,(ExtPacked t (GPDynSeq (mconcat [ a | Right a <- parsed ]))))
    else Left (unlines errs)

-- | This is used by the generated code
wireSizeExtField :: ExtField -> WireSize
wireSizeExtField (ExtField m) = F.foldl' aSize 0 (M.assocs m)  where
  aSize old (fi,(ExtFromWire raw)) =
    let toSize (EP wt bs) = size'WireTag (mkWireTag fi wt) + L.length bs
    in F.foldl' (\oldVal new -> oldVal + toSize new) old raw
  aSize old (fi,(ExtOptional ft (GPDyn d))) = old +
    let tagSize = size'WireTag (toWireTag fi ft)
    in wireSizeReq tagSize ft d
  aSize old (fi,(ExtRepeated ft (GPDynSeq s))) = old +
    let tagSize = size'WireTag (toWireTag fi ft)
    in wireSizeRep tagSize ft s
  aSize old (fi,(ExtPacked ft (GPDynSeq s))) = old +
    let tagSize = size'WireTag (toPackedWireTag fi)
    in wireSizePacked tagSize ft s

-- | This is used by the generated code. The data is serialized in
-- order of increasing field number.
wirePutExtField :: ExtField -> Put
wirePutExtField (ExtField m) = mapM_ aPut (M.assocs m) where
  aPut (fi,(ExtFromWire raw)) = F.mapM_ (\(EP wt bs) -> putVarUInt (getWireTag $ mkWireTag fi wt) >> putLazyByteString bs) raw
  aPut (fi,(ExtOptional ft (GPDyn d))) = wirePutOpt (toWireTag fi ft) ft (Just d)
  aPut (fi,(ExtRepeated ft (GPDynSeq s))) = wirePutRep (toWireTag fi ft) ft s
  aPut (fi,(ExtPacked   ft (GPDynSeq s))) = wirePutPacked (toPackedWireTag fi) ft s

notExtension :: (ReflectDescriptor a, ExtendMessage a,Typeable a) => FieldId -> WireType -> a -> Get a
notExtension fieldId _wireType msg = throwError ("Field id "++show fieldId++" is not a valid extension field id for "++show (typeOf (undefined `asTypeOf` msg)))

-- | get a value from the wire into the message's ExtField. This is used by generated code for
-- extensions that were not known at compile time.
loadExtension :: (ReflectDescriptor a, ExtendMessage a) => FieldId -> WireType -> a -> Get a
--loadExtension fieldId wireType msg | isValidExt fieldId msg = do -- XXX check moved to generated code
--loadExtension fieldId wireType msg = unknown fieldId wireType msg -- XXX
loadExtension fieldId wireType msg = do
  let (ExtField ef) = getExtField msg
      badwt :: WireType -> Get a
      badwt wt = do here <- bytesRead
                    fail $ "Conflicting wire types at byte position "++show here ++ " for extension to message: "++show (typeOf msg,fieldId,wireType,wt)
  case M.lookup fieldId ef of
    Nothing -> do
       bs <- wireGetFromWire fieldId wireType
       let v' = ExtFromWire (Seq.singleton (EP wireType bs))
           ef' = M.insert fieldId v' ef
       seq v' $ seq ef' $ return $ putExtField (ExtField ef') msg
    Just (ExtFromWire raw) -> do
      bs <- wireGetFromWire fieldId wireType
      let v' = seq bs $ ExtFromWire (raw |> (EP wireType bs))
          ef' = M.insert fieldId v' ef
      seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)
    Just (ExtOptional ft (GPDyn a)) | toWireType ft /= wireType -> badwt (toWireType ft)
                                    | otherwise -> do
      b <- wireGet ft
      let v' = ExtOptional ft (GPDyn (mergeAppend a b))
          ef' = M.insert fieldId v' ef
      seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)
-- handle wireType of "2" when toWireType ft is not "2" but ft could be packed by using wireGetPacked ft
    Just (ExtRepeated ft (GPDynSeq s)) | toWireType ft /= wireType -> if (wireType==2) && (isValidPacked ft) 
                                                                        then do
                                                                          aa <- wireGetPacked ft
                                                                          let v' = ExtRepeated ft (GPDynSeq (s >< aa))
                                                                              ef' = M.insert fieldId v' ef
                                                                          seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)
                                                                        else badwt (toWireType ft)
                                       | otherwise -> do
      a <- wireGet ft
      let v' = seq a $ ExtRepeated ft (GPDynSeq (s |> a))
          ef' = M.insert fieldId v' ef
      seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)
-- handle wireType of NOT "2" when wireType is good match for ft by using wireGet ft
    Just (ExtPacked ft (GPDynSeq s)) | 2 /= wireType -> if (toWireType ft) == wireType
                                                          then do
                                                            a <- wireGet ft
                                                            let v' = seq a $ ExtPacked ft (GPDynSeq (s |> a))
                                                                ef' = M.insert fieldId v' ef
                                                            seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)
                                                          else badwt 2  {- packed uses length delimited: 2 -}
                                     | otherwise -> do
      aa <- wireGetPacked ft
      let v' = ExtPacked ft (GPDynSeq (s >< aa))
          ef' = M.insert fieldId v' ef
      seq v' $ seq ef' $ return (putExtField (ExtField ef') msg)

class MessageAPI msg a b | msg a -> b where
  -- | Access data in a message.  The first argument is always the
  -- message.  The second argument can be one of 4 categories.
  --
  -- * The field name of a required field acts a simple retrieval of
  -- the data from the message.
  --
  -- * The field name of an optional field will retreive the data if
  -- it is set or lookup the default value if it is not set.
  --
  -- * The field name of a repeated field always retrieves the
  -- (possibly empty) 'Seq' of values.
  --
  -- * A Key for an optional or repeated value will act as the field
  -- name does above, but if there is a type mismatch or parse error
  -- it will use the defaultValue for optional types and an empty
  -- sequence for repeated types.
  getVal :: msg -> a -> b

  -- | Check whether data is present in the message.
  --
  -- * Required fields always return 'True'.
  --
  -- * Optional fields return whether a value is present.
  --
  -- * Repeated field return 'False' if there are no values, otherwise
  -- they return 'True'.
  --
  -- * Keys return as optional or repeated, but checks only if the
  -- field # is present.  This assumes that there are no collisions
  -- where more that one key refers to the same field number of this
  -- message type.
  isSet :: msg -> a -> Bool
  isSet _ _ = True

instance (Default msg,Default a) => MessageAPI msg (msg -> Maybe a) a where
  getVal m f = fromMaybe (fromMaybe defaultValue (f defaultValue)) (f m)
  isSet m f = isJust (f m)

instance MessageAPI msg (msg -> (Seq a)) (Seq a) where
  getVal m f = f m
  isSet m f = not (Seq.null (f m))

instance (Default v) => MessageAPI msg (Key Maybe msg v) v where
  getVal m k@(Key _ _ md) = case getExt k m of
                              Right (Just v) -> v
                              _ -> fromMaybe defaultValue md
  isSet m (Key fid _ _) = let (ExtField x) = getExtField m
                          in M.member fid x

instance (Default v) => MessageAPI msg (Key Seq msg v) (Seq v) where
  getVal m k@(Key _ _ _) = case getExt k m of
                             Right s -> s
                             _ -> Seq.empty
  isSet m (Key fid _ _) = let (ExtField x) = getExtField m
                          in M.member fid x

instance MessageAPI msg (msg -> ByteString) ByteString where getVal m f = f m
instance MessageAPI msg (msg -> Utf8) Utf8 where getVal m f = f m
instance MessageAPI msg (msg -> Double) Double where getVal m f = f m
instance MessageAPI msg (msg -> Float) Float where getVal m f = f m
instance MessageAPI msg (msg -> Int32) Int32 where getVal m f = f m
instance MessageAPI msg (msg -> Int64) Int64 where getVal m f = f m
instance MessageAPI msg (msg -> Word32) Word32 where getVal m f = f m
instance MessageAPI msg (msg -> Word64) Word64 where getVal m f = f m

-- Must keep synchronized with Parser.isValidPacked
isValidPacked :: FieldType -> Bool
isValidPacked fieldType =
  case fieldType of
    9 -> False
    10 -> False
    11 -> False -- Impossible value for typeCode from parseType, but here for completeness
    12 -> False
    _ -> True
