/*
 *  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.XMLio;


import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;  
import org.xml.sax.SAXParseException;

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

public final class XMLTableUtilities {
	
	public static Element CreateTable(Document base, Object[][] data) {
		return CreateTable(base,data,null,false);
	}
	
	public static Element CreateTable(Document base, Object[][] data, Object[] headers) {
		return CreateTable(base,data,headers,false);
	}	
	
	public static Element CreateTable(Document base, Object[][] data, Object[] headers, boolean formal_headers) {
		
		// Error catching
		if(data == null) return null;
		if(data.length == 0) return null;
		if(data[0].length == 0) return null;
		
		Element table = base.createElement(TableTags.TBL_TABLE);
		
		Element col = base.createElement("table:table-column");
        col.setAttribute("table:number-columns-repeated",Integer.toString(data[0].length));
        table.appendChild(col);
		
        // Create a row of headers if appropriate
        Element row = null;
        if(headers != null) {
        	row = base.createElement(TableTags.TBL_ROW);
        	for(int i=0; i< headers.length; i++) {
        		String text = null;
        		if(headers[i] == null)
        			text = "null";
        		else
        			text = headers[i].toString();
        		
        		Element cell = base.createElement(TableTags.TBL_CELL);
        		Element cell_text = base.createElement(TableTags.TBL_CELL_CONTENTS_TEXT);
        		cell_text.appendChild(base.createTextNode(text));
        		cell.appendChild(cell_text);
        		row.appendChild(cell);
        	}
        }
        
        if(row != null && formal_headers) {
        	Element header_rows = base.createElement(TableTags.TBL_HEADER_ROWS);
        	header_rows.appendChild(row);
        	table.appendChild(header_rows);
        	row = null;
        }
        
        Element all_rows = base.createElement(TableTags.TBL_ROWS);
        if(row != null)
        	all_rows.appendChild(row);
        
        for(int i=0;i<data.length;i++) {
        	row = base.createElement(TableTags.TBL_ROW);
        	
        	Object[] data_row = data[i];
        	if(data_row == null)
        		continue;
        	
        	// Cycle through the columns
        	for(int j=0;j<data_row.length;j++)
        		AddTableCell(row,data_row[j]);
        		
        	all_rows.appendChild(row);
        }
        table.appendChild(all_rows);
         
        return table;
	}
	
    private static void AddTableCell(Element parent, Object content) {
    	Document doc = parent.getOwnerDocument();
    	
        Element cell = doc.createElement(TableTags.TBL_CELL);
        
        if(content == null) {
        	content = TableTags.NO_DATA;
        }
        
        if(content.getClass() == Double.class || content.getClass() == Integer.class ) {
        	cell.setAttribute(TableTags.TBL_CELL_TYPE_SUBELEMENT,TableTags.TBL_CELL_TYPE_FLOAT_ATTR);
        	cell.setAttribute(TableTags.TBL_CELL_VALUE_SUBELEMENT,content.toString());
        } else if(content.getClass() == DateObject.class) {
        	cell.setAttribute(TableTags.TBL_CELL_TYPE_SUBELEMENT,"date");
        	cell.setAttribute(TableTags.TBL_CELL_VALUE_SUBELEMENT,content.toString());
        }
        Element cell_text = doc.createElement(TableTags.TBL_CELL_CONTENTS_TEXT);
		cell_text.appendChild(doc.createTextNode(content.toString()));
        cell.appendChild(cell_text);
        parent.appendChild(cell);
    }
    
    public static Object[][] ReadTableData(Element table, boolean headers_present_in_table) {
    	NodeList all_rows = table.getElementsByTagName(TableTags.TBL_ROWS);
    	if(all_rows == null) return null;
    	if(all_rows.getLength() == 0) return null;
    	
    	NodeList rows = ((Element)all_rows.item(0)).getElementsByTagName(TableTags.TBL_ROW);
    	if(rows == null) return null;
    	if(rows.getLength() == 0 || (rows.getLength() <= 1 &&  headers_present_in_table)) return null;
    	
    	int start = 0;
    	if(headers_present_in_table) start = 1;
    	Object[][] return_val = null;
    	
    	for(int i=start;i<rows.getLength();i++) {
    		Object[] this_row = GetRowData((Element)rows.item(i));
    		
    		if(return_val == null) {
    			return_val = new Object[rows.getLength()-start][this_row.length];
    		}
    		
    		for(int j=0;j<this_row.length;j++) {
    			return_val[i-start][j] = this_row[j];
    		}
    	}
    	
    	return return_val;
    }
    
    public static double[][] ReadTableDataDouble(Element table, boolean headers_present_in_table) {
    	Object[][] odata = ReadTableData(table,headers_present_in_table);
    	if(odata == null) return null;
    	
    	double[][] return_val = new double[odata.length][];
    	for(int i=0;i<odata.length;i++) {
    		return_val[i] = new double[odata[i].length];
    		for(int j=0;j<odata[i].length;j++) {
    			if(Number.class.isAssignableFrom(odata[i][j].getClass()))
    				return_val[i][j] = ((Number)odata[i][j]).doubleValue();
    			else
    				return_val[i][j] = Double.NaN;
    		}
    	}
    	return return_val;
    }
    
    public static String[] ReadTableHeaders(Element table) {
    	Element holding_row = null;
    	NodeList headers = table.getElementsByTagName(TableTags.TBL_HEADER_ROWS);
    	if(headers != null) {
    		if(headers.getLength() > 0) {
    			Element header_rows = (Element)headers.item(0);
    			NodeList rows = header_rows.getElementsByTagName(TableTags.TBL_ROW);
    			if(rows != null) {
    				if(rows.getLength()>0)
    					holding_row = (Element)rows.item(0);
    			}
    			
    		}
    	} else {
    		headers = table.getElementsByTagName(TableTags.TBL_ROWS);
    		if(headers != null) {
        		if(headers.getLength() > 0) {
        			Element header_rows = (Element)headers.item(0);
        			NodeList rows = header_rows.getElementsByTagName(TableTags.TBL_ROW);
        			if(rows != null) {
        				if(rows.getLength()>0)
        					holding_row = (Element)rows.item(0);
        			}
        			
        		}
        	}
    	}
    	
    	if(holding_row == null) 
    		return null;
    	
    	Object[] row_data = GetRowData(holding_row);
    	if(row_data == null) return null;
    	String[] return_val = new String[row_data.length];
    	for(int i=0;i<row_data.length;i++) {
    		if(row_data[i] == null)
    			return_val[i] = null;
    		else
    			return_val[i] = row_data[i].toString();
    	}
    	return return_val;
    }
    
    private static Object[] GetRowData(Element row) {
    	NodeList cell_contents = row.getElementsByTagName(TableTags.TBL_CELL);
		if(cell_contents == null) return null;
		if(cell_contents.getLength() < 1) return null;
		
		Object[] return_val = new Object[cell_contents.getLength()];
		for(int i=0;i<cell_contents.getLength();i++) {
			Element one_cell = (Element)cell_contents.item(i);
			String type = one_cell.getAttribute(TableTags.TBL_CELL_TYPE_SUBELEMENT);
			
			String cell_text = ((Element)one_cell.getElementsByTagName("text:p").item(0)).getTextContent();
			
			if(type.compareTo("date")==0) {
				//return_val[i] = ParseDate.ParseTextToDate(cell_text,time_series_type);
				
				System.err.println("Date handling in table utilities not yet supported...");
				
			} else if(type.compareTo("float")==0) {
				try {
					return_val[i] = new Double(cell_text);
				} catch(NumberFormatException nfe) {
					return_val[i] = null;
				}
			} else {
				// Other types are not supported!
				// Assume its a string
				return_val[i] = new String(cell_text);
				if(((String)return_val[i]).compareTo(TableTags.NO_DATA) == 0) {
					return_val[i] = null;
				}
			}
		}
		
		return return_val;
    }
	
}
