/*
 */

package usda.weru.weps;

import de.schlichtherle.io.File;
import de.schlichtherle.io.FileReader;
import java.awt.*;

import java.io.BufferedReader;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;

import usda.weru.util.*;

/**
 * This class builds the Maps for each county for the eventually drawing the country map
 * and showing all the wind & climate generation stations for identification purpose.
 * @author  manmohan
 */
public class CountyMap extends Polygon implements java.util.Comparator {

        /**
         * Factor used to scale the map for initial display on the screen.
         */
	public	final	static	float	scaleFactor = 100000.0f;
        public final static double cushion = 0.01 * scaleFactor;
	String	name;
	String	state;
	String	stateId;
	int		stateNum;
	double	distance = 0.0;
	float	lat = 0.0f;
	float	lon = 0.0f;
	String	latitude;
	String	latitudeSign	= "+";
	String	longitude;
	String	longitudeSign	= "-";

        /**
         * Default no argument constructor.
         */
	public CountyMap () {
    }

        /**
         * The three argument constructor that that builds the county map object
         * based on the state and county name information with data retrieved using the 
         * appropriate hashtable key and object element.
         *@param inputCounty The name of county whose map is to be drawn
         *@param inputState The name of state in which above mentioned county whose
         * map is to be drawn is located.
         *@param ht The hashtable that links the county and state information to
         * extract the boundary information on its location as similar named counties
         * may exist in multiple states. 
         */
	public CountyMap(String inputCounty, String inputState, Hashtable ht) {
		name = inputCounty.substring(1,inputCounty.length()-1);
		name = Util.initialToUpperCase(name);
//		this.state = new String(stateId);
		stateId = inputState.substring(1,3);
		try {
			stateNum = Integer.parseInt(stateId);
		} catch (Exception e) {
			stateNum = 0;
		}
		StateMap sm = (StateMap) ht.get(stateId);
		this.state = sm.name;
		sm.countyVec.add(this);
	}

        /**
         * The file that stores all the latitude & longitude intersection points 
         * along the border of that county within and on the border of that state 
         * if any to mark its boundary.
         *@param ht The hashtable object that stores the objects with all these 
         *value points to be used for drawing the county borders.
         *@return The vector that contains all the points as objects.
         */
	public static Vector loadFile(Hashtable ht) {

		Vector mapVec = new Vector();

		String temp,outString;

		try {
			BufferedReader in = new BufferedReader(new FileReader(new File("data", "counties.txt").getCanOrAbsFile()));

			CountyMap cm = null;

			while ((temp=in.readLine()) != null) {
				StringTokenizer st = new StringTokenizer(temp, ",");
				if (st.countTokens() == 2) {			// point line
					float lonf = (new Float(st.nextToken())).floatValue() * scaleFactor; // move decimal point 5 places
					int lon = (int) lonf;
					float latf = (new Float(st.nextToken())).floatValue() * scaleFactor;// move decimal point 5 places
					int lat = (int) latf;
					cm.addPoint(lon,lat);
				} else if (st.countTokens() == 3) {		// county line
					String stid = st.nextToken().trim();
					String ctid = st.nextToken().trim();
					String linCnt = st.nextToken().trim();
					cm = new CountyMap(ctid, stid, ht);
					mapVec.add(cm);
				} else if (st.countTokens() != 0) {								// invalid line
					//System.err.println("CSC: invalid line " + st.countTokens() + " " + temp);
				}
			}
//            out.close();
			in.close();
		}catch (Exception e) {
			//System.err.println("CM_lF: " + e.toString());
			e.printStackTrace();
		}

		return mapVec;
	}

