Validates and normalizes a set of parameters for an offer to send.
NOTE: This returns “mtime” as epoch seconds, not a Date. This is
inconsistent with the get_details() method in Session, but it’s
more useful for sending over the wire.

@param {FileDetails} params - The file details. Some fairly trivial
variances from the specification are allowed.

@return {FileDetails} The parameters that should be sent. `mtime`
will be a Date rather than a number.
function offer_parameters(params) {
        if (!params.name) {
            throw new Zmodem.Error("validation", "Need “name”!");
        }

        if (typeof params.name !== "string") {
            throw new Zmodem.Error("validation", "“name” (" + params.name + ") must be a string!");
        }

        //So that we can override values as is useful
        //without affecting the passed-in object.
        params = Object.assign({}, params);

        if (LOOKS_LIKE_ZMODEM_HEADER.test(params.name)) {
            console.warn("The filename " + JSON.stringify(name) + " contains characters that look like a ZMODEM header. This could corrupt the ZMODEM session; consider renaming it so that the filename doesn’t contain control characters.");
        }

        if (params.serial !== null && params.serial !== undefined) {
            throw new Zmodem.Error("validation", "“serial” is meaningless.");
        }

        params.serial = null;

        ["size", "mode", "files_remaining", "bytes_remaining"].forEach(
            function(k) {
                var ok;
                switch (typeof params[k]) {
                    case "object":
                        ok = (params[k] === null);
                        break;
                    case "undefined":
                        params[k] = null;
                        ok = true;
                        break;
                    case "number":
                        _validate_number(k, params[k]);

                        ok = true;
                        break;
                }

                if (!ok) {
                    throw new Zmodem.Error("validation", "“" + k + "” (" + params[k] + ") must be null, undefined, or a number.");
                }
            }
        );

        if (typeof params.mode === "number") {
            params.mode |= 0x8000;
        }

        if (params.files_remaining === 0) {
            throw new Zmodem.Error("validation", "“files_remaining”, if given, must be positive.");
        }

        var mtime_ok;
        switch (typeof params.mtime) {
            case "object":
                mtime_ok = true;

                if (params.mtime instanceof Date) {

                    var date_obj = params.mtime;
                    params.mtime = Math.floor( date_obj.getTime() / 1000 );
                    if (params.mtime < 0) {
                        throw new Zmodem.Error("validation", "“mtime” (" + date_obj + ") must not be earlier than 1970.");
                    }
                }
                else if (params.mtime !== null) {
                    mtime_ok = false;
                }

                break;

            case "undefined":
                params.mtime = null;
                mtime_ok = true;
                break;
            case "number":
                _validate_number("mtime", params.mtime);
                mtime_ok = true;
                break;
        }

        if (!mtime_ok) {
            throw new Zmodem.Error("validation", "“mtime” (" + params.mtime + ") must be null, undefined, a Date, or a number.");
        }

        return params;
    }