/*
 * @file: DetailReader.java
 * @author:  Zubin Girglani, Evelina Khukhashvili, Marie Shvartsapel
 * @course:  V22.0480.03, Visualization, Spring 2003
 * @date: March 2003
 * @synopsis:
 *	This is derived from Reader.java by Bansi Kotecha and Chee Yap.
 *		To understand the format used in this program, please
 *		read the comments in Reader.java.
 *	This reader is highly specialized to combine the
 *		information in Tiger RT1 and RT2 files.
 *	The goal is to produce a single path called "details"
 *		for each tiger line.  
 *	-- It assumes that the RT1.fmt and RT2.fmt files 
 *	   are stored in the same directory location.
 *	-- It assumes that the database bas been created(?)
 *
 */

import java.io.*;
import java.util.*;
import java.sql.*;

public class DetailReader extends RawDataReader {

    private final int fileExtLength = 3;
    private String frmtFile;	// e.g., RT1.fmt
    private String dataFile1;	// e.g., TGR34017.RT1
    private String rt2FmtFileName;
    private String rt2FileName;
    private String tableName;	// Extracted from dataFile, e.g., RT1
    private String[][] frmtdata;
    private int noOfAttributes;
    private int fieldsPerAttribute;	// This is a slight misnomer, as
    private int countRows = 0;
    private Statement stmt;
    private PreparedStatement pstmt;
    private char option;

    public DetailReader(String[] args) {
        getArguments(args);
        //tableName = getTableName(dataFile1);
        tableName = "rt12";
        rt2FmtFileName = getRT2FmtFile(frmtFile);
        rt2FileName = getRT2File(dataFile1);

        try {
            stmt = establishConnection().createStatement();
        } catch (SQLException se) {
            se.printStackTrace();
        }

        if ((option == 'c') || (option == 'C'))
            createTable();
        readFrmtFile(frmtFile);
        readRawDataFile1();
        readFrmtFile(rt2FmtFileName);
        readRawDataFile2();
    }


    public void getArguments(String args[]) {
        if (args[0].startsWith("-")) {
            // do something to get the option
            // this is temporary. There can me more than one options.
            option = args[0].charAt(1);
            this.frmtFile = args[1];
            this.dataFile1 = args[2];
        } else {
            this.frmtFile = args[0];
            this.dataFile1 = args[1];
        }
    }

    private String getTableName(String filename) {
        String ext = filename.substring(filename.length() - fileExtLength);
        return ext;
    }

    private String getRT2FmtFile(String filename) {
        String fmtFile = filename.substring(0, filename.length() - (fileExtLength + 2));
        fmtFile = fmtFile + "2.fmt";
        return fmtFile;
    }

    private String getRT2File(String filename) {
        String tigerFile = filename.substring(0, filename.length() - fileExtLength);
        tigerFile = tigerFile + "RT2";
        return tigerFile;
    }