        /**
         * Converts the object to a string object and returns it.
         * @return String The new string converted object.
         */
    @Override
	public String toString() {
		return name;
	}
/**
 * This method fetches the countyMap vector that holds the latitude & longitude 
 * intersection points for drawing that county's mapped boundary.
 *@param lons The longitudes that runs through the referenced county.
 *@param lats The latitudes that runs through the referenced county.
 *@param longSign The sign of the longitude that is being referred to indicating East(+ve)/West(-ve).
 *@param latSign The sign of the latitude that is being referred to indicating North(+ve)/South(-ve).
 *@param mapVec The vector that holds the objects consisting the boundary points for the county map
 * to be drawn.
 */
	static CountyMap getCounty(String lons, String lats, String longSign, String latSign, Vector mapVec) {
		if (lons.trim().length() == 0) {
            return null;
        }
		if (lats.trim().length() == 0) {
            return null;
            ////System.out.println("CM_gC: " + mapVec.size());
            ////System.out.println("CM_gC: " + lons + " " + lats);
        }
            ////System.out.println("CM_gC: " + mapVec.size());		
            ////System.out.println("CM_gC: " + lons + " " + lats);		
             
                try{
		float lonf = (new Float(lons)).floatValue() * scaleFactor; // move decimal point 5 places
		float latf = (new Float(lats)).floatValue() * scaleFactor; // move decimal point 5 places
                
                ////System.out.println("CM_gC: " + lonf + " " + latf);		
		CountyMap cm = null;
                
		for (java.util.Iterator mv = mapVec.iterator(); mv.hasNext();) {
			cm = (CountyMap) mv.next();
            //if (cnt++ < 10) //System.out.println("CM_gC: " + cm.xpoints[0] + " " + lonf);
                        
            /** Taking into consideration the signs i.e East or West Longitude and North or South Latitude.
              * West Longitude means "-" Sign and East Longitude means "+" Sign
              * North Latitude means "+" Sign and South Latitude means "-" Sign
             */
          
             if(longSign.equals("W")){
                if(latSign.equals("N")){
                    if (cm.contains(-lonf,latf)) {
                        
			////System.out.println("CSC: " + lonf + " " + latf + " " + cm.name);
			return cm;
		    }            
               }else{
                    if (cm.contains(-lonf,-latf)) {
                        return cm; 
                    }            
                }
                                
           }else{
               // East "+" Longitude Sign
              if(latSign.equals("N")){
                  if (cm.contains(+lonf,+latf)) {
                      return cm; 
                  }            
              }else{
                 if (cm.contains(+lonf,-latf)) {
                     return cm; 
                 }            
              }
          }//end of if-else block
        }
                }
                catch(Exception e){
                    //Sometimes a float parse exception causes weps to crash.
                    e.printStackTrace();
                    return null;
                }
		return null;
	}

    /**
     * This method fetches the countyMap vector that holds the floats with latitude & longitude 
     * intersection points for drawing that county's mapped boundary on the country's map.
     *@param lonf The float value of longitudes that runs through the referenced county.
     *@param latf The float value of latitudes that runs through the referenced county.
     *@param mapVec The vector that holds the objects consisting the boundary points for the county map
     *to be drawn.
     */
	static CountyMap getCounty(float lonf, float latf, Vector mapVec) {
////System.out.println("CM_gC: " + mapVec.size());		
		CountyMap cm = null;		
int cnt = 0;
		for (java.util.Iterator mv = mapVec.iterator(); mv.hasNext();) {
			cm = (CountyMap) mv.next();
if (cnt++ < 10) ////System.out.println("CM_gC: " + cm.xpoints[0]);
			if (cm.contains(lonf*100000,latf*100000)) {
				////System.out.println("CSC: " + lonf + " " + latf + " " + cm.name);
				return cm;
			}
		}
		return null;
	}

	static int errcnt = 0;
	

