module PaymentChannel.Internal.Receiver.Util
( updState
--, mkExtendedKeyRPC
, mkExtendedDerivRpc
, metaKeyIndex
--, setMetadata
, setChannelStatus
, getChannelStatus
, markAsBusy
, isReadyForPayment
, checkChannelStatus
, getClosingPayment
, calcNewData
, updateMetadata
, module PaymentChannel.Internal.Receiver.Types
, module PaymentChannel.Internal.Class.Value
)
where

import PaymentChannel.Internal.Class.Value
import PaymentChannel.Internal.Receiver.Types
import qualified PaymentChannel.Internal.Settlement.Util as Settle
import PaymentChannel.Internal.Error
import PaymentChannel.Internal.Payment
import PaymentChannel.Internal.Metadata.Util
import PaymentChannel.Internal.Class.Value     (HasValue(..))
import qualified Network.Haskoin.Crypto as HC



updState :: ServerPayChanG kd sd -> SignedPayment -> ServerPayChanG kd BtcSig
updState rpc p =
    rpc { rpcState = replacePayment (rpcState rpc) p }
  where
    replacePayment state p' = state { pcsPayment = p' }

-- |Create a 'ServerPayChanX', which has an associated BIP32 extended pubkey, from a
--  'ServerPayChan'
mkExtendedKeyRPC :: ServerPayChanI kd -> ExtPub -> Maybe ServerPayChanX
mkExtendedKeyRPC rpc@(MkServerPayChan pcs _) xpk =
    -- Check that it's the right pubkey first
    if getKey xpk == getPubKey (getRecvPubKey pcs) then
            Just $ mkExtendedKeyRPCUnsafe xpk rpc
        else
            Nothing

-- | Add an 'ExtPub' to a 'ServerPayChan' that has been created with a server
--    pubkey derived using 'PaymentChannel.Internal.ChanScript.deriveRecvPub'
mkExtendedDerivRpc :: RootPub -> ServerPayChanI kd -> Maybe ServerPayChanX
mkExtendedDerivRpc rootPub spc =
    if derivedChanParams == chanParams
        then Just $ setKeyMetadata_ spc xPub
        else Nothing
  where
    (derivedChanParams,xPub) = deriveRecvPub rootPub chanParams
    chanParams = pairRedeemScript . pcsPayment . rpcState $ spc


mkExtendedKeyRPCUnsafe :: ExtPub -> ServerPayChanI kd -> ServerPayChanX
mkExtendedKeyRPCUnsafe xPub rpc =
    rpc {
        rpcMetadata =
            Metadata xPub 0 [] (valueOf $ rpcState rpc) ReadyForPayment
        }

--mkDummyExtendedRPC :: ServerPayChanI kd -> ServerPayChanX
--mkDummyExtendedRPC = mkExtendedKeyRPCUnsafe 0

metaKeyIndex :: ServerPayChanI KeyDeriveIndex -> KeyDeriveIndex
metaKeyIndex = mdKeyData . rpcMetadata

-- | INTERNAL: Server/receiver: set pubkey metadata
setKeyMetadata_ :: ServerPayChanG a sd -> b -> ServerPayChanG b sd
setKeyMetadata_ sp@MkServerPayChan{..} kd =
    sp { rpcMetadata =
            rpcMetadata { mdKeyData = kd }
       }


-- Status
setChannelStatus :: PayChanStatus -> ServerPayChanG kd sd -> ServerPayChanG kd sd
setChannelStatus s pcs@MkServerPayChan{ rpcMetadata = meta } =
    pcs { rpcMetadata = metaSetStatus s meta }

getChannelStatus :: ServerPayChanG kd sd -> PayChanStatus
getChannelStatus MkServerPayChan{ rpcMetadata = meta } =
    metaGetStatus meta

markAsBusy :: ServerPayChanG kd sd -> ServerPayChanG kd sd
markAsBusy = setChannelStatus PaymentInProgress

isReadyForPayment :: ServerPayChanG kd sd -> Bool
isReadyForPayment =
    (== ReadyForPayment) . getChannelStatus

checkChannelStatus :: ServerPayChanG kd sd -> Either PayChanError (ServerPayChanG kd sd)
checkChannelStatus rpc =
    maybe
    (Right rpc)
    (Left . StatusError)
    (checkReadyForPayment $ getChannelStatus rpc)

getClosingPayment :: ServerPayChanI kd -> Maybe SignedPayment
getClosingPayment spc =
    case getChannelStatus spc of
        ChannelClosed p -> Just p
        _               -> Nothing

-- Metadata
calcNewData :: MetadataI a -> PayChanState BtcSig -> MetadataI a
calcNewData md@Metadata{ mdUnsettledValue = oldValRecvd, mdPayCount = oldCount } pcs =
    md { mdUnsettledValue = checkedVal, mdPayCount = oldCount+1 }
    where checkedVal  = if newValRecvd < oldValRecvd then error "BUG: Value lost :(" else newValRecvd
          newValRecvd = valueOf pcs

updateMetadata :: ServerPayChanI kd -> ServerPayChanI kd
updateMetadata rpc@MkServerPayChan{..} =
    rpc { rpcMetadata = calcNewData rpcMetadata rpcState }