
package jp.riken.brain.ni.samuraigraph.application;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.StringTokenizer;

import jp.riken.brain.ni.samuraigraph.base.SGData;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;
import jp.riken.brain.ni.samuraigraph.data.SGDataTypeConstants;
import jp.riken.brain.ni.samuraigraph.data.SGISXYTypeData;
import jp.riken.brain.ni.samuraigraph.data.SGSXYDateData;
import jp.riken.brain.ni.samuraigraph.data.SGSXYData;
import jp.riken.brain.ni.samuraigraph.data.SGSXYMultipleData;
import jp.riken.brain.ni.samuraigraph.data.SGSXYSamplingData;


/**
 * A class to create data objects.
 */
public class SGDataCreator
{

	/**
	 * The default constructor.
	 *
	 */
	public SGDataCreator()
	{
		super();
	}


	/**
	 * Creates a data object.
	 * @param path - the path name of the file
	 * @param infoList - information of data
	 * @return created data
	 * @throws FileNotFoundException
	 */
	public SGData create( final String path, final ArrayList infoList )
		throws FileNotFoundException
	{
		if( path==null || infoList==null )
		{
			throw new NullPointerException("path==null || infoList==null");
		}

		// when information is empty, returns null
		if( infoList.size()==0 )
		{
			return null;
		}

		SGData data = null;
		final String dataType = (String)infoList.get(0);
		if( dataType.equals( SGDataTypeConstants.SXY_DATA ) )
		{
			data = (SGData)this.createSXYData(path);
		}
		else if( dataType.equals( SGDataTypeConstants.SXY_DATE_DATA ) )
		{
			data = (SGData)this.createSXYDateData(path);
		}
		else if( dataType.equals( SGDataTypeConstants.VXY_DATA ) )
		{
			
		}
		else if( dataType.equals( SGDataTypeConstants.SXY_MULTIPLE_DATA ) )
		{
			data = this.createSXYMultipleData(path);
		}
		else if( dataType.equals( SGDataTypeConstants.SXY_SAMPLING_DATA ) )
		{
			if( infoList.size()<=1 )
			{
				return null;
			}

			// 第2引数にはサンプリング周波数
			final double rate = ((Double)infoList.get(1)).doubleValue();

			data = this.createSXYSamplingData(path,rate);
		}

		return data;
	}



	private static ArrayList getSXYTextColumnIndexList( final int columnNum )
	{
		ArrayList list = new ArrayList();
		if( columnNum==3 )
		{
			list.add( new Integer(2) );
		}
		else if( columnNum==5 )
		{
			list.add( new Integer(4) );
		}

		return list;
	}

	private static ArrayList getVXYTextColumnIndexList( final int columnNum )
	{
		ArrayList list = new ArrayList();
		return list;
	}

	private static ArrayList getSXYMultipleTextColumnIndexList( final int columnNum )
	{
		ArrayList list = new ArrayList();
		return list;
	}


	private static ArrayList getSXYDateTextColumnIndexList( final int columnNum )
	{
		ArrayList list = new ArrayList();
		list.add( new Integer(0) );
		if( columnNum==3 )
		{
			list.add( new Integer(2) );
		}
		else if( columnNum==5 )
		{
			list.add( new Integer(4) );
		}
		
		return list;
	}


