def prepare_payload(op, method, uri, data):
    """Return the URI (modified perhaps) and body and headers.

    - For GET requests, encode parameters in the query string.

    - Otherwise always encode parameters in the request body.

    - Except op; this can always go in the query string.

    :param method: The HTTP method.
    :param uri: The URI of the action.
    :param data: An iterable of ``name, value`` or ``name, opener``
        tuples (see `name_value_pair`) to pack into the body or
        query, depending on the type of request.
    """
    query = [] if op is None else [("op", op)]

    def slurp(opener):
        with opener() as fd:
            return fd.read()

    if method == "GET":
        headers, body = [], None
        query.extend(
            (name, slurp(value) if callable(value) else value)
            for name, value in data)
    else:
        # Even if data is empty, construct a multipart request body. Piston
        # (server-side) sets `request.data` to `None` if there's no payload.
        message = build_multipart_message(data)
        headers, body = encode_multipart_message(message)

    uri = urlparse(uri)._replace(query=urlencode(query)).geturl()
    return uri, body, headers