/**
 * Copyright (c) 2015, Jozef Stefan Institute, Quintelligence d.o.o. and contributors
 * All rights reserved.
 * 
 * This source code is licensed under the FreeBSD license found in the
 * LICENSE file in the root directory of this source tree.
 */
/**
* File-system module.
* @module fs
* @example
* // import module
* var fs = require('qminer').fs;
* // open file in write mode
* var fout = fs.openWrite('file.txt');
* // write sync and close
* fout.writeLine('example text');
* fout.close();
* // open file in read mode
* var fin = fs.openRead('file.txt');
* // read a line
* var str = fin.readLine();
*/
/**
	* Open file in read mode and return file input stream.
	* @param {string} fileName - File name.
	* @returns {module:fs.FIn} Input stream.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('read_text.txt');
    * // write to file
    * fout.write('This is awesome!');
    * // close the stream 
    * fout.close();
    * // open file to read
    * var fin = fs.openRead('read_text.txt');
	*/
 exports.openRead = function(fileName) { return Object.create(require('qminer').fs.FIn.prototype); }
/**
	* Open file in write mode and return file output stream.
	* @param {string} fileName - File name.
	* @returns {module:fs.FOut} Output stream.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('write_text.txt');
    * // close the stream 
    * fout.close();
	*/
 exports.openWrite = function(fileName) { return Object.create(require('qminer').fs.FOut.prototype); }
/**
	* Open file in append mode and return file output stream.
	* @param {string} fileName - File name.
	* @returns {module:fs.FOut} Output stream.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('append_text.txt');
    * // close the stream
    * fout.close();
    * // open file in append mode
    * var foutAppend = fs.openAppend('append_text.txt');
    * // close the stream
    * foutAppend.close();
	*/
 exports.openAppend = function(fileName) { return Object.create(require('qminer').fs.FOut.prototype); }	
/**
	* Checks if the file exists.
	* @param {string} fileName - File name.
	* @returns {boolean} True if file exists.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // check if a file exists
    * fs.exists('text.txt');
	*/
 exports.exists = function(fileName) { return false; }	
/**
	* Copies a file.
	* @param {string} source - Source file name.
	* @param {string} dest - Destination file name.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('text.txt');
    * // close the stream
    * fout.close();
    * // copy the file
    * var destination = fs.copy('text.txt', 'copy.txt');
	*/
 exports.copy = function(source, dest) { return ""; }	
/**
	* Moves a file.
	* @param {string} source - Source file name.
	* @param {string} dest - Destination file name.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('text.txt');
    * // close the stream
    * fout.close();
    * // move the file
    * var destination = fs.move('text.txt', 'move.txt');
	*/
 exports.move = function(source, dest) { return ""; }
/**
	* Deletes a file.
	* @param {string} fileName - File name.
	* @returns {boolean} True if delete succeeded.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('delete.txt');
    * // close the stream
    * fout.close();
    * // delete the file
    * var destination = fs.del('delete.txt');
	*/
 exports.del = function(fileName) { return false; }	
/**
	* Renames a file.
	* @param {string} source - Source file name.
	* @param {string} dest - Destination file name.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('text.txt');
    * // close the stream
    * fout.close();
    * // rename the file
    * if (fs.exists('rename.txt')) {
    *    fs.del('rename.txt');
    * }
    * var destination = fs.rename('text.txt', 'rename.txt');
	*/
 exports.rename = function(source, dest) { return ""; }
/**
	* @typedef {Object} FileInfo 	
    * Information about the file.
	* @property  {string} createTime - Create time.
	* @property  {string} lastAccessTime - Last access time.
	* @property  {string} lastWriteTime - Last write time.
	* @property  {number} size - File size in bytes.	
	*/
/**
	* Returns the file info.
	* @param {string} fileName - File name.
	* @returns {module:fs~FileInfo} File info object.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // open file to write
    * var fout = fs.openWrite('text.txt');
    * // close the stream
    * fout.close();
    * // get the file info
    * var info = fs.fileInfo('text.txt');
	*/
 exports.fileInfo = function(fileName) { return { createTime : "",  lastAccessTime: "", lastWriteTime: "", size: 0 }}	
/**
	* Creates a folder.
	* @param {string} dirName - Folder name.
	* @returns {boolean} True if succeeded.
    * @example
    // import fs module
    * var fs = require('qminer').fs;
    * // create a folder
    * var makeFolder = fs.mkdir('folder');
	*/
 exports.mkdir = function(dirName) { return false; }	
/**
	* Removes a folder.
	* @param {string} dirName - Folder name.
	* @returns {boolean} True if succeeded.
    * @example
    // import fs module
    * var fs = require('qminer').fs;
    * // create a folder
    * var makeFolder = fs.mkdir('folder');
    * // delete folder
    * if (makeFolder) {
    *    fs.rmdir('folder');
    * }
	*/
 exports.rmdir = function(dirName) { return false; }
