/******************************************************************************
 *
 * Project:  OpenCPN
 *
 ***************************************************************************
 *   Copyright (C) 2010 by David S. Register                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
 ***************************************************************************
 */

#include "NavObjectCollection.h"
#include "routeman.h"
#include "navutil.h"
#include "Select.h"
#include "Track.h"
#include "Route.h"

#ifdef __OCPN__ANDROID__
#include <QDebug>
#endif

extern WayPointman *pWayPointMan;
extern Routeman    *g_pRouteMan;
extern MyConfig    *pConfig;

extern RouteList *pRouteList;
extern TrackList *pTrackList;
extern Select *pSelect;
//extern bool g_bIsNewLayer;


NavObjectCollection1::NavObjectCollection1()
: pugi::xml_document()
{
}

NavObjectCollection1::~NavObjectCollection1()
{
}



static RoutePoint * GPXLoadWaypoint1( pugi::xml_node &wpt_node, 
                               wxString def_symbol_name,
                               wxString GUID,
                               bool b_fullviz,
                               bool b_layer,
                               bool b_layerviz,
                               int layer_id
                             )
{
    bool bviz = false;
    bool bviz_name = false;
    bool bauto_name = false;
    bool bshared = false;
    bool b_propvizname = false;
    bool b_propviz = false;

    wxString SymString = def_symbol_name;       // default icon
    wxString NameString;
    wxString DescString;
    wxString TideStation;
    double   plan_speed = 0.0;
    wxString etd;
    wxString TypeString;
    wxString GuidString = GUID;         // default
    wxString TimeString;
    wxDateTime dt;
    RoutePoint *pWP;
    
    HyperlinkList *linklist = NULL;

    double rlat = wpt_node.attribute( "lat" ).as_double();
    double rlon = wpt_node.attribute( "lon" ).as_double();
    double ArrivalRadius = 0;
    int     l_iWaypointRangeRingsNumber = -1;
    float   l_fWaypointRangeRingsStep = -1;
    int     l_pWaypointRangeRingsStepUnits = -1;
    bool    l_bWaypointRangeRingsVisible = false;
    long    l_iWaypointScaleMin = 2147483646;
    long    l_iWaypoinScaleMax = 0;
    bool    l_bWaypointUseScale = false;
    wxColour    l_wxcWaypointRangeRingsColour;
    l_wxcWaypointRangeRingsColour.Set( _T( "#FFFFFF" ) );

    for( pugi::xml_node child = wpt_node.first_child(); child != 0; child = child.next_sibling() ) {
        const char *pcn = child.name();
        
        if( !strcmp( pcn, "sym" ) ) {
            SymString = wxString::FromUTF8( child.first_child().value() );
        }
        else
        if( !strcmp( pcn, "time") ) 
            TimeString = wxString::FromUTF8( child.first_child().value() );

        else
        if( !strcmp( pcn, "name") ) {
            NameString = wxString::FromUTF8( child.first_child().value() );
            if(NameString.StartsWith("@~~")) {
                // Convert the legacy tidal event definition and change the name so
                // that it does not kick in next time and cause overiding subsequent changes
                TideStation = NameString.Right(NameString.length() - 3);
                NameString.Replace("@~~", "@-~");
            }
        } 
        
        else
        if( !strcmp( pcn, "desc") ) {
                DescString = wxString::FromUTF8( child.first_child().value() );
        }
        
        else
        if( !strcmp( pcn, "type") ) {
                TypeString = wxString::FromUTF8( child.first_child().value() );
        }
        
        else              // Read hyperlink
        if( !strcmp( pcn, "link") ) {
            wxString HrefString;
            wxString HrefTextString;
            wxString HrefTypeString;
            if( linklist == NULL )
                linklist = new HyperlinkList;
            HrefString = wxString::FromUTF8( child.first_attribute().value() );

            for( pugi::xml_node child1 = child.first_child(); child1; child1 = child1.next_sibling() ) {
                wxString LinkString = wxString::FromUTF8( child1.name() );

                if( LinkString == _T ( "text" ) )
                    HrefTextString = wxString::FromUTF8( child1.first_child().value() );
                if( LinkString == _T ( "type" ) ) 
                    HrefTypeString = wxString::FromUTF8( child1.first_child().value() );
            }
          
            Hyperlink *link = new Hyperlink;
            link->Link = HrefString;
            link->DescrText = HrefTextString;
            link->LType = HrefTypeString;
            linklist->Append( link );
        }

    //    OpenCPN Extensions....
        else
        if( !strcmp( pcn, "extensions") ) {
            for( pugi::xml_node ext_child = child.first_child(); ext_child; ext_child = ext_child.next_sibling() ) {
                wxString ext_name = wxString::FromUTF8( ext_child.name() );
                if( ext_name == _T ( "opencpn:guid" ) ) {
                  GuidString = wxString::FromUTF8( ext_child.first_child().value() );
                }
                else
                if( ext_name == _T ( "opencpn:viz" ) ) {
                    b_propviz = true;
                    wxString s = wxString::FromUTF8( ext_child.first_child().value() );
                    long v = 0;
                    if( s.ToLong( &v ) )
                        bviz = ( v != 0 );
                }
                else
                if( ext_name == _T ( "opencpn:viz_name" ) ) {
                    b_propvizname = true;
                    wxString s = wxString::FromUTF8( ext_child.first_child().value() );
                    long v = 0;
                    if( s.ToLong( &v ) )
                        bviz_name = ( v != 0 );
                }
                else
                if( ext_name == _T ( "opencpn:auto_name" ) ) {
                    wxString s = wxString::FromUTF8( ext_child.first_child().value() );
                    long v = 0;
                    if( s.ToLong( &v ) )
                        bauto_name = ( v != 0 );
                }
                else
                if( ext_name  == _T ( "opencpn:shared" ) ) {
                    wxString s = wxString::FromUTF8( ext_child.first_child().value() );
                    long v = 0;
                    if( s.ToLong( &v ) )
                        bshared = ( v != 0 );
                }
                if( ext_name == _T ( "opencpn:arrival_radius" ) ) {
                    wxString::FromUTF8(ext_child.first_child().value()).ToDouble(&ArrivalRadius ) ;
                }
                if ( ext_name == _T("opencpn:waypoint_range_rings") ) {
                    for ( pugi::xml_attribute attr = ext_child.first_attribute(); attr; attr = attr.next_attribute() ) {
                        if ( wxString::FromUTF8(attr.name()) == _T("number") )
                            l_iWaypointRangeRingsNumber = attr.as_int();
                        else if ( wxString::FromUTF8(attr.name()) == _T("step") )
                            l_fWaypointRangeRingsStep = attr.as_float();
                        else if ( wxString::FromUTF8(attr.name()) == _T("units") )
                            l_pWaypointRangeRingsStepUnits = attr.as_int();
                        else if ( wxString::FromUTF8(attr.name()) == _T("visible") )
                            l_bWaypointRangeRingsVisible =  attr.as_bool();
                        else if ( wxString::FromUTF8(attr.name()) == _T("colour") )
                            l_wxcWaypointRangeRingsColour.Set( wxString::FromUTF8( attr.as_string() ) );
                    }
                 }
                if ( ext_name == _T("opencpn:scale_min_max") ) {
                    for ( pugi::xml_attribute attr = ext_child.first_attribute(); attr; attr = attr.next_attribute() ) {
                        if ( wxString::FromUTF8(attr.name()) == _T("UseScale") )
                            l_bWaypointUseScale = attr.as_bool();
                        else if ( wxString::FromUTF8(attr.name()) == _T("ScaleMin") )
                            l_iWaypointScaleMin = attr.as_int();
                        else if ( wxString::FromUTF8(attr.name()) == _T("ScaleMax") )
                            l_iWaypoinScaleMax = attr.as_float();
                    }
                }
                if( ext_name == _T ( "opencpn:tidestation" ) ) {
                    TideStation = wxString::FromUTF8(ext_child.first_child().value()) ;
                }
                if( ext_name == _T ( "opencpn:rte_properties" ) ) {
                    for (pugi::xml_attribute attr = ext_child.first_attribute(); attr; attr = attr.next_attribute())
                    {
                        if( !strcmp( attr.name(), "planned_speed" ) )
                            plan_speed = attr.as_double();
                        else
                            if( !strcmp( attr.name(), "etd" ) )
                                etd = attr.as_string();
                    }
                }
             }// for 
        } //extensions
    }   // for

    // Create waypoint

    if( b_layer ) {
        if( GuidString.IsEmpty() )
            GuidString = pWayPointMan->CreateGUID(NULL);
    }

    pWP = new RoutePoint( rlat, rlon, SymString, NameString, GuidString, false ); // do not add to global WP list yet...
    pWP->m_MarkDescription = DescString;
    pWP->m_TideStation = TideStation;
    pWP->m_bIsolatedMark = bshared;      // This is an isolated mark
    pWP->SetWaypointArrivalRadius( ArrivalRadius );
    pWP->SetWaypointRangeRingsNumber( l_iWaypointRangeRingsNumber );
    pWP->SetWaypointRangeRingsStep( l_fWaypointRangeRingsStep );
    pWP->SetWaypointRangeRingsStepUnits( l_pWaypointRangeRingsStepUnits );
    pWP->SetShowWaypointRangeRings( l_bWaypointRangeRingsVisible );
    
    // Migrate from O4.x XML format.
    // In O5, the attribute "range rings visible" is synonymous with ( "range rings number" != 0 )
    // So, if we see an attribute "visible"=false in importing from XML, we must set "number" = 0 to be consistent
    if(!l_bWaypointRangeRingsVisible)
        pWP->SetWaypointRangeRingsNumber(0);

    pWP->SetWaypointRangeRingsColour( l_wxcWaypointRangeRingsColour );
    pWP->SetScaMin( l_iWaypointScaleMin );
    pWP->SetScaMax( l_iWaypoinScaleMax );
    pWP->SetUseSca( l_bWaypointUseScale );
    pWP->SetPlannedSpeed(plan_speed);
    pWP->SetETD(etd);

    pWP->m_bShowNameData = bviz_name;
    if( b_propvizname )
        pWP->m_bShowName = bviz_name;
    else
        if( b_fullviz )
            pWP->m_bShowName = true;
        else
            pWP->m_bShowName = false;
    
    if( b_propviz )
        pWP->m_bIsVisible = bviz;
    else
        if( b_fullviz )
            pWP->m_bIsVisible = true;

    if(b_layer) {
        pWP->m_bIsInLayer = true;
        pWP->m_LayerID = layer_id;
        pWP->m_bIsVisible = b_layerviz;
        pWP->SetListed( false );
    }
   
    pWP->m_bKeepXRoute = bshared;
    pWP->m_bDynamicName = bauto_name;

    if(TimeString.Len()) {
        pWP->m_timestring = TimeString;
        pWP->SetCreateTime(wxInvalidDateTime);          // cause deferred timestamp parsing
    }
        

    if( linklist ) {
        delete pWP->m_HyperlinkList;                    // created in RoutePoint ctor
        pWP->m_HyperlinkList = linklist;
    }

    return pWP ;
}

