/**
 * 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(line) - A callback that gets called on each line (for example: `function (line) {}`).
     *   Function must return `true` to continue reading, else reading is stoped and `onEnd` is called.
     * @param {function} onEnd(err) - A callback that gets returned after all the lines have been read or
     *   function `onLine` returned `false`. If error was due to exception, the exception is provided in `err`.
     * @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;
     *     return true;
     * }
     * function onEnd(err) {
     *     if (err) { console.log("Error:", err); }
     *     console.log("Number of lines", numberOfLines);
     * }
     * fs.readLines(fin, onLine, onEnd);
     */
 exports.readLines = function (buffer, onLine, onEnd) {}
/**
    * 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.
     * As specified in CSV format standard defined in [RFC 4180]{@link https://tools.ietf.org/html/rfc4180}
     * double-quotes (") can be used as escape characters. If a double-quote appears in a field, the field nust be
     * enclosed in double-quotes and the double-quote appearing inside a field must be escaped by preceding it with
     * another double quote.
     * 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                                                                                                                                   
     * let fs = require('qminer').fs;                                                                                                                        
     * // create a file and write some lines                                                                                                                 
     * let fout = fs.openWrite('test.csv');                                                                                                                  
     * fout.write('name,movie\nGeorge Clooney,"O Brother, Where Art Thou?"\n"Sylvester ""Sly"" Stallone",Expendables');                                      
     * fout.close();                                                                                                                                         
     * // open the file in read mode                                                                                                                         
     * let fin = fs.openRead('test.csv');                                                                                                                    
     * // prepare callbacks for csv parsing                                                                                                                  
     * // count the lines and for each line output the parsed cells                                                                                          
     * let nLines = 0;                                                                                                                                       
     * function onLine(lineVals) {                                                                                                                           
     *     nLines += 1;                                                                                                                                      
     *     console.log(lineVals);                                                                                                                            
     *     return true;                                                                                                                                      
     * }                                                                                                                                                     
     * // at the end output the number of lines                                                                                                              
     * function onEnd(err) {                                                                                                                                 
     *     if (err) { console.log("Error:", err); }                                                                                                          
     *     console.log("Number of lines", nLines);                                                                                                           
     * }                                                                                                                                                     
     * // parse the csv files                                                                                                                                
     * fs.readCsvLines(fin, {                                                                                                                                
     *     "onLine": onLine,                                                                                                                                 
     *     "onEnd": onEnd                                                                                                                                    
     * });                                                                                                                                                   
     */
    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;
    }