Source: DCC/DCC.js

const EventEmitter = require('events')
const NetUtil = require("./NetUtil")
const net = require("net")
const fs = require("fs")
const path = require("path")

/**
 * @class
 * New DCC Client
 */
class DCC extends EventEmitter{

	/**
	 * @param {Client} client - The IRC Client
	 * @param {Object} options - Options for the DCC client
	 * @param {Array.<Number>} options.ports - The ports to use
	 * @param {String} options.localAddress - The clients local IP
	 */
	constructor(client, options={}){

		super()

		this._client = client

		this._options = options

		this._options.ports = options.ports||0
		this._options.localAddress = options.localAddress||NetUtil.getIP()[0]
	}

	/**
	 * Gets the clients public IP
	 * @function
	 * @private
	 * @returns {Promise.<String>}
	 */
	getIP(){
		return NetUtil.getPublicIP()
	}

	/**
	 * Gets a port and IP to use
	 * @function
	 * @private
	 * @returns {Promise.<Object>}
	 */
	getPortIP(){
		return new Promise((resolve, reject) => {
			let toReturn = {}

			this.getIP().then(host => {
				toReturn.host = host
				return this.getPort()
			}).then(port => {
				toReturn.port = port
				toReturn.long = NetUtil.toLong(toReturn.host)
				resolve(toReturn)
			})
		})
	}

	/**
	 * Gets a port to use
	 * @function
	 * @private
	 * @returns {Promise.<Object>}
	 */
	getPort(){
		return new Promise((resolve, reject) => {
			if(this._options.ports === 0){
				resolve(0)
			}else{
				return NetUtil.getUnusedPort({
					min: this._options.ports[0],
					max: this._options.ports[1],
					localAddress: this._options.localAddress
				})
			}
		})
	}

	/**
	 * Sends a CTCP query to a user
	 * @function
	 * @param {String} to - The user to send to
	 * @param {String} message - The message to send
	 * @author Mackan
	 */
	sendCTCPQuery(to, message){
		this._client._connection.write(`PRIVMSG ${to} :\u0001${message}\u0001\n`)
	}

	/**
	 * Sends a file to a user
	 * @function
	 * @param {String} to - The nick of the user to send the file to
	 * @param {String} filename - The filename to send
	 * @param {String} filepath - The path of the file
	 * @returns {Promise}
	 */
	sendFile(to, filename, filepath){
		return new Promise((resolve, reject) => {

			filepath = path.resolve(filepath)

			fs.stat(filepath, (err, filestat) => {

				if(err){
					reject(err)
					return
				}

				filename = filename.replace(/ /g, "_")

				this.getPortIP().then(details => {
					let start = 0

					function resumeCallback(from, args){
						if(args.filename === filename){
							start = args.position

							this.sendCTCPQuery(from, `DCC ACCEPT ${filename} ${args.port} ${args.position}`)
						}
					}

					let server = net.createServer()
					server.listen(details.port, this._options.localAddress, null, () => {
						let address = server.address()

						this.sendCTCPQuery(to, `DCC SEND ${filename} ${details.long} ${address.port} ${filestat.size}`)

						this.once('dcc-resume', resumeCallback)
					})

					server.on("connection", connection => {
						const cn = connection
						server.close()

						let readStream = fs.createReadStream(filepath, {
							start: start
						})

						readStream.pipe(cn, {end: false})

						readStream.on('end', () => {
							resolve()
						})
					})
				})
			})
		})
	}
}

module.exports = DCC