static TrackPoint * GPXLoadTrackPoint1( pugi::xml_node &wpt_node )
{
    wxString TimeString;

    double rlat = wpt_node.attribute( "lat" ).as_double();
    double rlon = wpt_node.attribute( "lon" ).as_double();

    for( pugi::xml_node child = wpt_node.first_child(); child != 0; child = child.next_sibling() ) {
        const char *pcn = child.name();
        if( !strcmp( pcn, "time") ) 
            TimeString = wxString::FromUTF8( child.first_child().value() );

    //    OpenCPN Extensions....
        else
        if( !strcmp( pcn, "extensions") ) {
            for( pugi::xml_node ext_child = child.first_child(); ext_child; ext_child = ext_child.next_sibling() ) {
                wxString ext_name = wxString::FromUTF8( ext_child.name() );
                if( ext_name == _T ( "opencpn:action" ) ) {
                }
             }// for 
        } //extensions
    }   // for

    // Create waypoint
    return new TrackPoint( rlat, rlon, TimeString );
}

static Track *GPXLoadTrack1( pugi::xml_node &trk_node, bool b_fullviz,
                      bool b_layer,
                      bool b_layerviz,
                      int layer_id )
{
    wxString TrackName;
    wxString DescString;
    unsigned short int GPXSeg;            
    bool b_propviz = false;
    bool b_viz = true;
    Track *pTentTrack = NULL;
    HyperlinkList *linklist = NULL;
    
    wxString Name = wxString::FromUTF8( trk_node.name() );
    if( Name == _T ( "trk" ) ) {
        pTentTrack = new Track();
        GPXSeg = 0;   
        
        TrackPoint *pWp = NULL;
        
        for( pugi::xml_node tschild = trk_node.first_child(); tschild; tschild = tschild.next_sibling() ) {
            wxString ChildName = wxString::FromUTF8( tschild.name() );
            if( ChildName == _T ( "trkseg" ) ) {
                GPXSeg += 1;                
                
                //    Official GPX spec calls for trkseg to have children trkpt
                for( pugi::xml_node tpchild = tschild.first_child(); tpchild; tpchild = tpchild.next_sibling() ) {
                    wxString tpChildName = wxString::FromUTF8( tpchild.name() );
                    if( tpChildName == _T("trkpt") ) {
                        pWp = ::GPXLoadTrackPoint1(tpchild);
                        pTentTrack->AddPoint( pWp );          // defer BBox calculation
                        pWp->m_GPXTrkSegNo = GPXSeg;
                    }
                }
            }
            else
            if( ChildName == _T ( "name" ) )
                TrackName = wxString::FromUTF8( tschild.first_child().value() );
            else
            if( ChildName == _T ( "desc" ) )
                DescString = wxString::FromUTF8( tschild.first_child().value() );
            else
            
            if( ChildName == _T ( "link") ) {
                wxString HrefString;
                wxString HrefTextString;
                wxString HrefTypeString;
                if( linklist == NULL )
                    linklist = new HyperlinkList;
                HrefString = wxString::FromUTF8( tschild.first_attribute().value() );

                for( pugi::xml_node child1 = tschild.first_child(); child1; child1 = child1.next_sibling() ) {
                    wxString LinkString = wxString::FromUTF8( child1.name() );

                    if( LinkString == _T ( "text" ) )
                        HrefTextString = wxString::FromUTF8( child1.first_child().value() );
                    if( LinkString == _T ( "type" ) ) 
                        HrefTypeString = wxString::FromUTF8( child1.first_child().value() );
                }
              
                Hyperlink *link = new Hyperlink;
                link->Link = HrefString;
                link->DescrText = HrefTextString;
                link->LType = HrefTypeString;
                linklist->Append( link );
            }
            
            else
            if( ChildName == _T ( "extensions" ) ) {
                        for( pugi::xml_node ext_child = tschild.first_child(); ext_child; ext_child = ext_child.next_sibling() ) {
                            wxString ext_name = wxString::FromUTF8( ext_child.name() );
                            if( ext_name == _T ( "opencpn:start" ) ) {
                                pTentTrack->m_TrackStartString = wxString::FromUTF8(ext_child.first_child().value());
                            }
                            else
                            if( ext_name == _T ( "opencpn:end" ) ) {
                                pTentTrack->m_TrackEndString = wxString::FromUTF8(ext_child.first_child().value());
                            }
                            
                            else
                            if( ext_name == _T ( "opencpn:viz" ) ) {
                                wxString viz = wxString::FromUTF8(ext_child.first_child().value());
                                b_propviz = true;
                                b_viz = ( viz == _T("1") );
                            }
                            else
                            if( ext_name == _T ( "opencpn:style" ) ) {
                                for (pugi::xml_attribute attr = ext_child.first_attribute(); attr; attr = attr.next_attribute())
                                {
                                    if( !strcmp( attr.name(), "style" ) )
                                        pTentTrack->m_style = (wxPenStyle)attr.as_int();
                                    else if( !strcmp( attr.name(), "width" ) )
                                        pTentTrack->m_width = attr.as_int();
                                }
                            }
 
                            else
                            if( ext_name == _T ( "opencpn:guid" ) ) {
                                    pTentTrack->m_GUID =  wxString::FromUTF8(ext_child.first_child().value());
                            }
                            
                            else
                            if( ext_name.EndsWith( _T ( "TrackExtension" ) ) ) //Parse GPXX color
                              {
                                for( pugi::xml_node gpxx_child = ext_child.first_child(); gpxx_child; gpxx_child = gpxx_child.next_sibling() ) {
                                    wxString gpxx_name = wxString::FromUTF8( gpxx_child.name() );
                                    if( gpxx_name.EndsWith( _T ( "DisplayColor" ) ) )
                                        pTentTrack->m_Colour = wxString::FromUTF8(gpxx_child.first_child().value() );
                                }
                            }
                    } //extensions
                }
        }
        
        pTentTrack->SetName( TrackName );
        pTentTrack->m_TrackDescription = DescString;

        if( b_propviz )
                pTentTrack->SetVisible( b_viz );
        else {
                if( b_fullviz )
                    pTentTrack->SetVisible();
        }

        if( b_layer ) {
            pTentTrack->SetVisible( b_layerviz );
            pTentTrack->m_bIsInLayer = true;
            pTentTrack->m_LayerID = layer_id;
            pTentTrack->SetListed( false );
        }
                
        pTentTrack->SetCurrentTrackSeg( GPXSeg );
                
    }
    
    if( linklist ) {
        delete pTentTrack->m_HyperlinkList;                    // created in TrackPoint ctor
        pTentTrack->m_HyperlinkList = linklist;
    }

    return pTentTrack;
}