        /**
         * Tests if a point is safely inside this polygon to avoid hit errors due to rounding.
         * A square the height and width of the cushion is centered around the given point.
         * Each corner of the cushion square must be contained in the polygon.
         * @param x x cordinate of the point to test
         * @param y y cordinate of the point to test
         * @param cushion amount of space to require a positive test around the point
         * @return true if each point of the cushion square is inside the polygon
         */                       
        private boolean contains(double x, double y, double cushion){            
            boolean test1 = contains (x - cushion/2, y - cushion/2);
            if (!test1) return false;
            boolean test2 = contains (x + cushion/2, y - cushion/2);
            if (!test2) return false;
            boolean test3 = contains (x + cushion/2, y + cushion/2);
            if (!test3) return false;
            boolean test4 = contains (x + cushion/2, y + cushion/2);
            if (!test4) return false;            
            return (test1 && test2 && test3 && test4);
        }
        
        
    /**
     * This method sets the latitudes and longitudes for the zoom level needed by the
     * visible screen while still focusing on the selected section.
     */
	void setLonLat() {
		if (npoints == 0) return;
		Rectangle rect = this.getBounds();                
		if (this.contains(rect.x + rect.width/2, rect.y + rect.height/2, cushion)) {
			lon = rect.x + rect.width/2;
			lat = rect.y + rect.height/2;
		} else if (this.contains(rect.x + rect.width/4, rect.y + rect.height/2, cushion)) {
			lon = rect.x + rect.width/4;
			lat = rect.y + rect.height/2;
			////System.out.println("CSC: center out of county 1 " + this.name + " " + this.state);
		} else if (this.contains(rect.x + 3*rect.width/4, rect.y + rect.height/2, cushion)) {
			lon = rect.x + 3*rect.width/4;
			lat = rect.y + rect.height/2;
			////System.out.println("CSC: center out of county 2 " + this.name + " " + this.state);
		} else if (this.contains(rect.x + rect.width/2, rect.y + rect.height/4, cushion)) {
			lon = rect.x + rect.width/2;
			lat = rect.y + rect.height/4;
			////System.out.println("CSC: center out of county 3 " + this.name + " " + this.state);
		} else if (this.contains(rect.x + rect.width/2, rect.y + 3*rect.height/4, cushion)) {
			lon = rect.x + rect.width/2;
			lat = rect.y + 3*rect.height/4;
			////System.out.println("CSC: center out of county 4 " + this.name + " " + this.state);
		} else {
			for (int idx = 0; idx < npoints; idx++) {
				if (this.contains((double) xpoints[idx], (double) ypoints[idx], cushion)) {
					lon = xpoints[idx];
					lat = ypoints[idx];
					break;
				}
			}
			////System.out.println("CSC: center out of county 6!!!! " + this.name + " " + this.state);
		}
		lon /= scaleFactor;
		lat /= scaleFactor;
		java.text.NumberFormat nf = java.text.NumberFormat.getInstance(java.util.Locale.US);
		nf.setMinimumFractionDigits(2);
		nf.setMaximumFractionDigits(2);
		longitude = nf.format((double) Math.abs(lon));
		longitudeSign = (lon < 0.0) ? "-" : "+";
		latitude = nf.format((double) Math.abs(lat));
		latitudeSign = (lat < 0.0) ? "-" : "+";
	}

    /**
     * This method compares the two objects for equality and returns an integer value
     * based on the result of comparison being passed as arguments to 
     * the method. 
     *@param arg1 The first object will be a CountyMap object whose name will
     * be used for comparison. 
     *@param arg2 The first object could be a CountyMap or a string whose name will
     * be used for comparison..
     *@return int Returns "zero" if both the objects have the same name string else
     * -ve if first has smaller name and +ve if bigger than the other. 
     */
	public int compare(Object arg1, Object arg2) {
		if (arg1 instanceof CountyMap && arg2 instanceof CountyMap) {
			return ((CountyMap) arg1).name.compareTo(((CountyMap) arg2).name);
		}
		if (arg1 instanceof CountyMap && arg2 instanceof String) {
			////System.out.println("CM_c: " + (String) arg2);
			return ((CountyMap) arg1).name.compareTo((String) arg2);
		}
		//System.err.println("CM_c: bad compare " + arg1.getClass() + " " + arg2.getClass());

		return 0;
	}

    /**
     * This method checks if the two objects are identical and sends the response.
     *@param obj The object being verified for equality.
     *@return boolean True if the objects are identical else false.
     *to be drawn.
     */
        public boolean equals(Object obj) {
		if (obj instanceof String) {
			return this.name.trim().equalsIgnoreCase(((String) obj).trim());
		} else if (obj instanceof CountyMap) {
			return this.name.trim().equals(((CountyMap) obj).name.trim());
		} else return false;
	}

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 31 * hash + (this.name != null ? this.name.hashCode() : 0);
        return hash;
    }
        
        

}


