package dotnet54.tscore.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import dotnet54.tscore.data.*;
import org.apache.commons.lang3.time.DurationFormatUtils;
import dotnet54.tscore.exceptions.NotSupportedException;
import dotnet54.util.PrintUtilities;

/**
 * 
 * DEV
 * 
 * add support for skip top n rows, exclude columns, 
 * 
 * @author shifaz
 * @email ahmed.shifaz@monash.edu
 *
 */

public class CsvReader {

	protected boolean hasHeader = false;
	protected String separator = ",";
	protected int labelColumn = 0;	// only supported {0,-1} == first or last column
	protected String extension = ".csv";
	protected int verbosity = 0;
	
	//DEV: HACKY: some stats of the last loaded file, this  gets overwritten every time, just for a quick printing if needed
	public CsvFileInfo _lastLoadedFile;
	
	public class CsvFileInfo{
		public String fileFullName;
		public String fileName; //without path
		public String datasetName;  //without extension
		public int rows;
		public int columns ;
		// column  types
		
		//simple stats
		public long memBeforeLoad; //in bytes
		public long memAfterLoad; //in bytes
		public long loadTime; //in nanoseconds

		public CsvFileInfo() {

		}
		
		
	}
	
	public CsvReader() {
		
	}
	
	public CsvReader(boolean hasHeader, int labelColumn) {
		this.hasHeader = hasHeader;
		this.labelColumn = labelColumn;
	}
	
	public CsvFileInfo getFileInfo(String fileName) throws IOException {
		FileReader input = new FileReader(fileName);
		LineNumberReader lineNumberReader = new LineNumberReader(input);		
		String line = null;
        String[] line_array = null;		
        CsvReader.CsvFileInfo file_info = new CsvFileInfo();
        
		try {
			boolean length_check = true;
			
	        while ((line = lineNumberReader.readLine()) != null){
	        	if (length_check) {
	        		length_check = false;
	        		line_array = line.split(separator);
	        	}
//	            System.out.println("Line " + lineNumberReader.getLineNumber() + ": " + line);
	        }			
	        
		}finally{
			if (input != null) input.close();
		}
		
		//this output array contains file information
		if (hasHeader) {
			//number of rows;
			file_info.rows = lineNumberReader.getLineNumber() == 0 ? lineNumberReader.getLineNumber(): lineNumberReader.getLineNumber() - 1; 
		}else {
			//number of rows;
			file_info.rows =lineNumberReader.getLineNumber();  
		}
		
		file_info.columns = line_array.length;  //number of columns;

		
		//record file name and infer dataset name
		Path p = Paths.get(fileName);
		file_info.fileFullName = fileName;
		file_info.fileName = p.getFileName().toString();
		file_info.datasetName = file_info.fileName.substring(0, file_info.fileName.indexOf('.'));
		
		return file_info;
	}

	public MTSDataset readFile(String fileName){
		LabelEncoder labelEncoder = new NonContinuousLabelEncoder();
		return readFile(fileName, labelEncoder);
	}

	// TODO add support for labelEncoder
	public MTSDataset readFile(String fileName, LabelEncoder labelEncoder){
        BufferedReader br = null;
        String line = "";
        int i = 0;
        long start, end, elapsed;
        CsvFileInfo file_info;
		MTSDataset dataset = null;
        long used_mem;
        String[] line_array = null;
        double[][] features;
        int num_features;
        Double label;
        File f = new File(fileName);
        int num_dimensions = 1; //TODO currently supports univariate files, use .ts reader for multivariate
        
        try {
        	if (verbosity > 0) {
        		System.out.print("Reading csv file ["+ f.getName() +"]:");
        	}
         	start  = System.nanoTime();

        	file_info = getFileInfo(fileName);	//useful to preallocate memory for large files
        	num_features = file_info.columns - 1; //-1 to exclude target the column
        	dataset = new MTSDataset(file_info.rows);
            br = new BufferedReader(new FileReader(fileName));
            
            while ((line = br.readLine()) != null) {
            	
            	if (hasHeader && i == 0) {
            		i++;
            		continue; //skip header row
            	}
            	
                // use comma as separator
                line_array = line.split(separator);
                
//                System.out.println("Line  " + i + " , Class=[" + line_array[0] + "] length= " + line_array.length);
               
                //allocate new memory every turn
                features = new double[num_dimensions][num_features];
     
                if (labelColumn == 0) {
	                for (int j = 1; j <= num_features; j++){
	                	features[0][j-1] = Double.parseDouble(line_array[j]);
	                }   
	                label = Double.parseDouble(line_array[0]);
                }else if (labelColumn == -1){
                	//assume target is the last column
                	int j;
	                for (j = 0; j < num_features; j++){
	                	features[0][j] = Double.parseDouble(line_array[j]);
	                }  
	                label = Double.parseDouble(line_array[j]);
                }else {
                	throw new NotSupportedException("Only labelColumn = {0, -1} is supported for first or last column");
                }

				TimeSeries series = new MTimeSeries(features, label.intValue());
                dataset.add(series);
                
                i++;
                
            	if (verbosity > 0) {
	                if (i % 1000  == 0) {
	                	if (i % 100000 == 0) {
	                		System.out.print("\n");
	                    	if (i % 1000000 == 0) {
	                    		used_mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
	                    		System.out.print(i +":" + used_mem/1024/1024 + "mb\n");
	                    	}else {
	                    		
	                    	}
	                	}else {
	                		System.out.print(".");
	                	}
	                }
            	}

                
            }
            end = System.nanoTime();
            elapsed = end - start;
    		String time_duration = DurationFormatUtils.formatDuration((long) (elapsed/1e6), "H:m:s.SSS");
        	if (verbosity > 0) {
        		System.out.println("finished in " + time_duration);
        	}

        	dataset.setName(file_info.datasetName);
        	_lastLoadedFile = file_info; //hacky, not thread-safe
        	
        } catch (FileNotFoundException e) {
//            e.printStackTrace();
        	PrintUtilities.abort(e);
        } catch (IOException e) {
//            e.printStackTrace();
        	PrintUtilities.abort(e);
        } catch (Exception e) {
			e.printStackTrace();
		} finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
        return dataset;
	}	
	
	

}