static Route *GPXLoadRoute1( pugi::xml_node &wpt_node, bool b_fullviz,
                      bool b_layer, bool b_layerviz,
                      int layer_id, bool b_change )
{
    wxString RouteName;
    wxString DescString;
    bool b_propviz = false;
    bool b_propSWPviz =false;
    bool b_viz = true;
    bool swpViz = false;
    Route *pTentRoute = NULL;
    
    wxString Name = wxString::FromUTF8( wpt_node.name() );
    if( Name == _T ( "rte" ) ) {
        pTentRoute = new Route();
        HyperlinkList *linklist = NULL;
        
        RoutePoint *pWp = NULL;
		bool route_existing = false;
        pTentRoute->m_TimeDisplayFormat = RTE_TIME_DISP_UTC;
        
        for( pugi::xml_node tschild = wpt_node.first_child(); tschild; tschild = tschild.next_sibling() ) {
            wxString ChildName = wxString::FromUTF8( tschild.name() );

			//load extentions first to determine if the route still exists
			if( ChildName == _T ( "extensions" ) ) {
                for( pugi::xml_node ext_child = tschild.first_child(); ext_child; ext_child = ext_child.next_sibling() ) {
                    wxString ext_name = wxString::FromUTF8( ext_child.name() );

                    if( ext_name == _T ( "opencpn:start" ) ) {
                        pTentRoute->m_RouteStartString = wxString::FromUTF8(ext_child.first_child().value());
                    }
                    else
                    if( ext_name == _T ( "opencpn:end" ) ) {
                        pTentRoute->m_RouteEndString = wxString::FromUTF8(ext_child.first_child().value());
                    }
                        
                    else
                    if( ext_name == _T ( "opencpn:viz" ) ) {
                                wxString viz = wxString::FromUTF8(ext_child.first_child().value());
                                b_propviz = true;
                                b_viz = ( viz == _T("1") );
                    }
                    
                    else
                    if( ext_name == _T ( "opencpn:sharedWPviz" ) ) {
                                wxString viz = wxString::FromUTF8(ext_child.first_child().value());
                                b_propSWPviz = true;
                                swpViz = ( viz == _T("1") );
                    }
                    else
                    if( ext_name == _T ( "opencpn:style" ) ) {
                        for (pugi::xml_attribute attr = ext_child.first_attribute(); attr; attr = attr.next_attribute())
                        {
                            if( !strcmp( attr.name(), "style" ) )
                                pTentRoute->m_style = (wxPenStyle)attr.as_int();
                            else
                            if( !strcmp( attr.name(), "width" ) )
                                pTentRoute->m_width = attr.as_int();
                        }
                     }
                     
                     else
                     if( ext_name == _T ( "opencpn:guid" ) ) {
                        //if ( !g_bIsNewLayer ) ) 
                        pTentRoute->m_GUID =  wxString::FromUTF8(ext_child.first_child().value());
                     }
                     
                     else
                     if( ext_name == _T ( "opencpn:planned_speed" ) ) {
                        pTentRoute->m_PlannedSpeed = atof( ext_child.first_child().value() );
                     }
                     
                     else
                     if( ext_name == _T ( "opencpn:planned_departure" ) ) {
                        ParseGPXDateTime( pTentRoute->m_PlannedDeparture, wxString::FromUTF8(ext_child.first_child().value()) );
                     }
                     
                     else
                     if( ext_name == _T ( "opencpn:time_display" ) ) {
                        pTentRoute->m_TimeDisplayFormat = wxString::FromUTF8(ext_child.first_child().value());
                     }
                     else
                     if( ext_name.EndsWith( _T ( "RouteExtension" ) ) ) //Parse GPXX color
                     {
                        for( pugi::xml_node gpxx_child = ext_child.first_child(); gpxx_child; gpxx_child = gpxx_child.next_sibling() ) {
                            wxString gpxx_name = wxString::FromUTF8( gpxx_child.name() );
                            if( gpxx_name.EndsWith( _T ( "DisplayColor" ) ) )
                                 pTentRoute->m_Colour = wxString::FromUTF8(gpxx_child.first_child().value() );
                        }
                     }
                }
				if( !b_change) {
					if( RouteExists(pTentRoute->m_GUID) ) { //we are loading a different route with the same guid so let's generate a new guid
						pTentRoute->m_GUID = pWayPointMan->CreateGUID(NULL);
						route_existing = true;
					}
				}
			}//extension
			else
            if( ChildName == _T ( "rtept" ) ) {
                RoutePoint *tpWp = ::GPXLoadWaypoint1(  tschild, _T("square"), _T(""), b_fullviz, b_layer, b_layerviz, layer_id);
                RoutePoint *erp = NULL;
                if(!b_layer)
                    erp = ::WaypointExists( tpWp->m_GUID );
				// 1) if b_change is true, that means we are after crash - load the route and points as found in source file
				// 2) if route_existing, we are loading a different route with the same guid. In this case load points as found in
				//source file, changing the guid, but keep existing "isolated point" as found in the DB
				// 3) in all other cases keep existing points if found and load new points if not found
				bool new_wpt = true;
				if( b_change )
					pWp = tpWp;
                else {
					if(  erp != NULL && (!route_existing || ( route_existing && tpWp->m_bKeepXRoute) ) ) {
						pWp = erp;
						new_wpt = false;
					}
					else {
						if( route_existing )
							tpWp->m_GUID = pWayPointMan->CreateGUID( NULL );
						pWp = tpWp;
					}
				}

                pTentRoute->AddPoint( pWp, false, true );          // defer BBox calculation
                pWp->m_bIsInRoute = true;                      // Hack

                if( new_wpt )
					pWayPointMan->AddRoutePoint( pWp );
				else
					delete tpWp;
            }
            else
            if( ChildName == _T ( "name" ) ) {
                RouteName = wxString::FromUTF8( tschild.first_child().value() );
            }
            else
            if( ChildName == _T ( "desc" ) ) {
                DescString = wxString::FromUTF8( tschild.first_child().value() );
            }

            if( ChildName == _T ( "link") ) {
                wxString HrefString;
                wxString HrefTextString;
                wxString HrefTypeString;
                if( linklist == NULL )
                    linklist = new HyperlinkList;
                HrefString = wxString::FromUTF8( tschild.first_attribute().value() );
                
                for( pugi::xml_node child1 = tschild.first_child(); child1; child1 = child1.next_sibling() ) {
                    wxString LinkString = wxString::FromUTF8( child1.name() );
                    
                    if( LinkString == _T ( "text" ) )
                        HrefTextString = wxString::FromUTF8( child1.first_child().value() );
                    if( LinkString == _T ( "type" ) )
                        HrefTypeString = wxString::FromUTF8( child1.first_child().value() );
                }
                
                Hyperlink *link = new Hyperlink;
                link->Link = HrefString;
                link->DescrText = HrefTextString;
                link->LType = HrefTypeString;
                linklist->Append( link );
            }
            
            else
            //TODO: This is wrong, left here just to save data of the 3.3 beta series users.
            if( ChildName.EndsWith( _T ( "RouteExtension" ) ) ) //Parse GPXX color
            {
                for( pugi::xml_node gpxx_child = tschild.first_child(); gpxx_child; gpxx_child = gpxx_child.next_sibling() ) {
                    wxString gpxx_name = wxString::FromUTF8( gpxx_child.name() );
                    if( gpxx_name.EndsWith( _T ( "DisplayColor" ) ) )
                         pTentRoute->m_Colour = wxString::FromUTF8(gpxx_child.first_child().value() );
                }
            }
        }
                    
        pTentRoute->m_RouteNameString = RouteName;
        pTentRoute->m_RouteDescription = DescString;
        if( linklist ) {
            pTentRoute->m_HyperlinkList = linklist;
        }

        if( b_propviz ) {
            pTentRoute->SetVisible( b_viz );
        } else if( b_fullviz ) {
            pTentRoute->SetVisible();
        }
        
        if( b_propSWPviz)
            pTentRoute->SetSharedWPViz( swpViz );
 
        if( b_layer ){
            pTentRoute->SetVisible( b_layerviz );
            pTentRoute->m_bIsInLayer = true;
            pTentRoute->m_LayerID = layer_id;
            pTentRoute->SetListed( false );
        }            
 
    }

    return pTentRoute;
}


