package org.postgresql.jdbc3;

import java.io.ByteArrayInputStream;
import java.sql.SQLException;
import java.util.StringTokenizer;
import java.util.Vector;

import Zql.ZDelete;
import Zql.ZExpression;
import Zql.ZFromItem;
import Zql.ZInsert;
import Zql.ZQuery;
import Zql.ZStatement;
import Zql.ZUpdate;
import Zql.ZqlParser;

public class ParserSQL {
	
	public Vector r;
	public Vector w;
	public Vector all;
	public char type; /* S select, U update, I insert, D delete, R drop,
	C create, A alter, B begin, E end, O abort, T other */
	
	public ParserSQL () {
		r = new Vector();
		w = new Vector();
		all = new Vector ();
		type = '0';
	}
	
	public Vector getR () {
		return r;
	}
	
	public Vector getW () {
		return w;
	}
	
	
	public Vector getAll () {
		return all;
	}
	
	public char getType () {
		return type;
	}
	
	public void setType (char type) {
		this.type = type;
	}
	
	
	public void clearTables () {
		if (r != null) {
			r.clear();
		}
		if (w != null) {
			w.clear();
		}
		if (all != null) {
			all.clear();
		}
		type = '0';
	}
	
	
	public void printTables () {
		int i;
		System.out.println();
		System.out.println("***************************************");
		if (r.size()!=0) {
			System.out.println ("Tables acceded to read:");
			for (i = 0; i < r.size(); i++) {
				System.out.println("   " + r.elementAt(i));
			}
		}
		else {
			System.out.println("No tables acceded to read");
		}
		
		if (w.size()!=0) {
			System.out.println("Tables acceded to write:");
			for (i = 0; i < w.size(); i++) {
				System.out.println("   " + w.elementAt(i));
			}
		}
		else {
			System.out.println("No tables acceded to write");
		}
		
		if (all.size()!=0) {
			System.out.println("Tables acceded to change database:");
			for (i = 0; i < all.size(); i++) {
				System.out.println("   " + all.elementAt(i));
			}
		}
		else {
			System.out.println("No tables acceded to change database");
		}
		System.out.println("TypeSQL = " + type);
		System.out.println("***************************************");
		System.out.println();
	}
	
	public void printTables (Vector v) {
		int i;
		if (v.size()!=0) {
			System.out.println("Tables:");
			for (i = 0; i < v.size(); i++) {
				System.out.println("   " + v.elementAt(i));
			}
		}
		else {
			System.out.println("No tables acceded");
		}
	}
	
