{-# LANGUAGE OverloadedStrings #-}

module Data.Bitcoin.PaymentChannel.Internal.Bitcoin.Script where

import Data.Bitcoin.PaymentChannel.Internal.Types
import Data.Bitcoin.PaymentChannel.Internal.Serialization   ()
import Data.Bitcoin.PaymentChannel.Internal.Util

import qualified  Network.Haskoin.Internals as HI
import qualified  Network.Haskoin.Crypto as HC
import qualified Data.ByteString as B


valReceiverSigHash = SigAll True

-- |Generates OP_CHECKLOCKTIMEVERIFY redeemScript, which can be redeemed in two ways:
--  1) by providing a signature from both server and client
--  2) after the date specified by lockTime: by providing only a client signature
paymentChannelRedeemScript :: SendPubKey
                           -> RecvPubKey
                           -> BitcoinLockTime
                           -> Script
paymentChannelRedeemScript clientPK serverPK lockTime =
    let
        -- Note: HI.encodeInt encodes values up to and including 2^31-1 as 4 bytes
        --      and values 2^31 through 2^32-1 (upper limit) as 5 bytes.
        encodeScriptInt = convertEmptyPush . opPushData . B.pack .
                          HI.encodeInt . fromIntegral . toWord32
        -- A push of an empty byte string and OP_0 are one and the same,
        --  but haskoin-core deserializes into OP_0.
        convertEmptyPush (OP_PUSHDATA "" OPCODE) = OP_0
        convertEmptyPush whatever = whatever
        serverPubKey    = getPubKey serverPK
        clientPubKey    = getPubKey clientPK
    in Script
             [OP_IF,
                 opPushData $ serialize serverPubKey, OP_CHECKSIGVERIFY,
             OP_ELSE,
                  encodeScriptInt lockTime, op_CHECKLOCKTIMEVERIFY, OP_DROP,
             OP_ENDIF,
             opPushData $ serialize clientPubKey, OP_CHECKSIG]



-- |scriptSig fulfilling 'paymentChannelRedeemScript' using two signatures (client+server)
paymentTxScriptSig :: PaymentSignature -> PaymentSignature -> Script --ScriptSig
paymentTxScriptSig clientSig serverSig = Script
    [opPushData $ serialize clientSig, --sig including SigHash byte
    opPushData $ serialize serverSig, --sig including SigHash byte
    OP_1]   -- Signal that we want to provide both PubKeys

-- |scriptSig fulfilling 'paymentChannelRedeemScript' using a single signature (client)
--  (not valid until specified lockTime)
refundTxScriptSig :: HC.Signature -> Script
refundTxScriptSig clientSig = Script
    [opPushData (B.append (serialize clientSig) hashTypeByte),
    OP_0]   -- Signal that we want to provide only one pubkey/sig pair (sender's),
            -- after it is checked that the lockTime has expired in the script
        where hashTypeByte = serialize (SigAll False)

-----Util-----

op_CHECKLOCKTIMEVERIFY = OP_NOP2

scriptToP2SHAddress :: Script -> HC.Address
scriptToP2SHAddress = HC.ScriptAddress . HC.hash160 . HC.getHash256 . HC.hash256 . serialize

getP2SHFundingAddress :: ChannelParameters -> HC.Address
getP2SHFundingAddress = scriptToP2SHAddress . getRedeemScript

getRedeemScript :: ChannelParameters -> Script
getRedeemScript (CChannelParameters senderPK recvrPK lockTime) =
    paymentChannelRedeemScript senderPK recvrPK lockTime

getRedeemScriptBS :: ChannelParameters -> B.ByteString
getRedeemScriptBS = serialize . getRedeemScript

getP2SHInputScript :: ChannelParameters -> Script -> Script
getP2SHInputScript cp scriptSig =
    Script $ scriptOps scriptSig ++ redeemScript
        where
             redeemScript = [opPushData $ getRedeemScriptBS cp]