static bool GPXCreateWpt( pugi::xml_node node, RoutePoint *pr, unsigned int flags )
{
    wxString s;
    pugi::xml_node child;
    pugi::xml_attribute attr;
    
    s.Printf(_T("%.9f"), pr->m_lat);
    node.append_attribute("lat") = s.mb_str();
    s.Printf(_T("%.9f"), pr->m_lon);
    node.append_attribute("lon") = s.mb_str();
 
    if(flags & OUT_TIME) {
            child = node.append_child("time");
            if( pr->m_timestring.Len() )
                child.append_child(pugi::node_pcdata).set_value(pr->m_timestring.mb_str());
            else {
                wxString t = pr->GetCreateTime().FormatISODate().Append(_T("T")).Append(pr->GetCreateTime().FormatISOTime()).Append(_T("Z"));
                child.append_child(pugi::node_pcdata).set_value(t.mb_str());
            }
    }
        
    if ( (!pr->GetName().IsEmpty() && (flags & OUT_NAME)) || (flags & OUT_NAME_FORCE) ) {
        wxCharBuffer buffer=pr->GetName().ToUTF8();
        if(buffer.data()) {
            child = node.append_child("name");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }       

    if ( (!pr->GetDescription().IsEmpty() && (flags & OUT_DESC)) || (flags & OUT_DESC_FORCE) ) {
        wxCharBuffer buffer=pr->GetDescription().ToUTF8();
        if(buffer.data()) {
            child = node.append_child("desc");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }       

    // Hyperlinks
    if(flags & OUT_HYPERLINKS ){
        HyperlinkList *linklist = pr->m_HyperlinkList;
        if( linklist && linklist->GetCount() ) {
            wxHyperlinkListNode *linknode = linklist->GetFirst();
            while( linknode ) {
                Hyperlink *link = linknode->GetData();
                
                pugi::xml_node child_link = node.append_child("link");;
                wxCharBuffer buffer=link->Link.ToUTF8();
                if(buffer.data())
                    child_link.append_attribute("href") = buffer.data();
                                
                buffer=link->DescrText.ToUTF8();
                if(buffer.data()) {
                    child = child_link.append_child("text");
                    child.append_child(pugi::node_pcdata).set_value(buffer.data());
                }
                
                buffer=link->LType.ToUTF8();
                if(buffer.data() && strlen(buffer.data()) > 0) {
                    child = child_link.append_child("type");
                    child.append_child(pugi::node_pcdata).set_value(buffer.data());
                }
                
                linknode = linknode->GetNext();
            }
        }
    }
    
    if (flags & OUT_SYM_FORCE) {
        child = node.append_child("sym");
        if (!pr->GetIconName().IsEmpty()) {
            child.append_child(pugi::node_pcdata).set_value(pr->GetIconName().mb_str());
        }
        else {
            child.append_child("empty");
        }
    }       
    
    if(flags & OUT_TYPE) {
        child = node.append_child("type");
        child.append_child(pugi::node_pcdata).set_value("WPT");
    }
    
    if( (flags & OUT_GUID) || (flags & OUT_VIZ) || (flags & OUT_VIZ_NAME) || (flags & OUT_SHARED)
       || (flags & OUT_AUTO_NAME)  || (flags & OUT_EXTENSION) || (flags & OUT_TIDE_STATION)
       || (flags & OUT_RTE_PROPERTIES) ) {
        
        pugi::xml_node child_ext = node.append_child("extensions");
        
        if (!pr->m_GUID.IsEmpty() && (flags & OUT_GUID) ) {
            child = child_ext.append_child("opencpn:guid");
            child.append_child(pugi::node_pcdata).set_value(pr->m_GUID.mb_str());
        }
         
         if((flags & OUT_VIZ) && !pr->m_bIsVisible) {
             child = child_ext.append_child("opencpn:viz");
             child.append_child(pugi::node_pcdata).set_value("0");
         }
            
         if((flags & OUT_VIZ_NAME) && pr->m_bShowName) {
             child = child_ext.append_child("opencpn:viz_name");
             child.append_child(pugi::node_pcdata).set_value("1");
         }
         
         if((flags & OUT_AUTO_NAME) && pr->m_bDynamicName) {
             child = child_ext.append_child("opencpn:auto_name");
             child.append_child(pugi::node_pcdata).set_value("1");
         }
         if((flags & OUT_SHARED) && pr->m_bKeepXRoute) {
             child = child_ext.append_child("opencpn:shared");
             child.append_child(pugi::node_pcdata).set_value("1");
         }
        if(flags & OUT_ARRIVAL_RADIUS) {
            child = child_ext.append_child("opencpn:arrival_radius");
            s.Printf(_T("%.3f"), pr->GetWaypointArrivalRadius());
            child.append_child(pugi::node_pcdata).set_value( s.mbc_str() );
        }
        if(flags & OUT_WAYPOINT_RANGE_RINGS) {
            child = child_ext.append_child("opencpn:waypoint_range_rings");
            pugi::xml_attribute viz = child.append_attribute( "visible" );
            viz.set_value( pr->m_bShowWaypointRangeRings );
            pugi::xml_attribute number = child.append_attribute( "number" );
            number.set_value( pr->m_iWaypointRangeRingsNumber );
            pugi::xml_attribute step = child.append_attribute( "step" );
            step.set_value( pr->m_fWaypointRangeRingsStep );
            pugi::xml_attribute units = child.append_attribute( "units" );
            units.set_value( pr->m_iWaypointRangeRingsStepUnits );
            pugi::xml_attribute colour = child.append_attribute( "colour" );
            colour.set_value( pr->m_wxcWaypointRangeRingsColour.GetAsString( wxC2S_HTML_SYNTAX ).utf8_str() ) ;
        }
        if(flags & OUT_WAYPOINT_SCALE) {
            child = child_ext.append_child("opencpn:scale_min_max");
            pugi::xml_attribute use = child.append_attribute( "UseScale" );
            use.set_value( pr->GetUseSca() );
            pugi::xml_attribute sca = child.append_attribute( "ScaleMin" );
            sca.set_value( pr->GetScaMin() );
            pugi::xml_attribute max = child.append_attribute( "ScaleMax" );
            max.set_value( pr->GetScaMax() );
        }
        if((flags & OUT_TIDE_STATION) && !pr->m_TideStation.IsEmpty()) {
            child = child_ext.append_child("opencpn:tidestation");
            child.append_child(pugi::node_pcdata).set_value( pr->m_TideStation.mb_str() );
        }
        if((flags & OUT_RTE_PROPERTIES) && (pr->GetPlannedSpeed() > 0.0001 || pr->m_manual_etd)) {
            child = child_ext.append_child("opencpn:rte_properties");
            if( pr->GetPlannedSpeed() > 0.0001 ) {
                pugi::xml_attribute use = child.append_attribute( "planned_speed" );
                use.set_value( wxString::Format(_T("%.1lf"), pr->GetPlannedSpeed()).mb_str());
            }
            if( pr->m_manual_etd ) {
                pugi::xml_attribute use = child.append_attribute( "etd" );
                use.set_value( pr->GetManualETD().FormatISOCombined().mb_str());
            }
        }
    }
    
    return true;
}

static bool GPXCreateTrkpt( pugi::xml_node node, TrackPoint *pt, unsigned int flags )
{
    wxString s;
    pugi::xml_node child;
    pugi::xml_attribute attr;
    
    s.Printf(_T("%.9f"), pt->m_lat);
    node.append_attribute("lat") = s.mb_str();
    s.Printf(_T("%.9f"), pt->m_lon);
    node.append_attribute("lon") = s.mb_str();
 
    if(flags & OUT_TIME) {
        child = node.append_child("time");
        if( pt->GetTimeString() )
            child.append_child(pugi::node_pcdata).set_value(pt->GetTimeString());
    }
    
    return true;
}

static bool GPXCreateTrk( pugi::xml_node node, Track *pTrack, unsigned int flags )
{
    pugi::xml_node child;

    if( pTrack->GetName().Len() ) {
        wxCharBuffer buffer=pTrack->GetName().ToUTF8();
        if(buffer.data()) {
            child = node.append_child("name");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    if( pTrack->m_TrackDescription.Len() ) {
        wxCharBuffer buffer=pTrack->m_TrackDescription.ToUTF8();
        if(buffer.data()) {
            child = node.append_child("desc");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    // Hyperlinks
    HyperlinkList *linklist = pTrack->m_HyperlinkList;
    if( linklist && linklist->GetCount() ) {
        wxHyperlinkListNode *linknode = linklist->GetFirst();
        while( linknode ) {
            Hyperlink *link = linknode->GetData();
        
            pugi::xml_node child_link = node.append_child("link");
            wxCharBuffer buffer = link->Link.ToUTF8();
            if(buffer.data())
                child_link.append_attribute("href") = buffer.data();
                        
            buffer=link->DescrText.ToUTF8();
            if(buffer.data()) {
                child = child_link.append_child("text");
                child.append_child(pugi::node_pcdata).set_value(buffer.data());
            }
        
            buffer=link->LType.ToUTF8();
            if(buffer.data()  && strlen(buffer.data()) > 0) {
                child = child_link.append_child("type");
                child.append_child(pugi::node_pcdata).set_value(buffer.data());
            }
        
            linknode = linknode->GetNext();
        }
    }
    
    pugi::xml_node child_ext = node.append_child("extensions");
    
    child = child_ext.append_child("opencpn:guid");
    child.append_child(pugi::node_pcdata).set_value(pTrack->m_GUID.mb_str());
    
    child = child_ext.append_child("opencpn:viz");
    child.append_child(pugi::node_pcdata).set_value(pTrack->IsVisible() == true ? "1" : "0");

    if( pTrack->m_TrackStartString.Len() ) {
        wxCharBuffer buffer=pTrack->m_TrackStartString.ToUTF8();
        if(buffer.data()) {
            child = child_ext.append_child("opencpn:start");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    if( pTrack->m_TrackEndString.Len() ) {
        wxCharBuffer buffer=pTrack->m_TrackEndString.ToUTF8();
        if(buffer.data()) {
            child = child_ext.append_child("opencpn:end");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    if( pTrack->m_width != WIDTH_UNDEFINED || pTrack->m_style != wxPENSTYLE_INVALID ) {
        child = child_ext.append_child("opencpn:style");
        
        if( pTrack->m_width != WIDTH_UNDEFINED )
            child.append_attribute("width") = pTrack->m_width;
        if( pTrack->m_style != wxPENSTYLE_INVALID )
            child.append_attribute("style") = pTrack->m_style;
    }
    
    if( pTrack->m_Colour != wxEmptyString ) {
        pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:TrackExtension");
        child = gpxx_ext.append_child("gpxx:DisplayColor");
        child.append_child(pugi::node_pcdata).set_value(pTrack->m_Colour.mb_str());
    }
        
    
    if(flags & RT_OUT_NO_RTPTS)
        return true;
    
    int node2 = 0;
    TrackPoint *prp;
        
    unsigned short int GPXTrkSegNo1 = 1;
        
    do {
        unsigned short int GPXTrkSegNo2 = GPXTrkSegNo1;
        
        pugi::xml_node seg = node.append_child("trkseg");
        
        while( node2 < pTrack->GetnPoints() ) {
            prp = pTrack->GetPoint(node2);
            GPXTrkSegNo1 = prp->m_GPXTrkSegNo;
            if(GPXTrkSegNo1 != GPXTrkSegNo2)
                break;
            
            GPXCreateTrkpt(seg.append_child("trkpt"), prp, OPT_TRACKPT);
            
            node2++;
        }
    } while( node2 < pTrack->GetnPoints() );
        
        
    return true;
}
                   
static bool GPXCreateRoute( pugi::xml_node node, Route *pRoute )
{
    pugi::xml_node child;
    
    if( pRoute->m_RouteNameString.Len() ) {
        wxCharBuffer buffer=pRoute->m_RouteNameString.ToUTF8();
        if(buffer.data()) {
            child = node.append_child("name");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    if( pRoute->m_RouteDescription.Len() ) {
        wxCharBuffer buffer=pRoute->m_RouteDescription.ToUTF8();
        if(buffer.data()) {
            child = node.append_child("desc");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    // Hyperlinks
    HyperlinkList *linklist = pRoute->m_HyperlinkList;
    if( linklist && linklist->GetCount() ) {
        wxHyperlinkListNode *linknode = linklist->GetFirst();
        while( linknode ) {
            Hyperlink *link = linknode->GetData();
            
            pugi::xml_node child_link = node.append_child("link");
            wxCharBuffer buffer = link->Link.ToUTF8();
            if(buffer.data())
                child_link.append_attribute("href") = buffer.data();
            
            buffer=link->DescrText.ToUTF8();
            if(buffer.data()) {
                child = child_link.append_child("text");
                child.append_child(pugi::node_pcdata).set_value(buffer.data());
            }
            
            buffer=link->LType.ToUTF8();
            if(buffer.data()  && strlen(buffer.data()) > 0) {
                child = child_link.append_child("type");
                child.append_child(pugi::node_pcdata).set_value(buffer.data());
            }
            
            linknode = linknode->GetNext();
        }
    }
    
    pugi::xml_node child_ext = node.append_child("extensions");
    
    child = child_ext.append_child("opencpn:guid");
    child.append_child(pugi::node_pcdata).set_value(pRoute->m_GUID.mb_str());
    
    child = child_ext.append_child("opencpn:viz");
    child.append_child(pugi::node_pcdata).set_value(pRoute->IsVisible() == true ? "1" : "0");
    
    if(pRoute->ContainsSharedWP()){
        child = child_ext.append_child("opencpn:sharedWPviz");
        child.append_child(pugi::node_pcdata).set_value(pRoute->GetSharedWPViz() == true ? "1" : "0");

    }
 
    if( pRoute->m_RouteStartString.Len() ) {
        wxCharBuffer buffer=pRoute->m_RouteStartString.ToUTF8();
        if(buffer.data()) {
            child = child_ext.append_child("opencpn:start");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    if( pRoute->m_RouteEndString.Len() ) {
        wxCharBuffer buffer=pRoute->m_RouteEndString.ToUTF8();
        if(buffer.data()) {
            child = child_ext.append_child("opencpn:end");
            child.append_child(pugi::node_pcdata).set_value(buffer.data());
        }
    }
    
    if( pRoute->m_PlannedSpeed != ROUTE_DEFAULT_SPEED ) {
        child = child_ext.append_child("opencpn:planned_speed");
        wxString s;
        s.Printf(_T("%.2f"), pRoute->m_PlannedSpeed);
        child.append_child(pugi::node_pcdata).set_value(s.mb_str());
    }
    
    if( pRoute->m_PlannedDeparture.IsValid() ) {
        child = child_ext.append_child("opencpn:planned_departure");
        wxString t = pRoute->m_PlannedDeparture.FormatISODate().Append(_T("T")).Append(pRoute->m_PlannedDeparture.FormatISOTime()).Append(_T("Z"));
        child.append_child(pugi::node_pcdata).set_value(t.mb_str());
    }

    child = child_ext.append_child("opencpn:time_display");
    child.append_child(pugi::node_pcdata).set_value(pRoute->m_TimeDisplayFormat.mb_str());
    
    if( pRoute->m_width != WIDTH_UNDEFINED || pRoute->m_style != wxPENSTYLE_INVALID ) {
        child = child_ext.append_child("opencpn:style");
        
        if( pRoute->m_width != WIDTH_UNDEFINED )
            child.append_attribute("width") = pRoute->m_width;
        if( pRoute->m_style != wxPENSTYLE_INVALID )
            child.append_attribute("style") = pRoute->m_style;
    }
 
    pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:RouteExtension");
    child = gpxx_ext.append_child("gpxx:IsAutoNamed");
    child.append_child(pugi::node_pcdata).set_value("false");
    
    if( pRoute->m_Colour != wxEmptyString ) {
        child = gpxx_ext.append_child("gpxx:DisplayColor");
        child.append_child(pugi::node_pcdata).set_value(pRoute->m_Colour.mb_str());
    }
                                 
    RoutePointList *pRoutePointList = pRoute->pRoutePointList;
    wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
    RoutePoint *prp;
    
    while( node2  ) {
        prp = node2->GetData();
            
        GPXCreateWpt(node.append_child("rtept"), prp, OPT_ROUTEPT);
            
        node2 = node2->GetNext();
    }
    
    return true;
}
                       

static bool InsertRouteA( Route *pTentRoute )
{
    if( !pTentRoute )
        return false;
    
    bool bAddroute = true;
    //    If the route has only 1 point, don't load it.
    if( pTentRoute->GetnPoints() < 2 )
        bAddroute = false;
    
    //    TODO  All this trouble for a tentative route.......Should make some Route methods????
    if( bAddroute ) {
            
        pRouteList->Append( pTentRoute );
                 
                //    Do the (deferred) calculation of BBox
                    pTentRoute->FinalizeForRendering();

                    
                    //    Add the selectable points and segments
                    
                    int ip = 0;
                    float prev_rlat = 0., prev_rlon = 0.;
                    RoutePoint *prev_pConfPoint = NULL;
                    
                    wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
                    while( node ) {
                        RoutePoint *prp = node->GetData();
                        
                        if( ip )
                            pSelect->AddSelectableRouteSegment( prev_rlat, prev_rlon, prp->m_lat,
                                                                prp->m_lon, prev_pConfPoint, prp, pTentRoute );
                        pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
                        prev_rlat = prp->m_lat;
                        prev_rlon = prp->m_lon;
                        prev_pConfPoint = prp;
                        
                        ip++;
                        
                        node = node->GetNext();
                    }
    }
    else {
        
        // walk the route, deleting points used only by this route
        wxRoutePointListNode *pnode = ( pTentRoute->pRoutePointList )->GetFirst();
        while( pnode ) {
            RoutePoint *prp = pnode->GetData();
            
            // check all other routes to see if this point appears in any other route
            Route *pcontainer_route = g_pRouteMan->FindRouteContainingWaypoint( prp );
            
            if( pcontainer_route == NULL ) {
                prp->m_bIsInRoute = false; // Take this point out of this (and only) track/route
                if( !prp->m_bKeepXRoute ) {
                    pConfig->m_bSkipChangeSetUpdate = true;
                    pConfig->DeleteWayPoint( prp );
                    pConfig->m_bSkipChangeSetUpdate = false;
                    delete prp;
                }
            }
            
            pnode = pnode->GetNext();
        }
        
        delete pTentRoute;
    }
    return bAddroute;
}
                       
static bool  InsertTrack( Track *pTentTrack, bool bApplyChanges = false )
{
    if(!pTentTrack)
        return false;
    
    bool bAddtrack = true;
    //    If the track has only 1 point, don't load it.
    //    This usually occurs if some points were discarded as being co-incident.
    if( !bApplyChanges && pTentTrack->GetnPoints() < 2 )
        bAddtrack = false;
    
    //    TODO  All this trouble for a tentative track.......Should make some Track methods????
    if( bAddtrack ) {
        pTrackList->Append( pTentTrack );
        
               
        //    Do the (deferred) calculation of Track BBox
//        pTentTrack->FinalizeForRendering();
            
        //    Add the selectable points and segments
                
        float prev_rlat = 0., prev_rlon = 0.;
        TrackPoint *prev_pConfPoint = NULL;
                
        for(int i=0; i<pTentTrack->GetnPoints(); i++) {
            TrackPoint *prp = pTentTrack->GetPoint(i);
            
            if( i ) pSelect->AddSelectableTrackSegment( prev_rlat, prev_rlon, prp->m_lat,
                                                        prp->m_lon, prev_pConfPoint, prp, pTentTrack );
                    
            prev_rlat = prp->m_lat;
            prev_rlon = prp->m_lon;
            prev_pConfPoint = prp;
        }
    } else
        delete pTentTrack;

    return bAddtrack;
}                       
                       
static void UpdateRouteA( Route *pTentRoute )
 {
    if( !pTentRoute )
        return;
    if( pTentRoute->GetnPoints() < 2 )
        return;

    //first delete the route to be modified if exists
    Route *pExisting = ::RouteExists( pTentRoute->m_GUID );
    if( pExisting ) {
        pConfig->m_bSkipChangeSetUpdate = true;
        g_pRouteMan->DeleteRoute( pExisting );
        pConfig->m_bSkipChangeSetUpdate = false;
    }

    //create a new route
    Route *pChangeRoute = new Route();
    pRouteList->Append( pChangeRoute );

    //update new route keeping the same gui
    pChangeRoute->m_GUID = pTentRoute->m_GUID;
    pChangeRoute->m_RouteNameString = pTentRoute->m_RouteNameString;
    pChangeRoute->m_RouteStartString = pTentRoute->m_RouteStartString;
    pChangeRoute->m_RouteEndString = pTentRoute->m_RouteEndString;
    pChangeRoute->SetVisible( pTentRoute->IsVisible() );

    //Add points and segments to new route
    int ip = 0;
    float prev_rlat = 0., prev_rlon = 0.;
    RoutePoint *prev_pConfPoint = NULL;

    wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
    while( node ) {
		RoutePoint *prp = node->GetData();

		//if some wpts have been not deleted, that meens they should be used in other routes
		//or are isolated way points so need to be updated
		RoutePoint *ex_rp = ::WaypointExists( prp->m_GUID );
		if( ex_rp ) {
			pSelect->DeleteSelectableRoutePoint(ex_rp);
			ex_rp->m_lat = prp->m_lat;
			ex_rp->m_lon = prp->m_lon;
			ex_rp->SetIconName( prp->GetIconName() );
			ex_rp->m_MarkDescription = prp->m_MarkDescription;
			ex_rp->SetName( prp->GetName() );
            ex_rp->m_TideStation = prp->m_TideStation;
            ex_rp->SetPlannedSpeed(prp->GetPlannedSpeed());
			pChangeRoute->AddPoint( ex_rp );
			pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, ex_rp);

		} else {
			pChangeRoute->AddPoint( prp );
			pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
		}

        if( ip )
			pSelect->AddSelectableRouteSegment( prev_rlat, prev_rlon, prp->m_lat,
					prp->m_lon, prev_pConfPoint, prp, pChangeRoute );
        prev_rlat = prp->m_lat;
        prev_rlon = prp->m_lon;
        prev_pConfPoint = prp;

        ip++;

        node = node->GetNext();
	}
	//    Do the (deferred) calculation of BBox
    pChangeRoute->FinalizeForRendering();
}

                       
bool NavObjectCollection1::CreateNavObjGPXPoints( void )
{
    
    //    Iterate over the Routepoint list, creating Nodes for
    //    Routepoints that are not in any Route
    //    as indicated by m_bIsolatedMark == false

    if(!pWayPointMan)
        return false;

    wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
    
    RoutePoint *pr;
    
    while( node ) {
        pr = node->GetData();
        
        if( ( pr->m_bIsolatedMark ) && !( pr->m_bIsInLayer ) && !(pr->m_btemp) )
        {
            GPXCreateWpt(m_gpx_root.append_child("wpt"), pr, OPT_WPT);
        }
        node = node->GetNext();
    }
    
    return true;
}

bool NavObjectCollection1::CreateNavObjGPXRoutes( void )
{
    // Routes
    if(!pRouteList)
        return false;
    
    wxRouteListNode *node1 = pRouteList->GetFirst();
    while( node1 ) {
        Route *pRoute = node1->GetData();
        
        if( !pRoute->m_bIsInLayer && !pRoute->m_btemp )
            GPXCreateRoute(m_gpx_root.append_child("rte"), pRoute);
        node1 = node1->GetNext();
    }
    
    return true;
}

bool NavObjectCollection1::CreateNavObjGPXTracks( void )
{
    // Tracks
    wxTrackListNode *node1 = pTrackList->GetFirst();
    while( node1 ) {
        Track *pTrack = node1->GetData();
        
        if( pTrack->GetnPoints() ) {
            if( !pTrack->m_bIsInLayer && !pTrack->m_btemp ) 
                GPXCreateTrk(m_gpx_root.append_child("trk"), pTrack, 0);
        }
        node1 = node1->GetNext();
    }
    
    return true;
}


bool NavObjectCollection1::CreateAllGPXObjects()
{
    SetRootGPXNode();
    
    CreateNavObjGPXPoints();
    CreateNavObjGPXRoutes();
    CreateNavObjGPXTracks();
    
    return true;
}

bool NavObjectCollection1::AddGPXRoute(Route *pRoute)
{
    SetRootGPXNode();
    GPXCreateRoute(m_gpx_root.append_child("rte"), pRoute);
    return true;
}

bool NavObjectCollection1::AddGPXTrack(Track *pTrk)
{
    SetRootGPXNode();
    GPXCreateTrk(m_gpx_root.append_child("trk"), pTrk, 0 );
    return true;
}

bool NavObjectCollection1::AddGPXWaypoint(RoutePoint *pWP )
{
    SetRootGPXNode();
    GPXCreateWpt(m_gpx_root.append_child("wpt"), pWP, OPT_WPT);
    return true;
}

void NavObjectCollection1::AddGPXRoutesList( RouteList *pRoutes )
{
    SetRootGPXNode();
    
    wxRouteListNode* pRoute = pRoutes->GetFirst();
    while (pRoute) {
        Route* pRData = pRoute->GetData();
        AddGPXRoute( pRData);
        pRoute = pRoute->GetNext();
    }
}

void NavObjectCollection1::AddGPXTracksList( TrackList *pTracks )
{
    SetRootGPXNode();
    
    wxTrackListNode* pTrack = pTracks->GetFirst();
    while (pTrack) {
        Track* pRData = pTrack->GetData();
        AddGPXTrack( pRData);
        pTrack = pTrack->GetNext();
    }
}

bool NavObjectCollection1::AddGPXPointsList( RoutePointList *pRoutePoints )
{
    SetRootGPXNode();
    
    wxRoutePointListNode* pRoutePointNode = pRoutePoints->GetFirst();
    while (pRoutePointNode) {
        RoutePoint* pRP = pRoutePointNode->GetData();
        AddGPXWaypoint( pRP);
        pRoutePointNode = pRoutePointNode->GetNext();
    }
    
    return true;
}



void NavObjectCollection1::SetRootGPXNode(void)
{
    if(!strlen(m_gpx_root.name())) {
        m_gpx_root = append_child("gpx");
        m_gpx_root.append_attribute ( "version" ) = "1.1";
        m_gpx_root.append_attribute ( "creator" ) = "OpenCPN";
        m_gpx_root.append_attribute( "xmlns:xsi" ) = "http://www.w3.org/2001/XMLSchema-instance";
        m_gpx_root.append_attribute( "xmlns" ) = "http://www.topografix.com/GPX/1/1";
        m_gpx_root.append_attribute( "xmlns:gpxx" ) =  "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
        m_gpx_root.append_attribute( "xsi:schemaLocation" ) = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd";
        m_gpx_root.append_attribute( "xmlns:opencpn" ) = "http://www.opencpn.org";
    }
}
        
bool NavObjectCollection1::IsOpenCPN()
{
    for (pugi::xml_attribute attr = root().first_child().first_attribute(); attr; attr = attr.next_attribute())
    if( !strcmp(attr.name(), "creator") && !strcmp(attr.value(), "OpenCPN") )
        return true;
    return false;
}
            
bool NavObjectCollection1::SaveFile( const wxString filename )
{
    save_file(filename.fn_str(), "  ");
    return true;
}

bool NavObjectCollection1::LoadAllGPXObjects( bool b_full_viz, int &wpt_duplicates, bool b_compute_bbox  )
{
    wpt_duplicates = 0;
    pugi::xml_node objects = this->child("gpx");
    
    for (pugi::xml_node object = objects.first_child(); object; object = object.next_sibling())
    {
        if( !strcmp(object.name(), "wpt") ) {
            RoutePoint *pWp = ::GPXLoadWaypoint1( object, _T("circle"), _T(""), b_full_viz, false, false, 0 );
            
            pWp->m_bIsolatedMark = true;      // This is an isolated mark
            RoutePoint *pExisting = WaypointExists( pWp->GetName(), pWp->m_lat, pWp->m_lon );
            if( !pExisting ) {
                    if( NULL != pWayPointMan )
                        pWayPointMan->AddRoutePoint( pWp );
                     pSelect->AddSelectableRoutePoint( pWp->m_lat, pWp->m_lon, pWp );
                     LLBBox wptbox;
                     wptbox.Set(pWp->m_lat, pWp->m_lon, pWp->m_lat, pWp->m_lon);
                     BBox.Expand(wptbox);
            }
            else {
                delete pWp;
                wpt_duplicates++;
            }
        }
        else
            if( !strcmp(object.name(), "trk") ) {
                Track *pTrack = GPXLoadTrack1( object, b_full_viz, false, false, 0);
                if (InsertTrack( pTrack ) && b_compute_bbox && pTrack->IsVisible()) {
                        //BBox.Expand(pTrack->GetBBox());
                }
            }
            else
                if( !strcmp(object.name(), "rte") ) {
                    Route *pRoute = GPXLoadRoute1( object, b_full_viz, false, false, 0, false );
                    if (InsertRouteA( pRoute ) && b_compute_bbox && pRoute->IsVisible()) {
                        BBox.Expand(pRoute->GetBBox());
                    }
                }
                
                
    }
    
    return true;
}

int NavObjectCollection1::LoadAllGPXObjectsAsLayer(int layer_id, bool b_layerviz, wxCheckBoxState b_namesviz)
{
    if(!pWayPointMan)
        return 0;
    
    int n_obj = 0;
    pugi::xml_node objects = this->child("gpx");
    
    for (pugi::xml_node object = objects.first_child(); object; object = object.next_sibling())
    {
        if( !strcmp(object.name(), "wpt") ) {
            RoutePoint *pWp = ::GPXLoadWaypoint1( object, _T("circle"), _T(""), b_namesviz != wxCHK_UNDETERMINED, true, b_layerviz, layer_id );
            if( b_namesviz != wxCHK_UNDETERMINED ) {
                pWp->SetNameShown( b_namesviz == wxCHK_CHECKED );
            }
            pWp->m_bIsolatedMark = true;      // This is an isolated mark
            pWayPointMan->AddRoutePoint( pWp );
            pSelect->AddSelectableRoutePoint( pWp->m_lat, pWp->m_lon, pWp );
            n_obj++;
        }
        else{
            if( !strcmp(object.name(), "trk") ) {
                Track *pTrack = GPXLoadTrack1( object, false, true, b_layerviz, layer_id);
                n_obj++;
                InsertTrack( pTrack );
            }
            else
                if( !strcmp(object.name(), "rte") ) {
                    Route *pRoute = GPXLoadRoute1( object, true, true, b_layerviz, layer_id, false );
                    n_obj++;
                    InsertRouteA( pRoute );
                }
        }   
    }
    
    return n_obj;
}







NavObjectChanges::NavObjectChanges()
: NavObjectCollection1()
{
    m_changes_file = 0;
    m_bdirty = false;
}



NavObjectChanges::NavObjectChanges(wxString file_name)
    : NavObjectCollection1()
{
    m_filename = file_name;
    
    m_changes_file = fopen(m_filename.mb_str(), "a");
    m_bdirty = false;
}

NavObjectChanges::~NavObjectChanges()
{
    if(m_changes_file)
        fclose(m_changes_file);

    if( ::wxFileExists( m_filename ) )
        ::wxRemoveFile( m_filename );
        
}

void NavObjectChanges::AddRoute( Route *pr, const char *action )
{
    SetRootGPXNode();
    
    pugi::xml_node object = m_gpx_root.append_child("rte");
    GPXCreateRoute(object, pr );
    
    pugi::xml_node xchild = object.child("extensions");
    //FIXME  What if extensions do not exist?
    pugi::xml_node child = xchild.append_child("opencpn:action");
    child.append_child(pugi::node_pcdata).set_value(action);

    pugi::xml_writer_file writer(m_changes_file);
    object.print(writer, " ");
    fflush(m_changes_file);
    m_bdirty = true;
}

void NavObjectChanges::AddTrack( Track *pr, const char *action )
{
    SetRootGPXNode();
    
    pugi::xml_node object = m_gpx_root.append_child("trk");
    GPXCreateTrk(object, pr, RT_OUT_NO_RTPTS );         // emit a void track, no waypoints
    
    pugi::xml_node xchild = object.child("extensions");
    pugi::xml_node child = xchild.append_child("opencpn:action");
    child.append_child(pugi::node_pcdata).set_value(action);

    pugi::xml_writer_file writer(m_changes_file);
    object.print(writer, " ");
    fflush(m_changes_file);
    m_bdirty = true;
}

void NavObjectChanges::AddWP( RoutePoint *pWP, const char *action )
{
    SetRootGPXNode();
    
    pugi::xml_node object = m_gpx_root.append_child("wpt");
    GPXCreateWpt(object, pWP, OPT_WPT);

    pugi::xml_node xchild = object.child("extensions");
    pugi::xml_node child = xchild.append_child("opencpn:action");
    child.append_child(pugi::node_pcdata).set_value(action);

    pugi::xml_writer_file writer(m_changes_file);
    object.print(writer, " ");
    fflush(m_changes_file);
    m_bdirty = true;
}

void NavObjectChanges::AddTrackPoint( TrackPoint *pWP, const char *action, const wxString& parent_GUID )
{
    SetRootGPXNode();
    
    pugi::xml_node object = m_gpx_root.append_child("tkpt");
    GPXCreateTrkpt(object, pWP, OPT_TRACKPT);

    pugi::xml_node xchild = object.append_child("extensions");
    
    pugi::xml_node child = xchild.append_child("opencpn:action");
    child.append_child(pugi::node_pcdata).set_value(action);
    
    pugi::xml_node gchild = xchild.append_child("opencpn:track_GUID");
    gchild.append_child(pugi::node_pcdata).set_value(parent_GUID.mb_str());

    pugi::xml_writer_file writer(m_changes_file);
    object.print(writer, " ");
    fflush(m_changes_file);
    m_bdirty = true;
}


bool NavObjectChanges::ApplyChanges(void)
{
    //Let's reconstruct the unsaved changes
    
    pugi::xml_node object = this->first_child();
    
    while(strlen(object.name()))
    {
        if( !strcmp(object.name(), "wpt") && pWayPointMan) {
            RoutePoint *pWp = ::GPXLoadWaypoint1( object, _T("circle"), _T(""), false, false, false, 0 );
            
            pWp->m_bIsolatedMark = true;
            RoutePoint *pExisting = WaypointExists( pWp->m_GUID );
                
            pugi::xml_node xchild = object.child("extensions");
            pugi::xml_node child = xchild.child("opencpn:action");
                
            if(!strcmp(child.first_child().value(), "add") ){
                if( !pExisting )
                    pWayPointMan->AddRoutePoint( pWp );
                pSelect->AddSelectableRoutePoint( pWp->m_lat, pWp->m_lon, pWp );
            }
                
            else if(!strcmp(child.first_child().value(), "update") ){
                if( pExisting )
                    pWayPointMan->RemoveRoutePoint( pExisting );
                pWayPointMan->AddRoutePoint( pWp );
                pSelect->AddSelectableRoutePoint( pWp->m_lat, pWp->m_lon, pWp );
            }

            else if(!strcmp(child.first_child().value(), "delete") ){
                if( pExisting )
                    pWayPointMan->DestroyWaypoint( pExisting, false );
            }
            else
                delete pWp;
        }
        else
            if( !strcmp(object.name(), "trk") && g_pRouteMan) {
                Track * pTrack = GPXLoadTrack1( object, false, false, false, 0);

                if(pTrack ) {
                    pugi::xml_node xchild = object.child("extensions");
                    pugi::xml_node child = xchild.child("opencpn:action");
                    
                    Track *pExisting = TrackExists( pTrack->m_GUID );
                    if(!strcmp(child.first_child().value(), "update") ){
                         if( pExisting ) {
                             pExisting->SetName(pTrack->GetName());
                             pExisting->m_TrackStartString = pTrack->m_TrackStartString;
                             pExisting->m_TrackEndString = pTrack->m_TrackEndString;
                        }
                    }

                    else if(!strcmp(child.first_child().value(), "delete") ){
                        if( pExisting )
                            g_pRouteMan->DeleteTrack( pExisting );
                    }

                    else if(!strcmp(child.first_child().value(), "add") ){
                        if( !pExisting )
                            ::InsertTrack( pTrack, true );
                    }

                    else
                        delete pTrack;
                }
            }
            
            else
                if( !strcmp(object.name(), "rte") && g_pRouteMan) {
                    Route *pRoute = GPXLoadRoute1( object, false, false, false, 0, true );
                    
                    if(pRoute ) {
                        pugi::xml_node xchild = object.child("extensions");
                        pugi::xml_node child = xchild.child("opencpn:action");

                        if(!strcmp(child.first_child().value(), "add") ){
                            ::UpdateRouteA( pRoute );
                        }                    
                    
                        else if(!strcmp(child.first_child().value(), "update") ){
                            ::UpdateRouteA( pRoute );
                        }

                        else if(!strcmp(child.first_child().value(), "delete") ){
                            Route *pExisting = RouteExists( pRoute->m_GUID );
                            if(pExisting){
                                pConfig->m_bSkipChangeSetUpdate = true;
                                g_pRouteMan->DeleteRoute( pExisting );
                                pConfig->m_bSkipChangeSetUpdate = false;
                            }
                        }
                    
                        else
                            delete pRoute;
                    }
                }
            else
                if( !strcmp(object.name(), "tkpt") && pWayPointMan) {
                    TrackPoint *pWp = ::GPXLoadTrackPoint1( object );
                    
//                        RoutePoint *pExisting = WaypointExists( pWp->GetName(), pWp->m_lat, pWp->m_lon );
                        
                    pugi::xml_node xchild = object.child("extensions");
                    pugi::xml_node child = xchild.child("opencpn:action");

                    pugi::xml_node guid_child = xchild.child("opencpn:track_GUID");
                    wxString track_GUID(guid_child.first_child().value(), wxConvUTF8);

                    Track *pExistingTrack = TrackExists( track_GUID );
                        
                    if(!strcmp(child.first_child().value(), "add") && pExistingTrack ) {
                        pExistingTrack->AddPoint( pWp );
                        pWp->m_GPXTrkSegNo = pExistingTrack->GetCurrentTrackSeg() + 1;
                    }
                    else
                        delete pWp;
                }

        object = object.next_sibling();
    }
    // Check to make sure we haven't loaded tracks with less than 2 points
    wxTrackListNode *node1 = pTrackList->GetFirst();
    while( node1 ) {
        Track *pTrack = node1->GetData();
        if( pTrack->GetnPoints() < 2 ) {
            wxTrackListNode *tnode = node1->GetNext();
            delete pTrack;
            pTrackList->DeleteNode(node1);
            node1 = tnode;
        } else
            node1 = node1->GetNext();
    }
    
    return true;
}
