const fs	  = require('fs'); // FS Library for FileStreams
const Random  = require('randoma'); // PseudoRandom Seed-Based Generator
const Image   = require('./image');

class LSB {

	constructor (seed, bitrate = 1) {
		this.seed = seed;
		this.bitrate = bitrate;
	}

	/* 
	* + async LSB.hide(Buffer Data, Buffer buf)
	*	Hides buf into Data (THIS FUNCTION DOES NOT CREATE NEW BUFFER, INSTEAD, IT MODIFIES Data ARGUMENT!!)
	*/
	hide (Data, buf, start = 0) {
		let random = new Random({ seed: this.seed });

		let bitc = 0;
		let bc = 0;

		let x = 0;

		start = start * 8;

		for(let i=0;i < start;i++) {
			x += this.next(random);
		}

		for(let i=x, l = Data.length;i<l;i+=this.next(random)) {
			let d = Data[i];

			if(d === undefined) break;

			let bit = ((buf[bc] >> bitc) & 1);
			let mbit = d & 1;

			if(mbit !== bit) {
				if(d === 0xFF) Data[i]--;
				else Data[i]++;
			}

			if(++bitc === 8) {
				bc++;
				bitc = 0;

				if(bc >= buf.length) break;
			}
		}

		return bc;
	}

	/* 
	* + async LSB.recover(Buffer Data, int length)
	*	Returns certain number of BYTES of the container Buffer
	*/
	recover (Data, length, start = 0) {
		let random = new Random({ seed: this.seed });
		let buf = [];

		let bitc = 0;
		let bc = 0;

		let l = length * 8;
		start = start * 8;

		let x = 0;

		for(let i=0;i < start;i++) {
			x += this.next(random);
		}

		for(let i=0;i<l;i++) {
			if(Data[x] === undefined) break;
			if(buf[bc] === undefined) buf[bc] = 0;

			buf[bc] |= (Data[x] & 1) << bitc;

			if(++bitc === 8) {
				bc++;
				bitc = 0;
			}

			x += this.next(random);
		}

		return new Buffer(buf);
	}

	next (r) {
		if(this.bitrate === 1) return 1;

		return r.integerInRange(1, this.bitrate *2 -1);
	}

	sizeOf (buf) {
		let l = buf.length;
		let random = new Random({ seed: this.seed });

		let n = 0;
		let c = 0;

		while(n < l) {
			n += random.integerInRange(1, this.bitrate * 2 -1);
			c++;
		}

		return Math.floor(c/8);
	}

	async getFileSize (file) {
		let buf = (await Image.read(file)).data;

		return this.sizeOf(buf);
	}

	async read (path) {
		let img = await Image.read(path);
		let data = img.data;

		let length  = this.recover(data, 4);
			length  = length[0] | (length[1] << 8) | (length[2] << 16) | (length[1] << 24);

		let content = this.recover(data, length, 4);

		return content;
	}

	async write (path, content) {
		if(typeof content === "string") content = new Buffer(content);
		
		let img = await Image.read(path);
		let data = img.data;
		let length = content.length;
			length = new Buffer([length & 0xFF, (length >> 8) & 0xFF, (length >> 16) & 0xFF, (length >> 24) & 0xFF]);

		this.hide(data, length);
		this.hide(data, content, 4);

		await Image.write(path, img);
	}
}

module.exports = LSB;