package profileEngine;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import dataWarehousingTools.Configuration;
import dataWarehousingTools.DatabaseLogin;
import dataWarehousingTools.LogFile;

public class ProfileEngine
{	
	public static void main(String[] args)
	{
		Configuration configuration;
		LogFile logFile;
		Connection metadataConnection;
		Connection dataConnection;
		LoginData loginData;
		String connectionName = "";
		DatabaseMetaData databaseMetaData;
		String databaseProduct = null;
    	String databaseVersion = null;
    	DatabaseSchemaTableList databaseSchemaTableList;
		String databaseName = "";
		String schemaName = "";
		String tableName = "";
		String partsInName = "2";
		String encoding = "UNKNOWN";
		ColumnList columnList;
		Profile profile;
		Metadata metadata;
		Calendar dateStatisticsCollected;
		Calendar dateTimeRunEnded;
		long runTimeSeconds;
		int hours;
		int minutes;
		int seconds;
		NumberFormat numberFormat;
		SimpleDateFormat dateFormat;

		
		DatabaseLogin databaseLogin;
		String configurationFile = null;
		
		if (args.length < 2)
		{
			System.out.println("");
			System.out.println("Usage: ProfileEngine configuration connection [database [schema [table]]]");
			System.out.println("");
			System.out.println("    configuration: path of configuration file, XML file, always needed");
			System.out.println("    connection:    connection name in connections table, always needed");
			System.out.println("    database:      name of database to be profiled, needed if schema specified");
			System.out.println("    schema:        name of schema to be profiled, needed if table specified");
			System.out.println("    table:         name of table to be profiled");
			System.out.println("    parts_in_name: Optional: 3 means database.schema.table; 2 means schema.table; 1 means table only"); 
			System.out.println("                   This only seems to apply to Microsoft SQL Server which behaves inconsistently");
			System.out.println("                   when selecting from tables to get the profile results.  ");
			System.out.println("");
			System.out.println("If only configuration and connection are specified, list databases on this connection.");
			System.out.println("If only configuration, connection and database are specified, list schemas in this database.");
			System.out.println("If only configuration, connection, database and schema are specified, list tables in this database schema, with row counts.");
			System.out.println("If all 5 parameters, including table, are specified, create detailed profile of the table; store results in the database specified in the configuration file.");
			System.out.println("");
			System.out.println("Documentation: https://www.thedatastudio.net/data_profiler_documentation.htm");
			System.exit(1);
		}
		if (args.length > 0)		
			configurationFile = new String(args[0]);
		if (args.length > 1)
			connectionName = new String(args[1]);
		if (args.length > 2)
			databaseName = new String(args[2]);
		if (args.length > 3)
			schemaName  = new String(args[3]);
		if (args.length > 4)
			tableName = new String(args[4]);
		if (args.length > 5)
			partsInName = new String(args[5]);
		
		System.out.println("Configuration file: " + configurationFile);
		
		databaseLogin = new DatabaseLogin(configurationFile);		
		metadataConnection = databaseLogin.getConnection();

		configuration = new Configuration(databaseLogin.getConfigurationName(), metadataConnection, true);	//true means Profile-only, no data quality monitor
		
		System.out.println("Configuration: " + configuration.getConfigurationName());

		logFile = new LogFile(configuration, "ProfileEngine_");
		logFile.logMessage("Configuration: " + configuration.getConfigurationName());
		
		try
		{
			loginData = new LoginData(metadataConnection, logFile);		
			dataConnection = loginData.getConnection(connectionName);
			dateStatisticsCollected = Calendar.getInstance();

			try 
			{
				databaseMetaData = dataConnection.getMetaData();
				databaseProduct = databaseMetaData.getDatabaseProductName();
				databaseVersion = databaseMetaData.getDatabaseProductVersion();
				System.out.println("Database product: " + databaseProduct);
				System.out.println("Database version: " + databaseVersion);
//				if (databaseMetaData.generatedKeyAlwaysReturned())
//					System.out.println("Generated Keys Always Returned: true");
//				else
//					System.out.println("Generated Keys Always Returned: false");
			} 
			catch (SQLException e1) 
			{
				logFile.logMessage(e1.getMessage());
				logFile.logMessage(e1.getStackTrace().toString());
			}
			
			databaseSchemaTableList = new DatabaseSchemaTableList();
			
			if (connectionName.equals("Self"))
				logFile.logMessage("Data is in same database as metadata");
			
			if (databaseName.isEmpty())
			{
				if (configuration.getConfigurationName().equals("Self"))
					new DatabaseList(metadataConnection, databaseProduct, databaseSchemaTableList);
				else
					new DatabaseList(dataConnection, databaseProduct, databaseSchemaTableList);
				databaseSchemaTableList.print("database");
				System.exit(0);
			}
			
			if (connectionName.equals("Self"))
				new DatabaseEncoding(metadataConnection, databaseProduct, connectionName);
			else
				new DatabaseEncoding(dataConnection, databaseProduct, connectionName);
			encoding = new String(DatabaseEncoding.getEncoding());
			
				
//			if (schemaName.isEmpty())
//			{
//				if (databaseProduct.equals("Microsoft SQL Server"))
//				{
//					System.out.println("No schema name given; assume \"dbo\""); 
//					schemaName = new String("dbo");
//				}
//				else if (databaseProduct.equals("PostgreSQL"))
//				{
//					System.out.println("No schema name given; assume \"public\""); 
//					schemaName = new String("public");
//				}
//				else
//				{
//					System.out.println("Specify schema name to get list of tables");
//					System.exit(0);
//				}
//			}
			if (schemaName.isEmpty())
			{
				if (connectionName.equals("Self"))
					new SchemaList(metadataConnection, databaseProduct, databaseName, databaseSchemaTableList);
				else
					new SchemaList(dataConnection, databaseProduct, databaseName, databaseSchemaTableList);
				databaseSchemaTableList.print("schema");
				System.exit(0);
			}
			
			if (tableName.isEmpty())
			{
				if (connectionName.equals("Self"))
					new TableList(metadataConnection, databaseProduct, databaseName, schemaName);					
				else
					new TableList(dataConnection, databaseProduct, databaseName, schemaName);
			}
			else
			{
				metadata = new Metadata(metadataConnection, databaseProduct);
				metadata.insertTableProfile(databaseName, schemaName, tableName, dateStatisticsCollected);
				logFile.logMessage("Database: " + databaseName + ", Schema: " + schemaName + ", Table: " + tableName);
				if (connectionName.equals("Self"))
				{
					columnList = new ColumnList(databaseProduct, metadataConnection, databaseName, schemaName, tableName, metadata, dateStatisticsCollected);
					profile = new Profile(databaseProduct, metadataConnection, databaseName, schemaName, tableName, partsInName, columnList, metadata, logFile);
					new Frequency(databaseProduct, metadataConnection, databaseName, schemaName, tableName, partsInName, profile.getRowCount(), columnList, metadata, logFile);
					new NullCount(databaseProduct, metadataConnection, databaseName, schemaName, tableName, partsInName, columnList, metadata, logFile);
					new Pattern(databaseProduct, metadataConnection, databaseName, schemaName, tableName, partsInName, columnList, metadata, encoding, logFile);	
				}
				else
				{
					columnList = new ColumnList(databaseProduct, dataConnection, databaseName, schemaName, tableName, metadata, dateStatisticsCollected);
					profile = new Profile(databaseProduct, dataConnection, databaseName, schemaName, tableName, partsInName, columnList, metadata, logFile);					
					new Frequency(databaseProduct, dataConnection, databaseName, schemaName, tableName, partsInName, profile.getRowCount(), columnList, metadata, logFile);
					new NullCount(databaseProduct, dataConnection, databaseName, schemaName, tableName, partsInName, columnList, metadata, logFile);
					new Pattern(databaseProduct, dataConnection, databaseName, schemaName, tableName, partsInName, columnList, metadata, encoding, logFile);	
				}
			}
			
			dateTimeRunEnded = Calendar.getInstance();
			
	    	dateFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
			System.out.println("Start Time: " + dateFormat.format(dateStatisticsCollected.getTime()));
			System.out.println("End Time: " + dateFormat.format(dateTimeRunEnded.getTime()));
			
			runTimeSeconds = (dateTimeRunEnded.getTimeInMillis() - dateStatisticsCollected.getTimeInMillis()) / 1000;
			hours = (int)runTimeSeconds / 3600;
			minutes = (int)(runTimeSeconds - (hours * 3600)) / 60;
			seconds = (int)((runTimeSeconds - (hours * 3600)) - minutes * 60);
			
	    	numberFormat = NumberFormat.getInstance();
	    	numberFormat.setMinimumIntegerDigits(2);
			
			System.out.println
			(
				"Elapsed Time: " + 
				numberFormat.format(hours) + 
				":" + 
				numberFormat.format(minutes) + 
				":" + 
				numberFormat.format(seconds)
			);

			try 
			{
				dataConnection.close();
			} 
			catch (SQLException e) 
			{
				e.printStackTrace();
			}
		}
		finally
		{
			logFile.Close();
		}
	}
}