    private void readFrmtFile(String formatFile) {
        int count = 0;
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(formatFile));
            String line;
            int i = 0;
            while ((line = br.readLine()) != null) {
                line = line.trim();
                int x = line.indexOf("#");
                if (x >= 0) line = line.substring(0, x).trim();
                if (line.length() == 0) continue;
                count++;
                if (count == 1) {
                    noOfAttributes = Integer.parseInt(line);
                } else if (count == 2) {
                    fieldsPerAttribute = Integer.parseInt(line);
                    frmtdata =
                            new String[noOfAttributes][fieldsPerAttribute];
                } else {
                    if (i < frmtdata.length) {
                        parseFrmtdata(line, i);
                        i++;
                    }	// excess lines are discarded
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseFrmtdata(String line, int i) {
        StringTokenizer st = new StringTokenizer(line, " \t");
        int j = 0;
        while (st.hasMoreTokens()) {
            frmtdata[i][j] = st.nextToken();
            j++;
        }
    }

    private void createTable() {
        try {
            String query = "create table " + tableName + "(";
            for (int i = 0; i < frmtdata.length; i++) {
                query += frmtdata[i][0] + " "
                        + getDataTypeAsString(frmtdata[i][1]);
                if (i == (frmtdata.length - 1))
                    query += ")";
                else
                    query += ", ";
            }

            //print("create query is : " + query);
            int i = stmt.executeUpdate(query);
            if (i == 1)
                print("Table " + tableName + " created successfully");
            else
                print("Error: Could not create table " + tableName);
        } catch (SQLException se) {
            se.printStackTrace();
        }
    }

    private String getDataTypeAsString(String given) {
        if (given.equals("N"))
            return "integer";
        if (given.equals("A"))	// Also need "C" for single character.
            return "text";
        if (given.equals("L"))
            return "point";	// Kludge: cannot have rtree index
        // on point data!  Only polygondata.
        if (given.equals("P"))
            return "path";
        return "";		// should be error
    }

    private int stringToInt(String value) {
        if (value.equals("")) {
            return -1;
        } else {
            return Integer.parseInt(value);
        }
    }

    /******************************************************************
     readRawDataFile is the main reading function
     -- it reads each line of dataFile and inserts into database table.
     ******************************************************************/
     
    //process file RT1 
    private void readRawDataFile1() {
    	countRows = 0;
        //print("readRawDataFile() called");
        try {
            BufferedReader br = new BufferedReader(
                    new FileReader(dataFile1));
            String record, query;
            String[] attributes;

            while ((record = br.readLine()) != null) {
                attributes = parseRecord(record);
                query = createQuery(attributes);
                insertIntoDatabase(query);
            }

            commit();
            print("\n" + countRows + " rows inserted from file " + dataFile1);


        } catch (Exception ie) {
            ie.printStackTrace();
        }


    }
    

    private void readRawDataFile2() {
        //do the same for RT2
        String record, query;
        String[] attributes;

	countRows = 0;
        try {
            BufferedReader br = new BufferedReader(new FileReader(rt2FileName));

            String temp, currentTLID = "", path;
            pstmt = con.prepareStatement("SELECT startpt, endpt FROM " + tableName + " where tlid = ?");

            record = br.readLine();
            attributes = parseRecord(record);
            currentTLID = attributes[0];
            path = attributes[2];
            
            while ((record = br.readLine()) != null) {
                attributes = parseRecord(record);
                if (attributes[0].equals(currentTLID)) {
                    path = addSequence(path, attributes[2]);
                } else {
                    //the record last read belongs to the nextTLID
                    //store attr for later use
                    temp = attributes[2];
                    //create and insert query for complete path
                    query = createQueryRT2(currentTLID, path);
                    insertIntoDatabase(query);
                    //move to the next TLID
                    currentTLID = attributes[0];
                    path = temp;
                }

            }
            //inserting the last one
            query = createQueryRT2(currentTLID, path);
            insertIntoDatabase(query);

            commit();
            print("\n" + countRows + " rows inserted from file " + rt2FileName);
        } catch (Exception ie) {
            ie.printStackTrace();
        }


        //insert start and end points into details field for those tlid, that were not mentioned in the RT2 file
        /*try {
            String startpt;
            String endpt;
            String tlid;

            ResultSet results = stmt.executeQuery("SELECT tlid, startpt, endpt FROM " + tableName + " where details is null");

            int resSize =results.getFetchSize();
            String[] queries = new String[resSize];

            int count = 0;
            while (results.next()) {
                startpt = results.getString("startpt");
                endpt = results.getString("endpt");
                tlid = results.getString("tlid");
                //startpt = startpt.substring(1, (startpt.length() - 1));
                //endpt = endpt.substring(1, (endpt.length() - 1));

                query = "update " + tableName + " set details = '" + startpt + "," + endpt + "' where tlid=" + tlid;
                //System.out.println("last query is " + query);
                //insertIntoDatabase(query);
                queries[count] = query;
                count++;
                //print("resultSet size (after insert)"+results.getFetchSize());
            }
            for (int i = 0; i < queries.length; i++) {
                //System.out.println("updating query number " + i + "out of " + queries.length);
                insertIntoDatabase(queries[i]);
            }
            commit();
        } catch (java.sql.SQLException e) {
            System.out.println("Sql exception occurred: " + e);
            System.exit(0);
        }*/


    }

    private String addSequence(String path, String sequence) {
	//when reading detail points for the path with sequense number greater than 1, we need to ass them to existing path
        return path + ", " + sequence;
    }

    private String[] parseRecord(String record) {
        String[] attributes = new String[frmtdata.length];
        String type;

        for (int i = 0; i < frmtdata.length; i++) {
            type = frmtdata[i][1];
            if (type.equals("N")) {
                attributes[i] = stringToN(record, i);
            } else if (type.equals("A")) {
                attributes[i] = stringToA(record, i);
            } else if (type.equals("L")) { // assume equals("L"), lat/lon
                attributes[i] = stringToL(record, i);
            } else if (type.equals("P")) {
                attributes[i] = stringToP(record, i);
            }
        }
        return attributes; //attributes are string data representation
        //ready to go into DB
    }

    private String stringToN(String record, int i) {
        //to convert to type N just
        String result;

        result = record.substring(Integer.parseInt(frmtdata[i][2]) - 1,
                Integer.parseInt(frmtdata[i][3])).trim();
        return result;
    }

    private String stringToA(String record, int i) {
        //to convert to type A single quotes need to be added
        String result;

        result = record.substring(Integer.parseInt(frmtdata[i][2]) - 1,
                Integer.parseInt(frmtdata[i][3])).trim();
        return result;
    }

    private String stringToL(String record, int i) {
        //to convert to type L we need to extract Long, Lat and form a point
        String lonlat, result;
        lonlat = record.substring(Integer.parseInt(frmtdata[i][2]) - 1, Integer.parseInt(frmtdata[i][3]));

        return formPoint(lonlat);
    }

    private String stringToP(String record, int i) {
    //parses the record into pairs of long, lat, forming valid Postgress representation for a path /polygon 
        int beginIndex = 0;
        String path, result = "", point;
        String[] points = new String[10];

        path = record.substring(Integer.parseInt(frmtdata[i][2]) - 1, Integer.parseInt(frmtdata[i][3]));

        int k = 0;
        do {
            points[k] = path.substring(beginIndex, beginIndex + 19);
            point = formPoint(points[k]);
            if (!point.equals("") && k != 0) {
                result += ",";
            }
            result += point;
            beginIndex += 19;
            k++;
        } while (!point.equals("") && k < 10);

        return result;
    }

    private String formPoint(String longlat) {
        String lon, lat;
        //print("form point- longlat"+longlat);
        //we know that first 10 chars in this piece represent Lon and second 9 - Lat
        lon = longlat.substring(0, 10);     //first 10 chars
        lat = longlat.substring(10);         //starting from 11th(first is 0) till the end
        lon = lon.trim();
        lat = lat.trim();
        lon = removeLeadingPlus(lon);
        lat = removeLeadingPlus(lat);
        if (Integer.parseInt(lon) == 0 && Integer.parseInt(lat) == 0)
            return "";
        else
            return "(" + lon + ", " + lat + ")";
    }

    /******************************************************************
     Quoting all roque characters in the raw input files: \'
     ******************************************************************/
    private String quoteCharacters(String value) {
        StringBuffer sb = new StringBuffer(value);
        for (int j = 0; j < sb.length(); j++) {
            if (sb.charAt(j) == '\'') {
                sb.insert(j - 1, '\'');
            }
        }
        return sb.toString();
    }

    private String createQuery(String[] attributes) {
        /*for (int i = 0; i < attributes.length; i++) {
            System.out.println(attributes[i]);
        } */
        String query = "";

        query += "insert into " + tableName + " values (";

        for (int i = 0; i < frmtdata.length; i++) {
            query += "'" + attributes[i] + "'";

            if (i != frmtdata.length - 1) {
                query += ", ";
            }

        }
        //this breaks the generality of the query, but allows for more efficiency by inserting start and end points into details column
        query += ", '" + attributes[1] + "," + attributes[2] + "'";
        query += ")";
        //System.out.println("The query is " + query);
        return query;
    }

    private String createQueryRT2(String tlid, String path) {
        //for(int i = 0; i < attributes.length; i++)
        //{
        //	System.out.println(attributes[i]);
        //}

        String query = "";
        String startpt = "";
        String endpt = "";

        try {
            pstmt.setInt(1, Integer.parseInt(tlid));
            //execute select
            ResultSet results = pstmt.executeQuery();
            if (results.next()) {
                startpt = results.getString("startpt");
                endpt = results.getString("endpt");
            }
            //print("startpt " + startpt);
            //startpt = startpt.substring(1, (startpt.length() - 1));
            //endpt = endpt.substring(1, (endpt.length() - 1));
            results.close();
        } catch (java.sql.SQLException e) {
            System.out.println("Sql exception occurred: " + e);
            System.exit(0);
        }
        query = "update " + tableName + " set details = '" + startpt + "," + path + "," + endpt + "'";
        query += " where tlid=" + tlid;
        //System.out.println("The query is " + query);
        return query;
    }


    private String removeLeadingPlus(String ll) {
        if (ll.charAt(0) == '+')
            return ll.substring(1, ll.length());
        return ll;
    }

    public void insertIntoDatabase(String query) {
        try {
            int i = stmt.executeUpdate(query);
            if (i == 1) {
                countRows++;
                if ((countRows % 300) == 0)
                    print("Row " + countRows + " successfully inserted");
            } else
                print("could not complete insertion");
        } catch (Exception se) {
            se.printStackTrace();
        }
    }

    /******************************************************************
     The rest of the code are for testing
     ******************************************************************/

    private void printMatrix() {
        for (int i = 0; i < frmtdata.length; i++) {
            for (int j = 0; j < frmtdata[0].length; j++) {
                print("frmtdata[" + i + "][" + j + "] : " + frmtdata[i][j]);
            }
        }
    }

    private void print(String s) {
        System.out.println(s);
        System.out.flush();
    }

    //debugging constructor
    public DetailReader(String[] args, boolean b) {
        getArguments(args);
        tableName = getTableName(dataFile1);
        readFrmtFile(frmtFile);
        // printMatrix();
        readRawDataFile(true);
    }

    //debugging readRawDataFile (do not actually insert)
    private void readRawDataFile(boolean b) {
        try {
            BufferedReader br = new BufferedReader(new FileReader(dataFile1));
            String record, query;
            String[] attributes;
            while ((record = br.readLine()) != null) {
                attributes = parseRecord(record);
                query = createQuery(attributes);
                print("Query: " + query);
                if (countRows++ > 20) break;
            }
            print("\n" + countRows + " rows inserted from file " + dataFile1);
        } catch (Exception ie) {
            ie.printStackTrace();
        }

    }

    /******************************************************************
     The main program
     ******************************************************************/
    public static void main(String args[]) {

        // Reader r = new Reader(args, true);	// debug version
        DetailReader r = new DetailReader(args);
    }
}

