"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lookup = lookup;
exports.setDnsServers = setDnsServers;
const packetGenerator_ts_1 = require("./packetGenerator.js");
const packetDecoder_ts_1 = require("./packetDecoder.js");
const net = require("node:net");
const types_ts_1 = require("./types.js");
const node_dns_1 = require("node:dns");
/**
 * Looks up the server.
 * @example
 * Here's a simple example:
 * ```
 * let result = await mc.lookup({
 *    host: "mc.hypixel.net"
 *    port: 25565,
 *    timeout: 10000,
 *    ping: true,
 *    protocolVersion: 769
 *    throwOnParseError: false,
 *    SRVLookup: true,
 *    JSONParse: true
 * })
 * ```
 */
async function lookup(options) {
    return new Promise(async (resolve, reject) => {
        let hostname = options.host || options.hostname;
        if (!hostname)
            return reject(new Error("No hostname was provided!"));
        let port = options.port != null ? options.port : 25565;
        if (port < 0 || port > 65535)
            return reject(new Error("Port number must be between 0 and 25565!"));
        let timeout = options.timeout != null ? options.timeout : 10000;
        let ping = options.ping != null ? options.ping : true;
        let protocolVersion = options.protocolVersion != null ? options.protocolVersion : 769;
        let throwOnParseError = options.throwOnParseError != null ? options.throwOnParseError : true;
        let SRVLookup = options.SRVLookup != null ? options.SRVLookup : true;
        let JSONParse = options.JSONParse != null ? options.JSONParse : true;
        if (SRVLookup)
            ({ hostname, port } = await processSRV(hostname, port));
        // Default port of 25565, default timeout of 10 seconds.
        // Ping is sent by default. 
        let portal = net.createConnection({ port: port, host: hostname, lookup: customLookup }, async () => {
            // Send first the handshake, and then the status request to the server.
            let handshake = await packetGenerator_ts_1.default.craftHandshake(hostname, port, protocolVersion);
            let statusRequest = await packetGenerator_ts_1.default.craftEmptyPacket(0);
            portal.write(handshake);
            portal.write(statusRequest);
        });
        let packet = new types_ts_1.Packet();
        portal.on("data", async (_chunk) => {
            /*
                Pass every new chunk of data sent from the server into the pipeline,
                and also pass the packet object in, which holds the current state of the request.
                The pipeline returns the packet object, with changes made from processing the data chunk.
            */
            let chunk = new Uint8Array(_chunk);
            packet = await packetDecoder_ts_1.default.packetPipeline(chunk, packet);
            if (packet.Error) {
                clearTimeout(timeoutFunc);
                return reject(packet.Error);
            }
            if (packet.status.pingBaked || (packet.status.handshakeBaked && !ping)) {
                let serverStatus;
                try {
                    serverStatus = new types_ts_1.ServerStatus(packet.crafted.data, packet.crafted.latency, throwOnParseError, JSONParse);
                }
                catch (error) {
                    clearTimeout(timeoutFunc);
                    portal.destroy();
                    return reject(error);
                }
                clearTimeout(timeoutFunc);
                portal.destroy();
                return resolve(serverStatus);
            }
            /*
                If the handshake and status request have been successfully completed,
                generate the ping packet, record the time it is sent, and then send it.
            */
            if (packet.status.handshakeBaked && !packet.status.pingSent) {
                let pingRequest = await packetGenerator_ts_1.default.craftPingPacket();
                packet.status.pingSentTime = Date.now();
                packet.status.pingSent = true;
                await portal.write(pingRequest);
            }
        });
        portal.once("error", (netError) => {
            clearTimeout(timeoutFunc);
            reject(netError);
        });
        let timeoutFunc = setTimeout(() => {
            portal.destroy();
            reject(new Error("Timed out."));
        }, timeout);
    });
}
/**
 * Used for changing the default DNS servers.
 * @example
 * ```
    // Recommended servers
    mc.setDnsServers(["9.9.9.9", "1.1.1.1", "8.8.8.8"])
    // (Quad9, Cloudflare, Google)
    // Note: Cloudflare is typically the fastest for DNS queries.
```
 */
async function setDnsServers(serverArray) {
    await node_dns_1.promises.setServers(serverArray);
    return true;
}
async function customLookup(hostname, options, callback) {
    let result = await node_dns_1.promises.lookup(hostname, options).catch((e) => {
        callback(e);
    });
    if (!result)
        return;
    if (options?.all)
        callback(null, result);
    else
        callback(null, result.address, result.family);
}
async function processSRV(hostname, port) {
    /*
     *  Tries to get a SRV record from the provided hostname, unless disabled with the SRVLookup flag.
     *  The hostname can't be a local one, the port has to always be 25565, and the hostname cannot be an IP.
    */
    if (port != 25565 || net.isIP(hostname) != 0)
        return { hostname, port };
    let result = await node_dns_1.promises.resolveSrv("_minecraft._tcp." + hostname).catch(() => { });
    if (!result || result.length == 0 || !result[0].name || !result[0].port)
        return { hostname, port };
    return { hostname: result[0].name, port: result[0].port };
}
/**
 * The default export, containing setDnsServers and lookup
 */
exports.default = { setDnsServers, lookup };
//# sourceMappingURL=index.js.map