import HTTPMethod from "../API/HTTPMethod";
import Hostname from "../API/Hostname";
import URL from "../API/URL";
import DatabaseProperty from "../Database/DatabaseProperty";
import BadDataException from "../Exception/BadDataException";
import IP from "../IP/IP";
import { ObjectType } from "../JSON";
import JSONFunctions from "../JSONFunctions";
import ObjectID from "../ObjectID";
import Port from "../Port";
import MonitorCriteria from "./MonitorCriteria";
import { MonitorStepLogMonitorUtil, } from "./MonitorStepLogMonitor";
import MonitorType from "./MonitorType";
import { MonitorStepTraceMonitorUtil, } from "./MonitorStepTraceMonitor";
import { MonitorStepMetricMonitorUtil, } from "./MonitorStepMetricMonitor";
import { MonitorStepExceptionMonitorUtil, } from "./MonitorStepExceptionMonitor";
import { MonitorStepProfileMonitorUtil, } from "./MonitorStepProfileMonitor";
import { MonitorStepSnmpMonitorUtil, } from "./MonitorStepSnmpMonitor";
import { MonitorStepDnsMonitorUtil, } from "./MonitorStepDnsMonitor";
import { MonitorStepDomainMonitorUtil, } from "./MonitorStepDomainMonitor";
import { MonitorStepDnssecMonitorUtil, } from "./MonitorStepDnssecMonitor";
import { MonitorStepExternalStatusPageMonitorUtil, } from "./MonitorStepExternalStatusPageMonitor";
import { MonitorStepKubernetesMonitorUtil, } from "./MonitorStepKubernetesMonitor";
import { MonitorStepDockerMonitorUtil, } from "./MonitorStepDockerMonitor";
import Zod from "../../Utils/Schema/Zod";
/*
 * Caps and defaults for per-step request timeout and retry settings.
 * Users may lower these via the UI; values higher than the cap are clamped.
 */