	/**
	 * 
	 * @return
	 */
	private SGISXYTypeData createSXYData( final String path )
		throws FileNotFoundException
	{

		// get the number of columns
		final int num = this.getColumnNumber(path);
		if( num==-1 )
		{
			return null;
		}
		if( num<2 )
		{
			return null;
		}

		ArrayList columnIndexList = getSXYTextColumnIndexList(num);


		// リストの配列を作成
		ArrayList[] listArray
			= this.createListArray( path, num, columnIndexList );
		if( listArray==null )
		{
			return null;
		}


		// check
		final int dataLength = listArray[0].size();
		for( int ii=1; ii<listArray.length; ii++ )
		{
			if( listArray[ii].size()!=dataLength )
			{
				return null;
			}
		}


		//
		ArrayList cList = new ArrayList();
		if( this.getFileTypeCandidateList( path, cList ) == false )
		{
			return null;
		}


		//
		// create data object
		//

		SGISXYTypeData data = null;
		String str = null;
		Double d = null;
		{
			double[] xArray = new double[dataLength];
			double[] yArray = new double[dataLength];
			for( int ii=0; ii<dataLength; ii++ )
			{
				str = (String)listArray[0].get(ii);
				d = SGUtilityText.getDouble(str);
				if( d==null )
				{
					return null;
				}
				xArray[ii] = d.doubleValue();

				str = (String)listArray[1].get(ii);
				d = SGUtilityText.getDouble(str);
				if( d==null )
				{
					return null;
				}
				yArray[ii] = d.doubleValue();
			}

			double[] lArray = null;
			double[] uArray = null;
			String[] strArray = null;
			switch( num )
			{
				case 2 :
				{
					break;
				}
				case 4 :
				{
					lArray = new double[dataLength];
					uArray = new double[dataLength];
					for( int ii=0; ii<dataLength; ii++ )
					{
						str = (String)listArray[2].get(ii);
						d = SGUtilityText.getDouble(str);
						if( d==null )
						{
							return null;
						}
						lArray[ii] = d.doubleValue();

						str = (String)listArray[3].get(ii);
						d = SGUtilityText.getDouble(str);
						if( d==null )
						{
							return null;
						}
						uArray[ii] = d.doubleValue();
					}
					break;
				}
				case 3 :
				{
					strArray = new String[dataLength];
					for( int ii=0; ii<dataLength; ii++ )
					{
						strArray[ii] = (String)listArray[2].get(ii);
					}
					break;
				}
				case 5 :
				{
					lArray = new double[dataLength];
					uArray = new double[dataLength];
					strArray = new String[dataLength];
					for( int ii=0; ii<dataLength; ii++ )
					{
						str = (String)listArray[2].get(ii);
						d = SGUtilityText.getDouble(str);
						if( d==null )
						{
							return null;
						}
						lArray[ii] = d.doubleValue();

						str = (String)listArray[3].get(ii);
						d = SGUtilityText.getDouble(str);
						if( d==null )
						{
							return null;
						}
						uArray[ii] = d.doubleValue();

						strArray[ii] = (String)listArray[4].get(ii);
					}
					break;
				}
			
			}

			data = new SGSXYData( xArray, yArray, lArray, uArray, strArray );
		}

		return data;
	}




	/**
	 * 
	 * @return
	 */
	private SGISXYTypeData createSXYDateData( final String path )
		throws FileNotFoundException
	{
		// get the number of columns
		final int num = this.getColumnNumber(path);
		if( num==-1 )
		{
			return null;
		}
		if( num<2 )
		{
			return null;
		}


		ArrayList columnIndexList = getSXYDateTextColumnIndexList(num);


		// リストの配列を作成
		ArrayList[] listArray = this.createListArray( path, num, columnIndexList );
		if( listArray==null )
		{
			return null;
		}


		// check
		final int dataLength = listArray[0].size();
		for( int ii=1; ii<listArray.length; ii++ )
		{
			if( listArray[ii].size()!=dataLength )
			{
				return null;
			}
		}


		//
		ArrayList cList = new ArrayList();
		if( this.getFileTypeCandidateList( path, cList ) == false )
		{
			return null;
		}


		//
		// create data object
		//

		SGISXYTypeData data = null;
		String str = null;
		Double d = null;
		{
			String[] strArray = new String[dataLength];
			for( int ii=0; ii<dataLength; ii++ )
			{
				strArray[ii] = (String)listArray[0].get(ii);
			}

			double[] yArray = new double[dataLength];
			for( int ii=0; ii<dataLength; ii++ )
			{
				str = (String)listArray[1].get(ii);
				d = SGUtilityText.getDouble(str);
				if( d==null )
				{
					return null;
				}
				yArray[ii] = d.doubleValue();
			}

			double[] lArray = null;
			double[] uArray = null;
			if( num==4 | num==5 )
			{
				lArray = new double[dataLength];
				uArray = new double[dataLength];
				for( int ii=0; ii<dataLength; ii++ )
				{
					str = (String)listArray[2].get(ii);
					d = SGUtilityText.getDouble(str);
					if( d==null )
					{
						return null;
					}
					lArray[ii] = d.doubleValue();

					str = (String)listArray[3].get(ii);
					d = SGUtilityText.getDouble(str);
					if( d==null )
					{
						return null;
					}
					uArray[ii] = d.doubleValue();
				}
			}
			
			String[] sArray = null;
			int strIndex = -1;
			if( num==3 )
			{
				strIndex = 2;
			}
			else if( num==5 )
			{
				strIndex = 4;
			}
			if( strIndex!=-1 )
			{
				sArray = new String[dataLength];
				for( int ii=0; ii<dataLength; ii++ )
				{
					str = (String)listArray[strIndex].get(ii);
					sArray[ii] = new String(str);
				}
			}

			try
			{
				data = new SGSXYDateData( strArray, yArray, lArray, uArray, sArray );
			}
			catch( ParseException ex )
			{
				
			}
		}

		return data;
	}



