/*
 *  Draco Econometrics
 *  Copyright (C) 2006-2007 Approximatrix, LLC
 *  http://www.approximatrix.com
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.approximatrix.draco.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.lang.NumberFormatException;

import javax.swing.JOptionPane;

import com.approximatrix.draco.datamodel.Constant;
import com.approximatrix.draco.datamodel.DateObject;
import com.approximatrix.draco.datamodel.ParseDate;
import com.approximatrix.draco.datamodel.StatTableModel;

public class ImportExport {

	public static final int INTEGER_CLASS = 1;
	public static final int DOUBLE_CLASS = 2;
	public static final int DATE_CLASS = 3;

	public static final String DEFAULT_COLUMN_NAME = "import";
	
	private static final String DEFAULT_SEPARATOR = ",";
	
	private static final String NULL_VALUE = "NA";

	public static boolean SaveConstants(File out_file, StatTableModel model) {
		ArrayList constants = model.getConstants();
		if(constants == null)
			return false;
		else if(constants.size() == 0)
			return false;
		
		try {
    		PrintWriter out_stream = new PrintWriter(new FileWriter(out_file));
    		
    		for(int i=0;i<constants.size();i++) {
    			Constant one_c = (Constant)constants.get(i);
    			out_stream.print(one_c.getName()+DEFAULT_SEPARATOR);
    			out_stream.println(one_c.getValue().doubleValue());
    		}
    		
    		out_stream.close();
    		
    	} catch (IOException e) {
    		return false;
    	}
    	
    	return true;
	}
	
	public static boolean LoadConstants(File toload, StatTableModel model) {
		BufferedReader input;
		try {
			input = new BufferedReader(new FileReader(toload));
		} catch (FileNotFoundException fnfe) {
			return false;
		}
		
		try {
			String current_line = input.readLine();
			while(current_line != null) {
				String[] two = current_line.split(DEFAULT_SEPARATOR);
				if(two.length != 2)
					break;
				
				try {
					Double value = new Double(two[1]);
					model.getConstants().add(new Constant(two[0],value));
				} catch (NumberFormatException nfe) {
					
				}
				current_line = input.readLine();
			}
			input.close();
    	} catch (IOException e) {
    		return false;
    	}
    	
    	return true;
	}
	
	public static Object[] LoadText(BufferedReader input, int header_row, int skip_rows, String separators, int number_rows) {
		Object[] return_val = new Object[2];
		return_val[0] = null;
		return_val[1] = null;
		
		ArrayList results = null;
		
		String regex_string = separators; //"["+separators+"]";
		String[] headers = null;
		try {
			String current_line = null; 
			
			// Check for headers
			int j=0;
			current_line = input.readLine().trim();
			while(current_line != null) {
				j++;
				current_line = current_line.trim();
				String[] contents = current_line.split(regex_string);
				
				if(j==header_row) {
					headers = contents;
					for(int kk = 0;kk<headers.length;kk++)
						headers[kk] = headers[kk].trim();
					return_val[1] = headers;
					contents = null;
				}
				
				if(j>skip_rows && contents != null) {
					if(results == null) { 
						results = new ArrayList();
						return_val[0] = results;
					}
					results.add(contents);
					contents = null;
				}
				current_line = input.readLine();				
			}

		} catch (IOException ioe) {
			return return_val;
		}
		
		return return_val;
	}
	
	public static Object[] LoadText(File toload, int header_row, int skip_rows, String separators, int number_rows) {
		Object[] return_val = new Object[2];
		return_val[0] = null;
		return_val[1] = null;
		
		BufferedReader input;
		try {
			input = new BufferedReader(new FileReader(toload));
		} catch (FileNotFoundException fnfe) {
			return return_val;
		}

		try {
			return_val = LoadText(input,header_row,skip_rows,separators,number_rows);
			input.close();
		} catch (IOException ioe) {
			return return_val;
		}
		
		return return_val;
	}

	/** Creates a new StatTableModel from an ArrayList of String[]'s,
	 * inteligently deciding whether something is a Double or an Integer.
	 * 
	 * @param data an ArrayList, each entry representing a data row, containing a String[] array of values
	 * @return a new StatTableModel with default column titles
	 */ 	
	public static StatTableModel CreatePanelModel(ArrayList data) {
		int number_columns;
		int number_rows;
		
		if(data == null) return null;
		if(data.size() == 0) return null;
		
		// Determine the data structure from the first row
		String[] first_row = (String[])data.get(0);
		number_columns = first_row.length;
		number_rows = data.size();

		Object[][] passing = new Object[number_rows][number_columns];
		for(int row = 0; row < number_rows; row++) {

			String[] this_row = (String[])data.get(row);
			if(this_row == null) continue;

			for(int col = 0; col < Math.min(number_columns,this_row.length); col++) {
				if(this_row[col] == null) {
					passing[row][col] = null;
				} else if(this_row[col].indexOf('.') != -1) {
					try {
						passing[row][col] = new Double(this_row[col].trim());
					} catch (NumberFormatException nfe) {
						passing[row][col] = null;
					}
				} else {
					try {
						passing[row][col] = new Integer(this_row[col].trim());
					} catch(NumberFormatException nfe) {
						passing[row][col] = null;
					}
				}
			}
		}

		// Just generate default names for now
		String[] column_names = new String[number_columns];
		for(int col = 0; col < number_columns; col++) {
			column_names[col] = new String(DEFAULT_COLUMN_NAME + new Integer(col).toString());
		}

		// Create the StatTableModel for return
		return new StatTableModel(passing,column_names);	
		
	}
	
	/** Creates a new StatTableModel by from an ArrayList full of String[]'s with
	 * the specified column names.
	 *
	 * @param data an ArrayList, each entry a row, containing a String[] representing the row's data for each column
	 * @param column_names the names of the columns
	 * @return a new StatTableModel or null if there's a problem
	 */
	public static StatTableModel CreatePanelModel(ArrayList data, String[] column_names) {
		StatTableModel return_val = CreatePanelModel(data);
		if(return_val != null && column_names != null) {
			for(int col=0;col<return_val.getColumnCount()&&col<column_names.length;col++) {
				return_val.renameColumn(col, column_names[col]);
			}
		}

		return return_val;
	}

	/** Imports time series data with text-specified dates into an existing StatTableModel
	 * that already has a DateObject column to use a reference while importing data. The
	 * function imports all numbers as Doubles.  The dates in the text file are used only
	 * for referencing, not for building the StatTableModel
	 *
	 * @param date_table a valid StatTableModel that contains a DateObject column
	 * @param data the data to import into the model
	 * @param date_column the column in the data ArrayList containing a date in String form
	 * @param column_names the names of each column in the data ArrayList or null if none
	 * @return the modified version of date_table containing all imported data corresponding to the pre-existing DateObjects
	 */
	public static StatTableModel ImportTimeSeriesData(StatTableModel date_table, ArrayList data, int counted_date_column, String[] column_names) {
		if(date_table == null || data == null) return null;
		if(data.size() == 0) return null;
		
		// Column counting for regular poeple should start at 1
		int date_column = counted_date_column-1;
		// Make sure this makes sense
		if(date_column < 0)
			return null;
		
		
		int number_columns = ((String[])data.get(0)).length;
		int number_rows = data.size();
		
		// Cycle through one column at a time
		for(int col=0;col<number_columns;col++) {
			
			// Don't deal with the date column
			if(col == date_column) continue;
			
			Object[] entries = new Object[date_table.getRowCount()];
			for(int row=0;row<entries.length;row++) {
				entries[row] = null;
			}
			
			for(int drow = 0;drow<number_rows;drow++) {
				String date = ((String[])data.get(drow))[date_column];
				String value = ((String[])data.get(drow))[col];
				
				// Get this row's date
				DateObject date_value = ParseDate.ParseTextToDate(date,date_table.getDateType());
				
				// Now search the table for a matching date
				for(int row=0;row<entries.length;row++) {
					DateObject this_table = date_table.getDateAt(row);
					if(this_table.getCountFrom(date_value) == 0) {
						try {
							entries[row] = new Double(value);
						} catch(NumberFormatException nfe) {
							entries[row] = null;
						}
						break;
					}
				}
			}
			
			// At this point, entries is filled with valid column data.
			// Append this column to the date_table model
			String this_column = new String(DEFAULT_COLUMN_NAME+new Integer(col).toString());
			if(column_names != null) {
				if(col < column_names.length) {
					this_column = column_names[col];
				}
			}
			date_table.addColumn(entries,this_column);
		}
		
		return date_table;
	}
	
	public static boolean SaveText(PrintWriter out_stream, StatTableModel model) {
		
		out_stream.print(GenerateCSVData(model));
		
		return true;
	}
	
	public static boolean SaveText(File writer, StatTableModel model) {
		try {
    		PrintWriter out_stream = new PrintWriter(new FileWriter(writer));
    		
    		SaveText(out_stream,model);
    		
    		out_stream.close();
    		
    	} catch (IOException e) {
    		return false;
    	}
    	
    	return true;
	}
	
	/** Generates a new StatTableModel based on the start and end DateObjects
	 * 
	 * @param start start date
	 * @param end end date
	 * @return a StatTableModel with one column containing the date
	 */
	public static StatTableModel CreateTimeSeriesModel(DateObject start, DateObject end) {
		
		int difference = start.getCountFrom(end);
		DateObject base = end;
		if(difference < 0) {
			base = start;
			difference = -1*difference;
		}
		
		DateObject[][] values = new DateObject[difference+1][1];
		values[0][0] = base;
		base = null;
		for(int i=1;i<values.length;i++) {
			values[i][0] = new DateObject(values[0][0],i);
		}

		String[] title = {"Time"};
		
		// Now the array of DateObjects are built, just construct
		// the StatTableModel
		StatTableModel return_val = new StatTableModel(values,title);
		
		return return_val;
	}
	
	public static String GenerateCSVData(StatTableModel model, int[] rows, int[] columns) {
		return GenerateCSVData(model,rows,columns,DEFAULT_SEPARATOR);
	}
		
	public static String GenerateCSVData(StatTableModel model, int[] rows, int[] columns, String separator) {
		if(rows == null || columns == null) return null;
		
		StringBuffer generator = new StringBuffer();
		
		String[] column_names = model.getColumnNames(columns);
		
		// First, variable names
		for(int i=0;i<column_names.length;i++) {
			generator.append(column_names[i]);
			if(i<column_names.length-1)
				generator.append(separator);
		}
		generator.append(System.getProperty("line.separator"));
		
		// Now, all data
		Object[][] data = model.getData(rows,columns);
		for(int i=0;i<data.length;i++) {
			for(int j=0;j<data[i].length;j++) {
				if(data[i][j] != null)
					generator.append(data[i][j].toString());
				else
					generator.append(NULL_VALUE);
				
				if(j<data[i].length-1)
					generator.append(separator);
				else
					generator.append(System.getProperty("line.separator"));
			}
			
		}
		
		return generator.toString();
	}
	
	public static String GenerateCSVData(StatTableModel model, int[] columns) {
		return GenerateCSVData(model,columns,DEFAULT_SEPARATOR);
	}
		
	
	public static String GenerateCSVData(StatTableModel model, int[] columns, String separator) {
		int[] rows = new int[model.getRowCount()];
		for(int i=0;i<model.getRowCount();i++) rows[i] = i;
		return GenerateCSVData(model,rows,columns,separator);
	}
	
	public static String GenerateCSVData(StatTableModel model) {
		return GenerateCSVData(model, DEFAULT_SEPARATOR);
	}
	
	public static String GenerateCSVData(StatTableModel model, String separator) {
		int[] columns = new int[model.getColumnCount()];
		for(int i=0;i<model.getColumnCount();i++) columns[i] = i;
		return GenerateCSVData(model,columns,separator);
	}
}