export const MAX_MONITOR_REQUEST_TIMEOUT_IN_MS = 60000; // 60 seconds
export const DEFAULT_MONITOR_REQUEST_TIMEOUT_IN_MS = 60000;
export const MAX_MONITOR_RETRY_COUNT = 3;
export const DEFAULT_MONITOR_RETRY_COUNT = 3;
export const clampMonitorRequestTimeoutInMs = (value) => {
    if (!value || value <= 0) {
        return DEFAULT_MONITOR_REQUEST_TIMEOUT_IN_MS;
    }
    if (value > MAX_MONITOR_REQUEST_TIMEOUT_IN_MS) {
        return MAX_MONITOR_REQUEST_TIMEOUT_IN_MS;
    }
    return value;
};
export const clampMonitorRetryCount = (value) => {
    if (value === undefined || value === null || isNaN(value) || value < 0) {
        return DEFAULT_MONITOR_RETRY_COUNT;
    }
    if (value > MAX_MONITOR_RETRY_COUNT) {
        return MAX_MONITOR_RETRY_COUNT;
    }
    return value;
};
export default class MonitorStep extends DatabaseProperty {
    constructor() {
        super();
        this.data = undefined;
        this.data = {
            id: ObjectID.generate().toString(),
            monitorDestination: undefined,
            doNotFollowRedirects: undefined,
            allowSelfSignedCertificates: undefined,
            tlsClientCertificate: undefined,
            tlsClientKey: undefined,
            tlsClientKeyPassphrase: undefined,
            monitorDestinationPort: undefined,
            monitorCriteria: new MonitorCriteria(),
            requestType: HTTPMethod.GET,
            requestHeaders: undefined,
            requestBody: undefined,
            customCode: undefined,
            screenSizeTypes: undefined,
            browserTypes: undefined,
            retryCountOnError: undefined,
            requestTimeoutInMs: undefined,
            retryCount: undefined,
            logMonitor: undefined,
            traceMonitor: undefined,
            metricMonitor: undefined,
            exceptionMonitor: undefined,
            profileMonitor: undefined,
            snmpMonitor: undefined,
            dnsMonitor: undefined,
            domainMonitor: undefined,
            dnssecMonitor: undefined,
            externalStatusPageMonitor: undefined,
            kubernetesMonitor: undefined,
            dockerMonitor: undefined,
        };
    }
    static getDefaultMonitorStep(arg) {
        const monitorStep = new MonitorStep();
        monitorStep.data = {
            id: ObjectID.generate().toString(),
            monitorDestination: undefined,
            doNotFollowRedirects: undefined,
            allowSelfSignedCertificates: undefined,
            tlsClientCertificate: undefined,
            tlsClientKey: undefined,
            tlsClientKeyPassphrase: undefined,
            monitorDestinationPort: undefined,
            monitorCriteria: MonitorCriteria.getDefaultMonitorCriteria(arg),
            requestType: HTTPMethod.GET,
            requestHeaders: undefined,
            requestBody: undefined,
            customCode: undefined,
            screenSizeTypes: undefined,
            browserTypes: undefined,
            retryCountOnError: undefined,
            requestTimeoutInMs: undefined,
            retryCount: undefined,
            logMonitor: undefined,
            traceMonitor: undefined,
            metricMonitor: undefined,
            exceptionMonitor: undefined,
            profileMonitor: undefined,
            snmpMonitor: undefined,
            dnsMonitor: undefined,
            domainMonitor: undefined,
            dnssecMonitor: undefined,
            externalStatusPageMonitor: undefined,
            kubernetesMonitor: undefined,
            dockerMonitor: undefined,
        };
        return monitorStep;
    }
    get id() {
        var _a;
        return new ObjectID((_a = this.data) === null || _a === void 0 ? void 0 : _a.id);
    }
    set id(v) {
        this.data.id = v.toString();
    }
    setRequestType(requestType) {
        this.data.requestType = requestType;
        return this;
    }
    setRequestHeaders(requestHeaders) {
        this.data.requestHeaders = requestHeaders;
        return this;
    }
    static clone(monitorStep) {
        return MonitorStep.fromJSON(monitorStep.toJSON());
    }
    setRequestBody(requestBody) {
        this.data.requestBody = requestBody;
        return this;
    }
    setMonitorDestination(monitorDestination) {
        this.data.monitorDestination = monitorDestination;
        return this;
    }
    setDoNotFollowRedirects(doNotFollowRedirects) {
        this.data.doNotFollowRedirects = doNotFollowRedirects;
        return this;
    }
    setAllowSelfSignedCertificates(allowSelfSignedCertificates) {
        this.data.allowSelfSignedCertificates = allowSelfSignedCertificates;
        return this;
    }
    setTlsClientCertificate(tlsClientCertificate) {
        this.data.tlsClientCertificate = tlsClientCertificate || undefined;
        return this;
    }
    setTlsClientKey(tlsClientKey) {
        this.data.tlsClientKey = tlsClientKey || undefined;
        return this;
    }
    setTlsClientKeyPassphrase(tlsClientKeyPassphrase) {
        this.data.tlsClientKeyPassphrase = tlsClientKeyPassphrase || undefined;
        return this;
    }
    setPort(monitorDestinationPort) {
        this.data.monitorDestinationPort = monitorDestinationPort;
        return this;
    }
    setScreenSizeTypes(screenSizeTypes) {
        this.data.screenSizeTypes = screenSizeTypes;
        return this;
    }
    setBrowserTypes(browserTypes) {
        this.data.browserTypes = browserTypes;
        return this;
    }
    setRetryCountOnError(retryCountOnError) {
        this.data.retryCountOnError = retryCountOnError;
        return this;
    }
    setRequestTimeoutInMs(requestTimeoutInMs) {
        if (requestTimeoutInMs === undefined) {
            this.data.requestTimeoutInMs = undefined;
            return this;
        }
        this.data.requestTimeoutInMs =
            clampMonitorRequestTimeoutInMs(requestTimeoutInMs);
        return this;
    }
    setRetryCount(retryCount) {
        if (retryCount === undefined) {
            this.data.retryCount = undefined;
            return this;
        }
        this.data.retryCount = clampMonitorRetryCount(retryCount);
        return this;
    }
    setLogMonitor(logMonitor) {
        this.data.logMonitor = logMonitor;
        return this;
    }
    setMetricMonitor(metricMonitor) {
        this.data.metricMonitor = metricMonitor;
        return this;
    }
    setTraceMonitor(traceMonitor) {
        this.data.traceMonitor = traceMonitor;
        return this;
    }
    setExceptionMonitor(exceptionMonitor) {
        this.data.exceptionMonitor = exceptionMonitor;
        return this;
    }
    setProfileMonitor(profileMonitor) {
        this.data.profileMonitor = profileMonitor;
        return this;
    }
    setSnmpMonitor(snmpMonitor) {
        this.data.snmpMonitor = snmpMonitor;
        return this;
    }
    setDnsMonitor(dnsMonitor) {
        this.data.dnsMonitor = dnsMonitor;
        return this;
    }
    setDomainMonitor(domainMonitor) {
        this.data.domainMonitor = domainMonitor;
        return this;
    }
    setDnssecMonitor(dnssecMonitor) {
        this.data.dnssecMonitor = dnssecMonitor;
        return this;
    }
    setExternalStatusPageMonitor(externalStatusPageMonitor) {
        this.data.externalStatusPageMonitor = externalStatusPageMonitor;
        return this;
    }
    setKubernetesMonitor(kubernetesMonitor) {
        this.data.kubernetesMonitor = kubernetesMonitor;
        return this;
    }
    setDockerMonitor(dockerMonitor) {
        this.data.dockerMonitor = dockerMonitor;
        return this;
    }
    setCustomCode(customCode) {
        this.data.customCode = customCode;
        return this;
    }
    setMonitorCriteria(monitorCriteria) {
        this.data.monitorCriteria = monitorCriteria;
        return this;
    }
    static getNewMonitorStepAsJSON() {
        return {
            _type: ObjectType.MonitorStep,
            value: {
                id: ObjectID.generate().toString(),
                monitorDestination: undefined,
                doNotFollowRedirects: undefined,
                allowSelfSignedCertificates: undefined,
                tlsClientCertificate: undefined,
                tlsClientKey: undefined,
                tlsClientKeyPassphrase: undefined,
                monitorDestinationPort: undefined,
                monitorCriteria: MonitorCriteria.getNewMonitorCriteriaAsJSON(),
                requestType: HTTPMethod.GET,
                requestHeaders: undefined,
                requestBody: undefined,
                customCode: undefined,
                screenSizeTypes: undefined,
                browserTypes: undefined,
                retryCountOnError: undefined,
                requestTimeoutInMs: undefined,
                retryCount: undefined,
                logMonitor: undefined,
                exceptionMonitor: undefined,
                kubernetesMonitor: undefined,
                dockerMonitor: undefined,
            },
        };
    }
    static getValidationError(value, monitorType) {
        if (!value.data) {
            return "Monitor Step is required.";
        }
        // If the monitor type is incoming request, then the monitor destination is not required
        if (!value.data.monitorDestination &&
            (monitorType === MonitorType.Port ||
                monitorType === MonitorType.API ||
                monitorType === MonitorType.Ping ||
                monitorType === MonitorType.Website ||
                monitorType === MonitorType.IP ||
                monitorType === MonitorType.SSLCertificate)) {
            return "Monitor Destination is required.";
        }
        if (!value.data.customCode &&
            (monitorType === MonitorType.CustomJavaScriptCode ||
                monitorType === MonitorType.SyntheticMonitor)) {
            if (monitorType === MonitorType.CustomJavaScriptCode) {
                return "Custom Code is required";
            }
            return "Playwright code is required.";
        }
        if (!value.data.monitorCriteria) {
            return "Monitor Criteria is required";
        }
        if (MonitorCriteria.getValidationError(value.data.monitorCriteria, monitorType)) {
            return MonitorCriteria.getValidationError(value.data.monitorCriteria, monitorType);
        }
        if (!value.data.requestType && monitorType === MonitorType.API) {
            return "Request Type is required";
        }
        if (monitorType === MonitorType.API ||
            monitorType === MonitorType.Website) {
            const hasCert = Boolean(value.data.tlsClientCertificate &&
                value.data.tlsClientCertificate.trim());
            const hasKey = Boolean(value.data.tlsClientKey && value.data.tlsClientKey.trim());
            if (hasCert && !hasKey) {
                return "Client private key is required when a client certificate is provided";
            }
            if (hasKey && !hasCert) {
                return "Client certificate is required when a client private key is provided";
            }
        }
        if (monitorType === MonitorType.Port &&
            !value.data.monitorDestinationPort) {
            return "Port is required";
        }
        if (monitorType === MonitorType.SNMP) {
            if (!value.data.snmpMonitor) {
                return "SNMP configuration is required";
            }
            if (!value.data.snmpMonitor.hostname) {
                return "SNMP hostname is required";
            }
            if (!value.data.snmpMonitor.oids ||
                value.data.snmpMonitor.oids.length === 0) {
                return "At least one OID is required";
            }
        }
        if (monitorType === MonitorType.DNS) {
            if (!value.data.dnsMonitor) {
                return "DNS configuration is required";
            }
            if (!value.data.dnsMonitor.queryName) {
                return "DNS query name (domain) is required";
            }
        }
        if (monitorType === MonitorType.Domain) {
            if (!value.data.domainMonitor) {
                return "Domain configuration is required";
            }
            if (!value.data.domainMonitor.domainName) {
                return "Domain name is required";
            }
        }
        if (monitorType === MonitorType.DNSSEC) {
            if (!value.data.dnssecMonitor) {
                return "DNSSEC configuration is required";
            }
            if (!value.data.dnssecMonitor.domainName) {
                return "Domain name is required";
            }
            if (!value.data.dnssecMonitor.resolvers ||
                value.data.dnssecMonitor.resolvers.length === 0) {
                return "At least one resolver is required";
            }
        }
        if (monitorType === MonitorType.ExternalStatusPage) {
            if (!value.data.externalStatusPageMonitor) {
                return "External status page configuration is required";
            }
            if (!value.data.externalStatusPageMonitor.statusPageUrl) {
                return "Status page URL is required";
            }
        }
        if (monitorType === MonitorType.Kubernetes) {
            if (!value.data.kubernetesMonitor) {
                return "Kubernetes monitor configuration is required";
            }
            if (!value.data.kubernetesMonitor.clusterIdentifier) {
                return "Kubernetes cluster is required";
            }
        }
        if (monitorType === MonitorType.Docker) {
            if (!value.data.dockerMonitor) {
                return "Docker monitor configuration is required";
            }
            if (!value.data.dockerMonitor.hostIdentifier) {
                return "Docker host is required";
            }
        }
        return null;
    }
    toJSON() {
        var _a, _b, _c, _d;
        if (this.data) {
            return JSONFunctions.serialize({
                _type: ObjectType.MonitorStep,
                value: {
                    id: this.data.id,
                    monitorDestination: ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.monitorDestination) === null || _b === void 0 ? void 0 : _b.toJSON()) || undefined,
                    doNotFollowRedirects: this.data.doNotFollowRedirects || undefined,
                    allowSelfSignedCertificates: this.data.allowSelfSignedCertificates || undefined,
                    tlsClientCertificate: this.data.tlsClientCertificate || undefined,
                    tlsClientKey: this.data.tlsClientKey || undefined,
                    tlsClientKeyPassphrase: this.data.tlsClientKeyPassphrase || undefined,
                    monitorDestinationPort: ((_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c.monitorDestinationPort) === null || _d === void 0 ? void 0 : _d.toJSON()) || undefined,
                    monitorCriteria: this.data.monitorCriteria.toJSON(),
                    requestType: this.data.requestType,
                    requestHeaders: this.data.requestHeaders || undefined,
                    requestBody: this.data.requestBody || undefined,
                    customCode: this.data.customCode || undefined,
                    screenSizeTypes: this.data.screenSizeTypes || undefined,
                    browserTypes: this.data.browserTypes || undefined,
                    retryCountOnError: this.data.retryCountOnError || undefined,
                    requestTimeoutInMs: this.data.requestTimeoutInMs || undefined,
                    retryCount: this.data.retryCount === undefined
                        ? undefined
                        : this.data.retryCount,
                    logMonitor: this.data.logMonitor
                        ? MonitorStepLogMonitorUtil.toJSON(this.data.logMonitor || MonitorStepLogMonitorUtil.getDefault())
                        : undefined,
                    metricMonitor: this.data.metricMonitor
                        ? MonitorStepMetricMonitorUtil.toJSON(this.data.metricMonitor)
                        : undefined,
                    traceMonitor: this.data.traceMonitor
                        ? MonitorStepTraceMonitorUtil.toJSON(this.data.traceMonitor ||
                            MonitorStepTraceMonitorUtil.getDefault())
                        : undefined,
                    exceptionMonitor: this.data.exceptionMonitor
                        ? MonitorStepExceptionMonitorUtil.toJSON(this.data.exceptionMonitor ||
                            MonitorStepExceptionMonitorUtil.getDefault())
                        : undefined,
                    profileMonitor: this.data.profileMonitor
                        ? MonitorStepProfileMonitorUtil.toJSON(this.data.profileMonitor ||
                            MonitorStepProfileMonitorUtil.getDefault())
                        : undefined,
                    snmpMonitor: this.data.snmpMonitor
                        ? MonitorStepSnmpMonitorUtil.toJSON(this.data.snmpMonitor)
                        : undefined,
                    dnsMonitor: this.data.dnsMonitor
                        ? MonitorStepDnsMonitorUtil.toJSON(this.data.dnsMonitor)
                        : undefined,
                    domainMonitor: this.data.domainMonitor
                        ? MonitorStepDomainMonitorUtil.toJSON(this.data.domainMonitor)
                        : undefined,
                    dnssecMonitor: this.data.dnssecMonitor
                        ? MonitorStepDnssecMonitorUtil.toJSON(this.data.dnssecMonitor)
                        : undefined,
                    externalStatusPageMonitor: this.data.externalStatusPageMonitor
                        ? MonitorStepExternalStatusPageMonitorUtil.toJSON(this.data.externalStatusPageMonitor)
                        : undefined,
                    kubernetesMonitor: this.data.kubernetesMonitor
                        ? MonitorStepKubernetesMonitorUtil.toJSON(this.data.kubernetesMonitor)
                        : undefined,
                    dockerMonitor: this.data.dockerMonitor
                        ? MonitorStepDockerMonitorUtil.toJSON(this.data.dockerMonitor)
                        : undefined,
                },
            });
        }
        return MonitorStep.getNewMonitorStepAsJSON();
    }
    static fromJSON(json) {
        if (json instanceof MonitorStep) {
            return json;
        }
        if (!json || json["_type"] !== "MonitorStep") {
            throw new BadDataException("Invalid monitor step");
        }
        if (!json["value"]) {
            throw new BadDataException("Invalid monitor step");
        }
        json = json["value"];
        let monitorDestination = undefined;
        if (json &&
            json["monitorDestination"] &&
            json["monitorDestination"]["_type"] === ObjectType.URL) {
            monitorDestination = URL.fromJSON(json["monitorDestination"]);
        }
        if (json &&
            json["monitorDestination"] &&
            json["monitorDestination"]["_type"] ===
                ObjectType.Hostname) {
            monitorDestination = Hostname.fromJSON(json["monitorDestination"]);
        }
        if (json &&
            json["monitorDestination"] &&
            json["monitorDestination"]["_type"] === ObjectType.IP) {
            monitorDestination = IP.fromJSON(json["monitorDestination"]);
        }
        const monitorDestinationPort = json["monitorDestinationPort"]
            ? Port.fromJSON(json["monitorDestinationPort"])
            : undefined;
        if (!json["monitorCriteria"]) {
            throw new BadDataException("Invalid monitor criteria");
        }
        if (MonitorCriteria.isValid(json["monitorCriteria"]) === false) {
            throw new BadDataException("Invalid monitor criteria");
        }
        const monitorStep = new MonitorStep();
        monitorStep.data = JSONFunctions.deserialize({
            id: json["id"],
            monitorDestination: monitorDestination || undefined,
            doNotFollowRedirects: json["doNotFollowRedirects"] || undefined,
            allowSelfSignedCertificates: json["allowSelfSignedCertificates"] || undefined,
            tlsClientCertificate: json["tlsClientCertificate"] || undefined,
            tlsClientKey: json["tlsClientKey"] || undefined,
            tlsClientKeyPassphrase: json["tlsClientKeyPassphrase"] || undefined,
            monitorDestinationPort: monitorDestinationPort || undefined,
            monitorCriteria: MonitorCriteria.fromJSON(json["monitorCriteria"]),
            requestType: json["requestType"] || HTTPMethod.GET,
            requestHeaders: json["requestHeaders"] || undefined,
            requestBody: json["requestBody"] || undefined,
            customCode: json["customCode"] || undefined,
            screenSizeTypes: json["screenSizeTypes"] || undefined,
            browserTypes: json["browserTypes"] || undefined,
            retryCountOnError: json["retryCountOnError"] || undefined,
            requestTimeoutInMs: json["requestTimeoutInMs"] || undefined,
            retryCount: json["retryCount"] === undefined || json["retryCount"] === null
                ? undefined
                : json["retryCount"],
            logMonitor: json["logMonitor"]
                ? json["logMonitor"]
                : undefined,
            metricMonitor: json["metricMonitor"]
                ? json["metricMonitor"]
                : undefined,
            traceMonitor: json["traceMonitor"]
                ? json["traceMonitor"]
                : undefined,
            exceptionMonitor: json["exceptionMonitor"]
                ? json["exceptionMonitor"]
                : undefined,
            profileMonitor: json["profileMonitor"]
                ? json["profileMonitor"]
                : undefined,
            snmpMonitor: json["snmpMonitor"]
                ? json["snmpMonitor"]
                : undefined,
            dnsMonitor: json["dnsMonitor"]
                ? json["dnsMonitor"]
                : undefined,
            domainMonitor: json["domainMonitor"]
                ? json["domainMonitor"]
                : undefined,
            dnssecMonitor: json["dnssecMonitor"]
                ? json["dnssecMonitor"]
                : undefined,
            externalStatusPageMonitor: json["externalStatusPageMonitor"]
                ? json["externalStatusPageMonitor"]
                : undefined,
            kubernetesMonitor: json["kubernetesMonitor"]
                ? json["kubernetesMonitor"]
                : undefined,
            dockerMonitor: json["dockerMonitor"]
                ? json["dockerMonitor"]
                : undefined,
        });
        return monitorStep;
    }
    static getSchema() {
        return Zod.object({
            _type: Zod.literal(ObjectType.MonitorStep),
            value: Zod.object({
                id: Zod.string(),
                monitorDestination: Zod.any().optional(),
                monitorCriteria: Zod.any(),
                requestType: Zod.any(),
                requestHeaders: Zod.any().optional(),
                requestBody: Zod.string().optional(),
                doNotFollowRedirects: Zod.boolean().optional(),
                allowSelfSignedCertificates: Zod.boolean().optional(),
                tlsClientCertificate: Zod.string().optional(),
                tlsClientKey: Zod.string().optional(),
                tlsClientKeyPassphrase: Zod.string().optional(),
                monitorDestinationPort: Zod.any().optional(),
                customCode: Zod.string().optional(),
                screenSizeTypes: Zod.any().optional(),
                browserTypes: Zod.any().optional(),
                retryCountOnError: Zod.number().optional(),
                requestTimeoutInMs: Zod.number().optional(),
                retryCount: Zod.number().optional(),
                logMonitor: Zod.any().optional(),
                traceMonitor: Zod.any().optional(),
                metricMonitor: Zod.any().optional(),
                profileMonitor: Zod.any().optional(),
                snmpMonitor: Zod.any().optional(),
                dnsMonitor: Zod.any().optional(),
                domainMonitor: Zod.any().optional(),
                dnssecMonitor: Zod.any().optional(),
                externalStatusPageMonitor: Zod.any().optional(),
                kubernetesMonitor: Zod.any().optional(),
                dockerMonitor: Zod.any().optional(),
            }).openapi({
                type: "object",
                example: {
                    id: "stepId",
                    monitorDestination: undefined,
                    monitorCriteria: {},
                    requestType: "GET",
                },
            }),
        }).openapi({
            type: "object",
            description: "MonitorStep object",
            example: {
                _type: ObjectType.MonitorStep,
                value: {
                    id: "stepId",
                    monitorDestination: undefined,
                    monitorCriteria: {},
                    requestType: "GET",
                },
            },
        });
    }
    isValid() {
        return true;
    }
    static toDatabase(value) {
        if (value && value instanceof MonitorStep) {
            return value.toJSON();
        }
        else if (value) {
            return JSONFunctions.serialize(value);
        }
        return null;
    }
    static fromDatabase(value) {
        if (value) {
            return MonitorStep.fromJSON(value);
        }
        return null;
    }
    toString() {
        return JSON.stringify(this.toJSON());
    }
}
//# sourceMappingURL=MonitorStep.js.map