/**
	* Returns a list fo files in the folder.
	* @param {string} dirName - Folder name.
	* @param {string} [fileExtension] - Results are filtered by file extension.
	* @param {boolean} [recursive=false] - Recursively searches for file names if true.
	* @returns {Array.<string>} Array of file names.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // get the names of all files
    * var fileNames = fs.listFile('./');
	*/
 exports.listFile = function(dirName, fileExtension, recursive) { return ['']; }
/**
     * Reads a buffer line by line and calls a callback for each line.
     * @param {String | module:fs.FIn | Buffer} buffer - Name of the file, input stream of a Node.js buffer.
     * @param {function} onLine - A callback that gets called on each line (for example: `function (line) {}`).
     * @param {function} onEnd - A callback that gets returned after all the lines have been read.
     * @param {function} onError - A callback that gets called if an error occurs.
     * @example
     * // import fs module
     * var fs = require('qminer').fs;
     * // create a file and write some lines
     * var fout = fs.openWrite('poem.txt');
     * fout.write('I dig,\nYou dig,\nHe digs,\nShe digs,\nWe dig,\nThey dig.\n It\'s not a beautiful poem, but it\'s deep.');
     * fout.close();
     * // open the file in read mode
     * var fin = fs.openRead('poem.txt');
     * // read the file line by line and call functions
     * //var numberOfLines = 0;
     * //function onLine(line) {
     * //    console.log(line);
     * //    numberOfLines += 1;
     * //}
     * //function onEnd(line) {
     * //    console.log("Number of lines", numberOfLines);
     * //}
     * //function onError(err) {
     * //    console.log(err);
     * //}
     * //fs.readLines(fin, onLine, onEnd, onError);
     */
 exports.readLines = function (buffer, onLine, onEnd, onError) {}
/**
	* Input file stream.
	* @classdesc Used for reading files.
	* @class
	* @param {string} fileName - File name.
	* @example
	* // import module
	* var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
	* // open file in read mode
	* var fin = new fs.FIn('file.txt');
	* // read a line
	* var str = fin.readLine();
	*/
 exports.FIn = function(fileName) { return Object.create(require('qminer').fs.FIn.prototype); }	
/**
	* Peeks a character.
	* @returns {string} Character string.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // peek the next character
    * var char = fin.peekCh();
	*/
 exports.FIn.prototype.peekCh= function() { return ''; }
/**
	* Reads a character.
	* @returns {string} Character string.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // get the next character
    * var char = fin.getCh();
	*/
 exports.FIn.prototype.getCh= function() { return ''; }
/**
	* Reads a line.
	* @returns {string} Line string.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // get/read a new line
    * var line = fin.readLine();
	*/
 exports.FIn.prototype.readLine = function() { return ''; }
/**
	* Reads a string that was serialized using `fs.FOut.writeBinary`.
	* @returns {string} String.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // read a string that was serialized using fs.FOut.writeBinary
	*/
 exports.FIn.prototype.readString = function() { return ''; }
/**
	* True if end of file is detected. Otherwise, false. Type `boolean`.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // check if it's end of the file
    * var eof = fin.eof;
	*/
 exports.FIn.prototype.eof = false;
/**
	* Length of input stream. Type `number`.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // get the length of the document
    * var len = fin.length;
	*/
 exports.FIn.prototype.length = 0;
/**
	* Reads the whole stream.
	* @returns {string} Content of the file.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // get/read a the whole string
    * var all = fin.readAll();
	*/
 exports.FIn.prototype.readAll = function() { return ''; }
/**
	* Closes the input stream.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // close the stream
    * fin.close();
	*/
 exports.FIn.prototype.close = function() { }
/**
	* Checks if the input stream is closed.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = fs.openWrite('file.txt');
    * // write sync and close
    * fout.writeLine('example text');
    * fout.close();
    * // open file in read mode
    * var fin = new fs.FIn('file.txt');
    * // check if the stream is closed
    * var check = fin.isClosed();
	*/
 exports.FIn.prototype.isClosed = function() { return false; }
/**
	* Output file stream.
	* @classdesc Used for writing files.
	* @class
	* @param {String} fileName - File name.
	* @param {boolean} [append=false] - Append flag.
	* @example
	* // import module
	* var fs = require('qminer').fs;
	* // open file in write mode
	* var fout = new fs.FOut('file.txt');
	* // write a line
	* fout.writeLine('example text');
	* // close
	* fout.close();
	*/
 exports.FOut = function(fileName, append) {}	
/**
	* Writes a string or number or a JSON object in human readable form.
	* @param {(String | Number | Object)} arg - Argument to write.
	* @returns {module:fs.FOut} Self.
    * @example
	* // import module
	* var fs = require('qminer').fs;
	* // open file in write mode
	* var fout = new fs.FOut('file.txt');
	* // write a string
	* fout.write('example text');
    * // close
    * fout.close();
	*/
 exports.FOut.prototype.write = function(arg) { return this; }
/**
	* Writes a string or number or a JSON object in binary form.
	* @param {(String | Number | Object)} str - Argument to write.
	* @returns {module:fs.FOut} Self.
    * @example
    * // import fs module
    * var fs = require('qminer').fs;
    * // save a string in binary form
	*/
 exports.FOut.prototype.writeBinary = function(arg) { return this; }