	/**
	 * 
	 * @return
	 */
	private SGSXYMultipleData createSXYMultipleData( final String path )
		throws FileNotFoundException
	{

		// ファイルからデータ列数を取得
		final int num = this.getColumnNumber(path);
		if( num==-1 )
		{
			return null;
		}
		if( num < 2 )
		{
			return null;
		}

		ArrayList columnIndexList = getSXYMultipleTextColumnIndexList(num);


		// リストの配列を作成
		ArrayList[] listArray = this.createListArray( path, num, columnIndexList );
		if( listArray==null )
		{
			return null;
		}

		// check
		final int dataLength = listArray[0].size();
		for( int ii=1; ii<listArray.length; ii++ )
		{
			if( listArray[ii].size()!=dataLength )
			{
				return null;
			}
		}


		//
		// create data object
		//

		SGSXYMultipleData data = null;
		{
			double[] xArray = new double[dataLength];
			for( int ii=0; ii<dataLength; ii++ )
			{
				String str = (String)listArray[0].get(ii);
				Double d = SGUtilityText.getDouble(str);
				if( d==null )
				{
					return null;
				}
				xArray[ii] = d.doubleValue();
			}

			double[][] yArray = new double[num-1][];
			for( int ii=0; ii<num-1; ii++ )
			{
				yArray[ii] = new double[dataLength];
				for( int jj=0; jj<dataLength; jj++ )
				{
					String str = (String)listArray[ii+1].get(jj);
					Double d = SGUtilityText.getDouble(str);
					if( d==null )
					{
						return null;
					}
					yArray[ii][jj] = d.doubleValue();
				}
			}

			data = new SGSXYMultipleData( xArray, yArray );
		}

		return data;
	}



	/**
	 * 
	 * @return
	 */
	private SGSXYMultipleData createSXYSamplingData(
		final String path, final double sRate )
			throws FileNotFoundException
	{

		// ファイルからデータ列数を取得
		final int num = this.getColumnNumber(path);
		if( num==-1 )
		{
			return null;
		}
		if( num < 1 )
		{
			return null;
		}

		ArrayList columnIndexList = getSXYMultipleTextColumnIndexList(num);

		// リストの配列を作成
		ArrayList[] listArray = this.createListArray( path, num, columnIndexList );
		if( listArray==null )
		{
			return null;
		}

		// check
		final int dataLength = listArray[0].size();
		for( int ii=1; ii<listArray.length; ii++ )
		{
			if( listArray[ii].size()!=dataLength )
			{
				return null;
			}
		}


		//
		// create data object
		//

		SGSXYSamplingData data = null;

		Double d = null;
		String str = null;
		{
			double[][] yArray = new double[num][];
			for( int ii=0; ii<num; ii++ )
			{
				yArray[ii] = new double[dataLength];
				for( int jj=0; jj<dataLength; jj++ )
				{
					str = (String)listArray[ii].get(jj);
					d = SGUtilityText.getDouble(str);
					if( d==null )
					{
						return null;
					}
					yArray[ii][jj] = d.doubleValue();
				}
			}

			data = new SGSXYSamplingData( sRate, yArray );
		}

		return data;
	}


	// Reads the first line and returns the number of token
	private int getColumnNumber( final String path )
		throws FileNotFoundException
	{
		int num = -1;

		BufferedReader br = null;
		try
		{
			br = new BufferedReader( new FileReader(path) );
			ArrayList tokenList = this.getFirstTokenList(br);
			num = tokenList.size();
		}
		catch( FileNotFoundException ex )
		{
			throw ex;
		}
		finally
		{
			if( br!=null )
			{
				try
				{
					br.close();
				}
				catch( IOException ex )
				{
					
				}
			}
		}

		return num;
	}



