// fixlist.cxx -- fix list management class
//
// Written by Curtis Olson, started April 2000.
//
// Copyright (C) 2000  Curtis L. Olson - http://www.flightgear.org/~curt
//
// 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.
//
// $Id: fixlist.cxx,v 1.16 2008/09/10 08:54:50 ehofman Exp $


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <algorithm>

#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/math/sg_geodesy.hxx>

#include "fixlist.hxx"
#include "Navaids/fix.hxx"
#include "Airports/simple.hxx"

FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) :
  FGPositioned(FIX, aIdent, aPos)
{
}

// Constructor
FGFixList::FGFixList( void ) {
}


// Destructor
FGFixList::~FGFixList( void ) {
}


// load the navaids and build the map
bool FGFixList::init( SGPath path ) {
    fixlist.erase( fixlist.begin(), fixlist.end() );

    sg_gzifstream in( path.str() );
    if ( !in.is_open() ) {
        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() );
        exit(-1);
    }

    // toss the first two lines of the file
    in >> skipeol;
    in >> skipeol;

    // read in each remaining line of the file
    while ( ! in.eof() ) {
      double lat, lon;
      string ident;
      in >> lat >> lon >> ident;
      if (lat > 95) break;

      FGFix* fix = new FGFix(ident, SGGeod::fromDeg(lon, lat));
      fixlist.insert(std::make_pair(fix->ident(), fix));
      in >> skipcomment;
    }
    return true;
}


// query the database for the specified fix, lon and lat are in
// degrees, elev is in meters
bool FGFixList::query( const string& ident, FGFix* &fix ) {
    fix_map_const_iterator it = fixlist.find(ident);
    if ( it != fixlist.end() ) {
        fix = it->second;
        return true;
    } else {
        return false;
    }
}


// query the database for the specified fix, lon and lat are in
// degrees, elev is in meters
bool FGFixList::query_and_offset( const string& ident, double lon, double lat,
                                  double elev, FGFix* &fix, double *heading,
                                  double *dist )
{
    std::pair<fix_map_const_iterator, fix_map_const_iterator> range = fixlist.equal_range(ident);

    if (range.first == range.second) {
        return false;
    }

    double min_s = -1.0;
    for (fix_map_const_iterator current = range.first; current != range.second; ++current) {
        double az1, az2, s;
        geo_inverse_wgs_84( elev, lat, lon,
                        current->second->get_lat(), current->second->get_lon(),
                        &az1, &az2, &s );
        // cout << "  dist = " << s << endl;
        if (min_s < 0 || s < min_s) {
            *heading = az2;
            *dist = s;
            min_s = s;
            fix = current->second;
        }
    }

    return true;
}

const FGFix* FGFixList::search(const string& ident)
{
  fix_map_iterator itr = fixlist.find(ident);
  if (itr == fixlist.end()) {
    return NULL;
  }
  
  return itr->second;
}

class orderingFunctor
{
public:
  orderingFunctor(FGIdentOrdering* aOrder) :
    mOrdering(aOrder)
  { assert(aOrder); }
  
  bool operator()(const fix_map_type::value_type& aA, const std::string& aB) const
  {
    return mOrdering->compare(aA.first,aB);
  }

  bool operator()(const std::string& aA, const fix_map_type::value_type& aB) const
  {
    return mOrdering->compare(aA, aB.first);
  }

  bool operator()(const fix_map_type::value_type& aA, const fix_map_type::value_type& aB) const
  {
    return mOrdering->compare(aA.first, aB.first);
  }
  
private:
  FGIdentOrdering* mOrdering;
};

const FGFix* FGFixList::findFirstByIdent( const string& ident, FGIdentOrdering* aOrder)
{
  fix_map_iterator itr;
  if (aOrder) {
    orderingFunctor func(aOrder);
    itr = std::lower_bound(fixlist.begin(),fixlist.end(), ident, func);
  } else {
    itr = fixlist.lower_bound(ident);
  }
  
  if (itr == fixlist.end()) {
    return NULL;
  }
  
  return itr->second;
}