/**
	* Writes a string and adds a new line.
	* @param {String} str - String to write.
	* @returns {module:fs.FOut} Self.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = new fs.FOut('file.txt');
    * // write a line
    * fout.writeLine('example text');
    * // close
    * fout.close();
	*/
 exports.FOut.prototype.writeLine = function(str) { return this; }
/**
	* Flushes the output stream.
	* @returns {module:fs.FOut} Self.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = new fs.FOut('file.txt');
    * // write a line
    * fout.writeLine('example text');
    * // flush the stream
    * fout.flush();
    * // close
    * fout.close();
	*/
 exports.FOut.prototype.flush = function() { return this; }
/**
	* Closes the output stream.
    * @example
    * // import module
    * var fs = require('qminer').fs;
    * // open file in write mode
    * var fout = new fs.FOut('file.txt');
    * // write a line
    * fout.writeLine('example text');
    * // close
    * fout.close();
	*/
 exports.FOut.prototype.close = function() {}


    function processCsvLine(opts) {
    	if (opts.delimiter == null) opts.delimiter = ',';
    	if (opts.onLine == null) throw 'Line callback missing!';
    	if (opts.onEnd == null) opts.onEnd = function () {}
    	if (opts.lineLimit == null) opts.lineLimit = Infinity;
    	if (opts.skipLines == null) opts.skipLines = 0;
    	if (opts.quote == null) opts.quote = '"';
    	
    	var strDelimiter = opts.delimiter;
    	
    	var lineCb = opts.onLine;
    	
    	// Create a regular expression to parse the CSV values.
        var objPattern = new RegExp((
                // Delimiters.
                "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
                // Quoted fields.
                "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
                // Standard fields.
                "([^\"\\" + strDelimiter + "\\r\\n]*))"
            ), "gi");
        
        var quoteRegex = new RegExp(opts.quote, 'g');
        
        var i = 0;
    	var line = '';
    	
    	return function (batch) {
    		line += batch;
    		
    		// if the number of delimiters is odd => the line hasn't ended, but a string field contained a line ending
    		if ((line.match(quoteRegex) || []).length % 2 == 1) {
    			return true;
    		}
			
			try {    				
				var arrData = [ ];
	            // Create an array to hold our individual pattern matching groups.
	            var arrMatches = null;
	            // Keep looping over the regular expression matches until we can no longer find a match.
	            while (arrMatches = objPattern.exec(line)){
	                // Let's check to see which kind of value we captured (quoted or unquoted).
	                var strMatchedValue = arrMatches[2] ?
	                    // We found a quoted value. When we capture this value, unescape any double quotes.
	                    arrMatches[2].replace(new RegExp( "\"\"", "g" ), "\"") :
	                    // We found a non-quoted value.
	                    arrMatches[3];
	                // Now that we have our value string, let's add it to the data array.
	                arrData.push(strMatchedValue);
	            }
	            
	            i++;
	            line = '';
	            
	            // Return the parsed data.
	            if (i > opts.skipLines) {
	            	lineCb(arrData);
	            }
	            
	            return ++i < opts.lineLimit;
			} catch (e) {
				opts.onEnd(e);
				return false;	// stop the loop if an error occurs
			}
    	}
    }
    
    /**
     * Reads a buffer, containing a CSV file, line by line and calls a callback for each line.
     * The callback function accepts an array with the values of the current line.
     * @param {Buffer} buffer - The Node.js buffer.
     * @param {Object} opts - Options parameter.
     * @param {function} opts.onLine - A callback that gets called on each line (for example: `function (lineArr) {}`).
     * @param {function} opts.onEnd - A callback that gets returned after all the lines have been read.
     * @param {String} opts.delimiter - The delimiter used when parsing.
     * @param {Number} opts.lineLimit - The maximum number of lines read.
     * @param {Number} opts.skipLines - The number of lines that should be skipped before first calling the callback.
     * @example
     * // import fs module
     * var fs = require('qminer').fs;
     * // open a csv file and read the lines
     */
    exports.readCsvLines = function (fin, opts) {
    	exports.readLines(fin, processCsvLine(opts), opts.onEnd);
    }
    
    /**
     * Reads json that was serialized using `fs.FOut.writeJson`.
     * @returns {Object} Json object.
     * @example
     * // import fs module
     * var fs = require('qminer').fs;
     * // create and save a json and then read it using the readJson method
     */
    exports.FIn.prototype.readJson = function () {
    	var str = this.readString();
    	return JSON.parse(str);
    }
    
    /**
     * Saves json object, which can be read by `fs.FIn.readJson`.
     * @returns {Object} obj - Json object to write.
     * @returns {module:fs.FOut} Self.
     * @example
     * // import fs module
     * var fs = require('qminer').fs;
     * // create and save a json using the writeJson method
     */
    exports.FOut.prototype.writeJson = function (json) {
    	var str = JSON.stringify(json);
    	this.writeBinary(str);
    	return this;
    }

    
