
package uk.co.wingpath.gpx;

import java.io.*;
import java.util.*;
import uk.co.wingpath.xml.*;

// Represents a waypoint, point of interest, or named feature on a map.

public class Waypoint
    implements Xml.Savable, Xml.Loader
{
    private double lat;         // The latitude of the point.
                                // Decimal degrees, WGS84 datum.
                                // -90.0 <= value <= 90.0
    private double lon;         // The latitude of the point.
                                // Decimal degrees, WGS84 datum.
                                // -180.0 <= value < 180.0
    private double ele;         // Elevation (in meters) of the point.
    private boolean hasEle;
    private String time;        // Creation/modification timestamp for element.
                                // Date and time in are in Univeral Coordinated
                                // Time (UTC), not local time! Conforms to
                                // ISO 8601 specification for date/time
                                // representation. Fractional seconds are
                                // allowed for millisecond timing in tracklogs.
    private boolean hasTime;
    private double magvar;      // Magnetic variation (in degrees) at the point
                                // 0.0 <= value < 360.0
    private boolean hasMagvar;
    private double geoidheight; // Height (in meters) of geoid (mean sea level)
                                // above WGS84 earth ellipsoid. As defined in
                                // NMEA GGA message.
    private boolean hasGeoidheight;
    private String name;        // The GPS name of the waypoint. This field
                                // will be transferred to and from the GPS.
                                // GPX does not place restrictions on the
                                // length of this field or the characters
                                // contained in it. It is up to the receiving
                                // application to validate the field before
                                // sending it to the GPS.
    private String cmt;         // GPS waypoint comment. Sent to GPS as comment
    private String desc;        // A text description of the element.
                                // Holds additional information about the
                                // element intended for the user, not the GPS.
    private String src;         // Source of data. Included to give user some
                                // idea of reliability and accuracy of data.
                                // e.g. "Garmin eTrex", "USGS quad Boston North"
    private List<Link> link;    // Link to additional information about the
                                // waypoint.
    private String sym;         // Text of GPS symbol name. For interchange
                                // with other programs, use the exact spelling
                                // of the symbol as displayed on the GPS.
                                // If the GPS abbreviates words, spell them out.
    private String type;        // Type (classification) of the waypoint.
    private String fix;         // Type of GPX fix.
                                // value is one of: none, 2d, 3d, dgps, pps
                                // 'none' means GPS had no fix. To signify the
                                // fix info is unknown, leave out fix type
                                // entirely. pps = military signal used
    private int sat;            // Number of satellites used to calculate the
                                // GPX fix.
    private boolean hasSat;
    private double hdop;        // Horizontal dilution of precision.
    private boolean hasHdop;
    private double vdop;        // Vertical dilution of precision.
    private boolean hasVdop;
    private double pdop;        // Position dilution of precision.
    private boolean hasPdop;
    private double ageofdgpsdata;// Number of seconds since last DGPS update.
    private boolean hasAgeofdgpsdata;
    private int dgpsid;         // ID of DGPS station used in differential
                                // correction.
                                // 0 <= value <= 1023
    private boolean hasDgpsid;

    public Waypoint ()
    {
        hasEle = false;
        hasTime = false;
        hasMagvar = false;
        hasGeoidheight = false;
        name = null;
        cmt = null;
        desc = null;
        src = null;
        link = new ArrayList<Link> ();
        sym = null;
        type = null;
        fix = null;
        hasSat = false;
        hasHdop = false;
        hasVdop = false;
        hasPdop = false;
        hasAgeofdgpsdata = false;
        hasDgpsid = false;
    }

    public double getLatitude ()
    {
        return lat;
    }

    public double getLongitude () 
    {
        return lon;
    }

    public double getElevation ()
    {
        if (hasEle)
            return ele;
        return 0.0;
    }

    public boolean hasElevation ()
    {
        return hasEle;
    }

    public double getHdop ()
    {
        return hdop;
    }

    public boolean hasHdop ()
    {
        return hasHdop;
    }

    public void setHdop (double hdop)
    {
        this.hdop = hdop;
        hasHdop = true;
    }

    public String getName ()
    {
        return name;
    }

    public String getDescription ()
    {
        return desc;
    }

    public String getComment ()
    {
        return cmt;
    }

    public String getSymbol ()
    {
        return sym;
    }

    public String getTime ()
    {
        return time;
    }


    public void setLatitude (double l)
    {
        lat = l;
    }

    public void setLongitude (double l)
    {
        lon = l;
    }

    public void setElevation (double e)
    {
        ele = e;
        hasEle = true;
    }

    public void setName (String n)
    {
        name = n;
    }

    public void setDescription (String d)
    {
        desc = d;
    }

    public void setComment (String c)
    {
        cmt = c;
    }

    public void setSymbol (String s)
    {
        sym = s;
    }

    public void setTime (String t)
    {
        time = t;
        hasTime = true;
    }

    public Xml.Loader startChild (String tag)
    {
        if (tag.equalsIgnoreCase ("link"))
        {
            Link l = new Link ();
            link.add (l);
            return l;
        }
        if (tag.equalsIgnoreCase ("ele"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    ele = value;
                    hasEle = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("time"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    time = value;
                    hasTime = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("magvar"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    magvar = value;
                    hasMagvar = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("geoidheight"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    geoidheight = value;
                    hasGeoidheight = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("name"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    name = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("cmt"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    cmt = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("desc"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    desc = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("src"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    src = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("sym"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    sym = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("type"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    type = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("fix"))
        {
            return new Xml.StringLoader (new Xml.Receiver<String> ()
            {
                public void receive (String value)
                {
                    fix = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("sat"))
        {
            return new Xml.IntegerLoader (new Xml.Receiver<Integer> ()
            {
                public void receive (Integer value)
                {
                    sat = value;
                }
            });
        }
        if (tag.equalsIgnoreCase ("hdop"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    hdop = value;
                    hasHdop = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("vdop"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    vdop = value;
                    hasVdop = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("pdop"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    pdop = value;
                    hasPdop = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("ageofdgpsdata"))
        {
            return new Xml.DoubleLoader (new Xml.Receiver<Double> ()
            {
                public void receive (Double value)
                {
                    ageofdgpsdata = value;
                    hasAgeofdgpsdata = true;
                }
            });
        }
        if (tag.equalsIgnoreCase ("dgpsid"))
        {
            return new Xml.IntegerLoader (new Xml.Receiver<Integer> ()
            {
                public void receive (Integer value)
                {
                    dgpsid = value;
                    hasDgpsid = true;
                }
            });
        }
        return null;
    }

    public void attribute (String attr, String value)
    {
        if (attr.equals ("lat"))
            lat = Double.parseDouble (value);
        else if (attr.equals ("lon"))
            lon = Double.parseDouble (value);
    }

    public void end (String value)
    {
    }

    public void cleanup ()
    {
    }

    public void save (Xml.Saver saver)
        throws IOException
    {
        saver.saveAttribute ("lat", "" + lat);
        saver.saveAttribute ("lon", "" + lon);
        if (hasEle)
            saver.saveValue ("ele", "" + ele);
        if (hasTime)
            saver.saveValue ("time", time);
        if (hasMagvar)
            saver.saveValue ("magvar", "" + magvar);
        if (hasGeoidheight)
            saver.saveValue ("geoidheight", "" + geoidheight);
        if (name != null)
            saver.saveValue ("name", name);
        if (cmt != null)
            saver.saveValue ("cmt", cmt);
        if (desc != null)
            saver.saveValue ("desc", desc);
        if (src != null)
            saver.saveValue ("src", src);

        for (Link l : link)
            saver.saveValue ("link", l);

        if (sym != null)
            saver.saveValue ("sym", sym);
        if (type != null)
            saver.saveValue ("type", type);
        if (fix != null)
            saver.saveValue ("fix", fix);
        if (hasSat)
            saver.saveValue ("sat", "" + sat);
        if (hasHdop)
            saver.saveValue ("hdop", "" + hdop);
        if (hasVdop)
            saver.saveValue ("vdop", "" + vdop);
        if (hasPdop)
            saver.saveValue ("pdop", "" + pdop);
        if (hasAgeofdgpsdata)
            saver.saveValue ("ageofdgpsdata", "" + ageofdgpsdata);
        if (hasDgpsid)
            saver.saveValue ("dgpsid", "" + dgpsid);
    }
}