	/**
	 * Creates an array of the list.
	 * @param path
	 * @param num
	 * @return
	 * @throws FileNotFoundException
	 */
	private ArrayList[] createListArray(
		final String path, final int num, final ArrayList columnIndexList )
		throws FileNotFoundException
	{

		// create an array of list
		ArrayList[] listArray = new ArrayList[num];
		for( int ii=0; ii<listArray.length; ii++ )
		{
			listArray[ii] = new ArrayList();
		}

		// read the file
		BufferedReader br = null;
		try
		{
			br = new BufferedReader( new FileReader(path) );

			while( true )
			{
				// read a line
				final String line = SGUtilityText.readLine(br);
				if( line==null )
				{
					break;
				}

				// check the line
				if( isValidLine( line ) == false )
				{
					return null;
				}

				// check the text string				
				ArrayList indexList = getColumnIndexListOfString(line);
				if( indexList.equals( columnIndexList ) == false )
				{
					return null;
				}

				// break the string into tokens
				final ArrayList tokenList = new ArrayList();
				if( SGUtilityText.tokenize( line, tokenList ) == false )
				{
					return null;
				}

				// check the tokens
				if( tokenList.size()!=num )
				{
					if( tokenList.size()!=0 )
					{
						// format error
						return null;
					}
					else
					{
						// only contains spaces and tabs
						continue;
					}
				}

				// array of the tokens
				final String[] array = new String[num];
				for( int ii=0; ii<num; ii++ )
				{
					String str = (String)tokenList.get(ii);
					listArray[ii].add( str );
				}
			}
		}
		catch( FileNotFoundException ex )
		{
			throw ex;
		}
		finally
		{
			if( br!=null )
			{
				try
				{
					br.close();
				}
				catch( IOException ex )
				{
					
				}
			}
		}

		return listArray;
	}
	
	
	
	public static void main( String[] args )
	{
		String line = "\"a \"\\\"a\"\"";
System.out.println(line);
System.out.println( isValidLine(line) );

	}
	

	// check whether double quotation marks exist in the right position
	private static boolean isValidLine( final String line )
	{
		int cnt = 0;
		boolean inside = false;
		for( int ii=0; ii<line.length(); ii++ )
		{
			final char c = line.charAt(ii);
			if( c=='\"' )
			{
				if( inside )
				{
					final char before = line.charAt(ii-1);
					if( before=='\\' )
					{
						continue;
					}
				}
				else
				{
					inside = true;
				}
				cnt++;
			}
		}

		return (cnt%2==0);
	}
	
	
	// returns a list of column index of the text string with two double quotation marks
	private static ArrayList getColumnIndexListOfString( final String line )
	{
		ArrayList list = new ArrayList();

		boolean inside = false;
		final char[] cArray = line.toCharArray();
		for( int ii=0; ii<cArray.length; ii++ )
		{
			if( cArray[ii]=='\"' )
			{
				inside = !inside;
			}

			if( inside )
			{
				cArray[ii] = '\"';
			}
		}

		int cnt = 0;
		StringTokenizer stk = new StringTokenizer( String.valueOf(cArray) );
		while( stk.hasMoreTokens() )
		{
			String str = stk.nextToken();
//System.out.println(str);
			if( str.startsWith("\"") )
			{
				list.add( new Integer(cnt) );
			}
			cnt++;
		}

//System.out.println(list);
		return list;
	}



	/**
	 * Determine the data-type from the first line.
	 */
	public boolean getFileTypeCandidateList(
		final String path,
		final ArrayList cList ) throws FileNotFoundException
	{
		BufferedReader br = null;
		try
		{
			br = new BufferedReader( new FileReader( path ) );

			String line = SGUtilityText.readLine(br);
			
			// check the line
			if( isValidLine( line ) == false )
			{
				return false;
			}

			ArrayList indexList = getColumnIndexListOfString(line);

			ArrayList tokenList = this.getFirstTokenList(br);
			final int tokenNum = tokenList.size();
			HashSet cSet = new HashSet();

			// SXY
			if( this.evaluateDataTypeSXY(tokenList) )
			{
				ArrayList columnIndexList = getSXYTextColumnIndexList(tokenNum);
				if( indexList.equals( columnIndexList ) )
				{
					cSet.add(SGDataTypeConstants.SXY_DATA);
				}
			}

			// VXY
			if( this.evaluateDataTypeVXY(tokenList) )
			{
				ArrayList columnIndexList = getVXYTextColumnIndexList(tokenNum);
				if( indexList.equals( columnIndexList ) )
				{
					cSet.add(SGDataTypeConstants.VXY_DATA);
				}
			}

			// SXY Multiple
			if( this.evaluateDataTypeSXYMultiple(tokenList) )
			{
				ArrayList columnIndexList = getSXYMultipleTextColumnIndexList(tokenNum);
				if( indexList.equals( columnIndexList ) )
				{
					if( tokenNum>=3 )
					{
						cSet.add(SGDataTypeConstants.SXY_MULTIPLE_DATA);
					}

					if( tokenNum>=1 )
					{
						cSet.add(SGDataTypeConstants.SXY_SAMPLING_DATA);
					}
				}
			}

			// SXY Date
			if( this.evaluateDataTypeSXYDate( tokenList ) )
			{
				ArrayList columnIndexList = getSXYDateTextColumnIndexList(tokenNum);
				if( indexList.equals( columnIndexList ) )
				{
					cSet.add(SGDataTypeConstants.SXY_DATE_DATA);
				}
			}

			cList.addAll( cSet );
		}
		catch( FileNotFoundException ex )
		{
			throw ex;
		}
		finally
		{
			if( br!=null )
			{
				try
				{
					br.close();
				}
				catch( IOException ex )
				{
					
				}
			}
		}

		return true;
	}