	public int findTables (String sql) {
		int steps = 0;
		String t = new String();
		try {
			// Pruebas
			//String sql = "select * from tabla1 where campo1 in (select * from tabla2, tabla3);";
			//String sql = "insert into tabla1 (campo1) select * from tabla2;";
			//String sql = "delete from tabla1 where campo1 in (select * from tabla2);";
			//String sql = "update tabla1 set campo1 = 2 where campo1 in (select * from tabla2);";
			//String sql = "create table tabla2 (id int);";
			//String sql = "drop table tabla1;";
			//String sql = "create index indice on tabla1 (nombre asc);";
			ByteArrayInputStream is = new ByteArrayInputStream(sql.getBytes());
			ZqlParser p = null;      
			StringTokenizer tokens = new StringTokenizer(sql, " ");
			t = tokens.nextToken();
			
			//System.out.println("The statement is " + t);
			type = 'X';
			if ( (t.toUpperCase()).equals("SET")) {
				type = 'T';
				
			} else if ( (t.toUpperCase()).equals("CREATE") || (t.toUpperCase()).equals("ALTER")) {
				if ( (t.toUpperCase()).equals("CREATE")) {
					type = 'C';
				}
				else {
					type = 'A';
				}
				createDB(tokens);
				
			} else if ( (t.toUpperCase()).equals("DROP")) {
				type = 'R';
				dropDB(tokens);
				
			} else if ( (t.toUpperCase()).equals("BEGIN")) {
				type = 'B';
				
			} else if ( (t.toUpperCase()).equals("END")) {
				type = 'E';
				
			} else if ( (t.toUpperCase()).equals("ABORT")) {
				type = 'O';	
				
			} else {
				//System.out.println("ERROR?");
				p = new ZqlParser(is);
				//System.out.println("ERROR?");
				steps++;
				//System.out.println("ERROR?");
				// Read all SQL statements from input
				ZStatement st;
				//System.out.println("ERROR?");
				try{
					while ((st = p.readStatement()) != null) {
						
						//System.out.println("Begin Sentence sql : " + st.toString()); // Display the statement
						
						if (st instanceof ZQuery) { // An SQL query: query the DB
							//System.out.println("type = 'S'");
							type = 'S';                      
							queryDB( (ZQuery) st);
						}
						else if (st instanceof ZInsert) { // An SQL insert
							//System.out.println("type = 'I'");
							type = 'I';
							insertDB( (ZInsert) st);
						}
						else if (st instanceof ZDelete) { // An SQL delete
							//System.out.println("type = 'D'");	
							type = 'D';
							deleteDB( (ZDelete) st);
						}
						else if (st instanceof ZUpdate) { // An SQL Update
							//System.out.println("type = 'U'");
							type = 'U';
							updateDB( (ZUpdate) st);
						}
						//System.out.println("End Sentence sql : " + st.toString()); // Display the statement
					}
				}catch (Exception e) {
					if (type == 'X'){
						//System.out.println("SET PARSER ON MANUAL MODE: " + e.toString());
						type = t.toUpperCase().charAt(0);
						manual(tokens);//Manual add by jsalas
					} else {
						throw e;
					}
				}
				//repairRepeatTables(); DO IN THE REST OF METHODS//Add by jsalas
			}												
			
		}catch (Exception e) {
			if ((t.toUpperCase()).equals("SELECT") && (type != 'S')) {
				type = 'S';
			}
			if (steps == 0)
				return -1;
			/*System.out.println("ERROR");
			 e.printStackTrace();*/
		}
		return 0;
	}
	
	private void repairRepeatTables () {
		//r = new Vector();
		//w = new Vector();
		//all = new Vector ();
		
	}
	
	private void manual (StringTokenizer tokens) throws SQLException {
		String t;
		if (type == 'U'){
			t = tokens.nextToken();
			if (!w.contains(t)){
				w.add(t);
			}
		} else if (type == 'I'){
		//TODO	
		}
		while (tokens.hasMoreTokens()){
			t = tokens.nextToken();
			if (t.equals("SELECT")){
				boolean found = false;
				while ((tokens.hasMoreTokens() && !found)){
					t = tokens.nextToken();
					if (t.equals("FROM")){
						t = tokens.nextToken();
						if ((!r.contains(t)) && (!w.contains(t))){
							r.add(t);
						}						
					}
					if (!found){
						throw new SQLException("Unrecognize statement");
					}
				}
			}
		}
	}
	/**
	 * Query the database
	 */
	public void queryDB(ZQuery q) throws Exception {
		//System.out.println("queryDB 1");
		if (q != null){
			int i;
			//System.out.println("queryDB 2");
			Vector from = q.getFrom(); // FROM part of the query
			//System.out.println("queryDB 3");
			ZExpression where;
			//System.out.println("queryDB 4");			
			saveTables(from, r);
			//System.out.println("queryDB 5");
			where = (ZExpression) q.getWhere();
			//System.out.println("queryDB 6");
			if (where != null) {
				//System.out.println("queryDB 7");
				for (i = 0; i < where.nbOperands(); i++) {
					if (where.getOperand(i)instanceof ZQuery) {
						queryDB( (ZQuery) where.getOperand(i));
					}
				}
			}
		}
	}
	
