contract Messages {
    struct AcceptGame {
        uint256 bet;
        bool isHost;
        address opponentAddress;
        bytes32 hashOfMySecret;
        bytes32 hashOfOpponentSecret;
    }
    
    struct SecretData {
        bytes32 salt;
        uint8 secret;
    }

    /**
     * Domain separator encoding per EIP 712.
     * keccak256(
     *     "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
     * )
     */
    bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472;

    /**
     * AcceptGame struct type encoding per EIP 712
     * keccak256(
     *     "AcceptGame(uint256 bet,bool isHost,address opponentAddress,bytes32 hashOfMySecret,bytes32 hashOfOpponentSecret)"
     * )
     */
    bytes32 private constant ACCEPTGAME_TYPEHASH = 0x5ceee84403c984fbd9fb4ebf11b09c4f28f87290116c8b7f24a3e2a89d26588f;

    /**
     * Domain separator per EIP 712
     */
    bytes32 public DOMAIN_SEPARATOR;

    /**
     * @notice Calculates acceptGameHash according to EIP 712.
     * @param _acceptGame AcceptGame instance to hash.
     * @return bytes32 EIP 712 hash of _acceptGame.
     */
    function _hash(AcceptGame memory _acceptGame) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            ACCEPTGAME_TYPEHASH,
            _acceptGame.bet,
            _acceptGame.isHost,
            _acceptGame.opponentAddress,
            _acceptGame.hashOfMySecret,
            _acceptGame.hashOfOpponentSecret
        ));
    }

    /**
     * @notice Calculates secretHash according to EIP 712.
     * @param _salt Salt of the gamer.
     * @param _secret Secret of the gamer.
     */
    function _hashOfSecret(bytes32 _salt, uint8 _secret) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(_salt, _secret));
    }

    /**
     * @return the recovered address from the signature
     */
    function _recoverAddress(
        bytes32 messageHash,
        bytes memory signature
    )
        internal
        view
        returns (address) 
    {
        bytes32 r;
        bytes32 s;
        bytes1 v;
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := mload(add(signature, 0x60))
        }
        bytes32 digest = keccak256(abi.encodePacked(
            "\x19\x01",
            DOMAIN_SEPARATOR,
            messageHash
        ));
        return ecrecover(digest, uint8(v), r, s);
    }

    /**
     * @return the address of the gamer signing the AcceptGameMessage
     */
    function _getSignerAddress(
        uint256 _value,
        bool _isHost,
        address _opponentAddress,
        bytes32 _hashOfMySecret,
        bytes32 _hashOfOpponentSecret,
        bytes memory signature
    ) 
        internal
        view
        returns (address playerAddress) 
    {   
        AcceptGame memory message = AcceptGame({
            bet: _value,
            isHost: _isHost,
            opponentAddress: _opponentAddress,
            hashOfMySecret: _hashOfMySecret,
            hashOfOpponentSecret: _hashOfOpponentSecret
        });
        bytes32 messageHash = _hash(message);
        playerAddress = _recoverAddress(messageHash, signature);
    }
}
/**
 * EIP712 Ethereum typed structured data hashing and signing
 */
function _recoverAddress(
    bytes32 messageHash,
    bytes memory signature
)
    internal
    view
    returns (address) 
{
    bytes32 r;
    bytes32 s;
    bytes1 v;
    // solium-disable-next-line security/no-inline-assembly
    assembly {
        r := mload(add(signature, 0x20))
        s := mload(add(signature, 0x40))
        v := mload(add(signature, 0x60))
    }
    bytes32 digest = keccak256(abi.encodePacked(
        "\x19\x01",
        DOMAIN_SEPARATOR,
        messageHash
    ));
    return ecrecover(digest, uint8(v), r, s);
}

/**
 * @return the recovered address from the signature
 */