package dqMonitor;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class QueryFile 
{
    private FileReader file;
    private LogFile logFile;
    private StringBuffer queryNoComments;
    private StringBuffer cleanQuery;

    
    public QueryFile(String fileName, LogFile logFile)
    {
        this.logFile = logFile;
        try 
        {
            file = new FileReader(fileName);
        } 
        catch (IOException e) 
        {
            logFile.logMessage(e.toString());
        }
    }
    
    public String getQuery()
    {
        BufferedReader reader = null;
        String line;
        StringBuffer queryFromFile;
        
        queryFromFile = new StringBuffer();
        
        try 
        {
            reader = new BufferedReader(file);
            
            line = reader.readLine();
            while (line != null)
            {
                queryFromFile.append(line);
                queryFromFile.append("\n");
                line = reader.readLine();
            }
        } 
        catch (IOException e) 
        {
            logFile.logMessage(e.toString());
        }
        finally
        {
            if (reader != null) 
            {
                try 
                {
                    reader.close();
                } 
                catch (IOException e) 
                {
                    logFile.logMessage(e.toString());
                }
            }
        }
        logFile.logMessage("Query from file:\n" + queryFromFile.toString());
        if (validateAndCleanQuery(queryFromFile))
        {
            return cleanQuery.toString();            
        }
        else
        {
            return "";
        }
    }
    
    public void close()
    {
        try 
        {
            file.close();
        } 
        catch (IOException e) 
        {
            logFile.logMessage(e.toString());
        }
    }

    private boolean validateAndCleanQuery(StringBuffer queryFromFile)
    {
        StringBuffer token;
        int i;
        boolean beforeToken;
        boolean firstToken;
        boolean tokenFound;
        boolean endOfLineFound;
        boolean endOfCommentFound;
        boolean selectFound;
        boolean dataChangeCommandFound;
        boolean lastCharWasWhitespace;
        char c = ' ';
        char cNext;
        
        
        cleanQuery = new StringBuffer();
        
        queryNoComments = new StringBuffer();
        firstToken = true;
        selectFound = false;
        dataChangeCommandFound = false;
        i = 0;

        beforeToken = true;
        // Ignore whitespace before token
        while((beforeToken) && (i < queryFromFile.length()))
        {
            c = queryFromFile.charAt(i);
            {
                if(Character.isWhitespace(c))
                {
                    i++;
                }
                else
                {
                    beforeToken = false;
                }
            }
        }
        
        lastCharWasWhitespace = false;
        while(i < queryFromFile.length())
        {
            // look for "--" comment and skip to end of line if found
            if (i < queryFromFile.length() - 1)
            {
                c = queryFromFile.charAt(i);
                cNext = queryFromFile.charAt(i + 1);
                if ((c == '-') && (cNext == '-'))
                {
                    i += 2;
                    endOfLineFound = false;
                    while (!endOfLineFound)
                    {
                        if (i >= queryFromFile.length())
                        {
                            endOfLineFound = true;
                        }
                        else 
                        {
                            c = queryFromFile.charAt(i);
                            if (c == '\n')
                            {
                                endOfLineFound = true;
                            }
                        }
                        i++;
                    }
                }
                else if ((c == '/') && (cNext == '*'))
                {
                    i += 2;
                    endOfCommentFound = false;
                    while (!endOfCommentFound)
                    {
                        if (i >= queryFromFile.length() - 1)
                        {
                            endOfCommentFound = true;
                        }
                        else 
                        {
                            c = queryFromFile.charAt(i);
                            cNext = queryFromFile.charAt(i + 1);
                            if ((c == '*') && (cNext == '/'))
                            {
                                endOfCommentFound = true;
                                i += 2;
                            }
                        }
                        i++;
                    }
                }
                else 
                {
                    if (Character.isWhitespace(c))
            		{
                    	if (lastCharWasWhitespace)
                    	{
                    		i++;
                    	}
                    	else
                    	{
                    		lastCharWasWhitespace = true;
                   		    queryNoComments.append(' ');
                    		i++;
                    	}
            		}
                    else
                    {
                    	lastCharWasWhitespace = false;
                		queryNoComments.append(c);
                		i++;
                    }
                }
            }
            else
            {
                i += 2;
            }
        }
        
        logFile.logMessage("Query with comments stripped and multiple whitespace compressed to one space:");
        logFile.logMessage(queryNoComments.toString());

 //  **** Start with queryNoConmments ****   
        i = 0;
        while(i < queryNoComments.length())
        {
            token = new StringBuffer();
            
            // Get first/next token 
            tokenFound = false;
            while((!tokenFound) && (i < queryNoComments.length()))
            {
                c = queryNoComments.charAt(i);
                if (Character.isWhitespace(c))
                {
                    tokenFound = true;
                    cleanQuery.append(c);
                }
                else if (c == ';')
                {
                    tokenFound = true;
                    if (i < queryNoComments.length() - 1)
                    {
                        logFile.logMessage((queryNoComments.length() - i) + " characters found after end of query"); 
                        i = queryNoComments.length();
                    }
                }
                else if
                (
                    (c == ',') ||
                    (c == '(') ||
                    (c == ')') ||
                    (c == '=') ||
                    (c == '<') ||
                    (c == '>') ||
                    (c == '!') ||
                    (c == '+') ||
                    (c == '-') ||
                    (c == '*') ||
                    (c == '/') ||
                    (c == '%') ||
                    (c == '&') ||
                    (c == '|') ||
                    (c == '^') ||
                    (c == '~') ||
                    (c == '\'') ||
                    (c == '"') ||
                    (c == '\\') 
                )
                {
                    tokenFound = true;
                    cleanQuery.append(c);
                }
                else
                {
                    token.append(c);
                    cleanQuery.append(c);
                }
                i++;
            }
            
            // First token must be "select"
            if (firstToken)
            {
                if (token.toString().equalsIgnoreCase("select"))
                {
                    selectFound = true;
                }
                if (!token.toString().isEmpty())
                {
                    firstToken = false;
                }
            }

            //  Any other data manipulation statements are not allowed
            if 
            (
                token.toString().equalsIgnoreCase("insert") ||
                token.toString().equalsIgnoreCase("update") ||
                token.toString().equalsIgnoreCase("delete") ||
                token.toString().equalsIgnoreCase("alter") ||
                token.toString().equalsIgnoreCase("truncate") ||
                token.toString().equalsIgnoreCase("create") ||
                token.toString().equalsIgnoreCase("drop") ||
                token.toString().equalsIgnoreCase("load") ||       // Hive
                token.toString().equalsIgnoreCase("import") ||     // Hive
                token.toString().equalsIgnoreCase("export") ||     // Hive
                token.toString().equalsIgnoreCase("copy")          // PostgreSQL
            )
            {
                dataChangeCommandFound = true;
                logFile.logMessage
                (
                    "Only select statements are allowed in data quality queries.  Found " + 
                    token.toString() + 
                    " at position " + 
                    (i - token.length())
                );
            }
        }
        if (dataChangeCommandFound)
        {
            return false;
        }
        if (selectFound)
        {
            logFile.logMessage("Cleaned query: " + cleanQuery);
            return true;
        }
        else
        {
        	logFile.logMessage("Query must start with \"select\"");
            return false;
        }
    }
}