	public void saveTables (Vector from, Vector v) {
		int i;
		for (i = 0; i < from.size(); i++) {
			ZFromItem table = (ZFromItem)from.elementAt(i);
			//v.add(table.toString());
			if (!v.contains(table.getTable())){
				v.add(table.getTable());
			}
		}
	}
	
	public void insertDB(ZInsert q) throws Exception {
		//System.out.println("insertDB 1");
		if (!w.contains(q.getTable())){
			//System.out.println("insertDB 2");
			w.add(q.getTable());
			//System.out.println("insertDB 3");
		}		
		//System.out.println("insertDB 4");
		queryDB(q.getQuery());
		//System.out.println("insertDB 5");
	}
	
	public void deleteDB(ZDelete q) throws Exception {
		ZExpression where;
		int i;
		if (!w.contains(q.getTable())){
			w.add(q.getTable());
		}
		where = (ZExpression) q.getWhere();
		if (where != null) {
			for (i = 0; i < where.nbOperands(); i++) {
				if (where.getOperand(i)instanceof ZQuery) {
					queryDB( (ZQuery) where.getOperand(i));
				}
			}
		}
	}
	
	public void updateDB(ZUpdate q) throws Exception {
		ZExpression where;
		int i;
		if (!w.contains(q.getTable())){
			w.add(q.getTable());
		}
		where = (ZExpression) q.getWhere();
		if (where != null) {
			for (i = 0; i < where.nbOperands(); i++) {
				if (where.getOperand(i)instanceof ZQuery) {
					queryDB( (ZQuery) where.getOperand(i));
				}
			}
		}
	}
	
	public void createDB (StringTokenizer tokens) {
		String t;		
		int numTokens;
		int readedTokens = 1;
		numTokens = tokens.countTokens() + 1;
		t = tokens.nextToken();
		readedTokens++;
		if ((t.toUpperCase()).equals("INDEX")) {
			while ((!(t.toUpperCase()).equals("ON"))&&(readedTokens < numTokens)) {
				t = tokens.nextToken();
				readedTokens++;
			}
			if (readedTokens < numTokens) {
				t = tokens.nextToken();
				if (!all.contains(t)){
					all.add(t);
				}
			}
		}
		else {
			while ((!(t.toUpperCase()).equals("TABLE"))&&(readedTokens < numTokens)) {
				t = tokens.nextToken();
				readedTokens++;
			}
			if (readedTokens < numTokens) {
				t = tokens.nextToken();
				if (!all.contains(t)){
					all.add(t);
				}
			}
		}
	}
	
	
	public void dropDB (StringTokenizer tokens) {
		int readedTokens = 1;
		String t;
		int numTokens = tokens.countTokens() + 1;
		t = tokens.nextToken();
		readedTokens++;
		while (readedTokens < numTokens) {
			t = tokens.nextToken(" ,");
			if (t.charAt(t.length()-1) == ';') {
				t = t.substring(0, t.length()-1);
			}			
			if (!all.contains(t)){
				all.add(t);
			}
			readedTokens++;
		}
	}
	
	static public String repairSQL (String sql) {
		StringTokenizer tokens = new StringTokenizer (sql);
		String t = new String();
		String result = new String();
		t = tokens.nextToken(" ");
		if ((t.toUpperCase()).equals("INSERT")) {
			t = tokens.nextToken(" ");
			result = "insert into ";
			result = result + tokens.nextToken(" (");
			result = result + " ";
			while (tokens.hasMoreTokens()) {
				result = result + tokens.nextToken(" ") + " ";
			}
			return result;
		}
		else {
			if ((t.toUpperCase()).equals("DELETE")) {
				t = tokens.nextToken(" ");
				result = "delete from ";
				while (tokens.hasMoreTokens()) {
					result = result + tokens.nextToken(" ") + " ";
				}
				return result;
			}
			else {
				if ((t.toUpperCase()).equals("UPDATE")) {
					result = "update ";
					while (tokens.hasMoreTokens()) {
						result = result + tokens.nextToken(" ") + " ";
					}
					return result;
				}
			}
		}
		return sql;
	}
};