	/**
	 * 
	 */
	private boolean evaluateDataTypeSXY( final ArrayList tokenList )
	{
		final int size = tokenList.size();

		ArrayList numberIndexList = new ArrayList();
		if( size==2 | size==4 )
		{
			for( int ii=0; ii<size; ii++ )
			{
				numberIndexList.add( new Integer(ii) );
			}
		}
		else if( size==3 )
		{
			numberIndexList.add( new Integer(0) );
			numberIndexList.add( new Integer(1) );
		}
		else if( size==5 )
		{
			for( int ii=0; ii<4; ii++ )
			{
				numberIndexList.add( new Integer(ii) );
			}
		}
		else
		{
			return false;
		}


		boolean b = true;
		for( int ii=0; ii<numberIndexList.size(); ii++ )
		{
			final int index = ((Integer)numberIndexList.get(ii)).intValue();
			String str = (String)tokenList.get(index);
			Double d = SGUtilityText.getDouble(str);
			if( d==null )
			{
				b = false;
				break;
			}
		}

		return b;
	}


	/**
	 * 
	 */
	private boolean evaluateDataTypeVXY( final ArrayList tokenList )
	{
		boolean b = true;
		final int size = tokenList.size();
		for( int ii=0; ii<size; ii++ )
		{
			String str = (String)tokenList.get(ii);
			Double d = SGUtilityText.getDouble(str);
			if( d==null )
			{
				b = false;
				break;
			}
		}

		return b;
	}



	/**
	 * 
	 */
	private boolean evaluateDataTypeSXYMultiple( final ArrayList tokenList )
	{
		boolean b = true;
		final int size = tokenList.size();
		for( int ii=0; ii<size; ii++ )
		{
			String str = (String)tokenList.get(ii);
			Double d = SGUtilityText.getDouble(str);
			if( d==null )
			{
				b = false;
				break;
			}
		}

		return b;
	}


	//
	private boolean evaluateDataTypeSXYDate( final ArrayList tokenList )
	{
		final int num = tokenList.size();
		if( num!=2 & num!=3 & num!=4 & num!=5 )
		{
			return false;
		}

		// date
		final ArrayList dList = new ArrayList();
		dList.add( tokenList.get(0) );

		final DateFormat df = DateFormat.getDateInstance();
		for( int ii=0; ii<dList.size(); ii++ )
		{
			final String str = (String)dList.get(ii);
			final Date d = SGUtilityText.getDate(str);
			if( d==null )
			{
				return false;
			}
		}


		// number
		final ArrayList nList = new ArrayList();
		nList.add( tokenList.get(1) );
		if( num==4 | num==5  )
		{
			nList.add( tokenList.get(1) );
			nList.add( tokenList.get(2) );
			nList.add( tokenList.get(3) );
		}

		for( int ii=0; ii<nList.size(); ii++ )
		{
			final String str = (String)nList.get(ii);
			final Double d = SGUtilityText.getDouble(str);
			if( d==null )
			{
				return false;
			}
		}

		return true;
	}


	/**
	 * Returns a list of tokens of the first line.
	 * @param br - a buffered reader
	 * @return a list of tokens
	 */
	private ArrayList getFirstTokenList( BufferedReader br )
	{
		ArrayList tokenList = new ArrayList();
		while( true )
		{
			String line = SGUtilityText.readLine(br);
			if( line == null )
			{
				break;
			}

			// tokenize the line
			tokenList.clear();
			SGUtilityText.tokenize( line, tokenList );
			final int size = tokenList.size();	// the number of tokens
			if( size!=0 )
			{
				break;
			}
		}

		return tokenList;
	}


}

