/* Copyright 2002 Daniel Egnor.  See LICENSE file.
 *
 * This is a simple command-line wrapper to the code in idx-find.cc,
 * which allows queries to be executed and output reported.  It is also
 * invoked from the query.cgi CGI script. */

#include "idx.h"

extern "C" {
#include "geo.h"
#include "io.h"
#include "geodesy.h"
}

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <vector>

#include <ctype.h>

using namespace std;

int main(int argc, char **argv) {
  if (argc < 3) {
    cerr << "usage: " << argv[0] 
         << " index-file map-file [-distance 'address'] keyword ..." << endl;
    return 2;
  }

  io_file * const index = io_open(argv[1]);
  if (NULL == index) return 1;

  io_file * const map = io_open(argv[2]);
  if (NULL == map) return 1;

  vector<Cursor *> terms;
  bool do_location = false;
  geo_location center;
  double radius = 0;
  for (int i = 3; i < argc; ++i) {
    char key[9];

    if ('-' == *argv[i]) {
      radius = strtod(argv[i] + 1, NULL);
      if (0 == radius) {
        cerr << "error: illegal distance \"" << argv[i] + 1 << "\"" << endl;
        return 2;
      }
      if (argc == ++i) {
        cerr << "error: missing address" << endl;
        return 2;
      }

      if (!geo_find(map, argv[i], strlen(argv[i]), &center)) {
        cout << "badaddress" << endl;
        cout << "address \"" << argv[i] << "\" not found" << endl;
        cout << endl;
        return 1;
      }

      terms.push_back(new GeoCursor(
        index, center.at.longitude, center.at.latitude, radius));
      do_location = true;
      continue;
    }

    int k = 0;
    while (k < 9 && *argv[i])
      if (isalnum(*argv[i]))
        key[k++] = *argv[i]++;
      else
        ++(argv[i]);

    while (k < 9)
      key[k++] = ' ';

    terms.push_back(new TermCursor(index, key));
  }

  int doc = -1;
  vector< pair<double, Document> > results;
  Cursor *docs = new AndCursor(terms.begin(), terms.end());
  while ((doc = docs->Get(doc + 1)) > 0) {
    Document document(index, doc);
    double distance = 0;
    if (do_location)
      for (int i = 0; i < document.size(); ++i) {
        const Location location = document[i];
        const double d = geo_distance(
          center.at.latitude, center.at.longitude,
          location.Latitude(), location.Longitude());
        if (0 == i || d < distance) distance = d;
      }

    results.push_back(make_pair(distance, Document(index, doc)));
  }

  
  if (results.empty()) {
    cout << "noresults" << endl;
    cout << "no results found" << endl;
  }
  else {
    cout << "ok" << endl;
    cout << results.size() << " results found" << endl;
  }

  cout << setprecision(8);
  if (do_location) {
    cout << radius << ":" 
         << center.at.latitude << ":"
         << center.at.longitude << ":"
         << center.at.address << " " << center.street_name;
    if (center.city_name[0])
      cout << ", " << center.city_name;
    if (center.state_name[0])
      cout << ", " << center.state_name;
    if (center.zip_code)
      cout << " " << setw(5) << setfill('0') << center.zip_code;
    cout << setw(0) << setfill(' ') << endl;
  }

  cout << endl;

  sort(results.begin(), results.end());
  vector< pair<double, Document> >::const_iterator i;
  vector< pair<double, Location> > spots;
  for (i = results.begin(); i != results.end(); ++i) {
    const Document &document = i->second;
    cout << document.URL() << endl;

    const string title = document.Title();
    cout << (title.empty() ? "-" : title) << endl;

    if (do_location) {
      spots.clear();
      for (int i = 0; i < document.size(); ++i) {
        const Location loc = document[i];
        const double distance = geo_distance(
          center.at.latitude, center.at.longitude,
          loc.Latitude(), loc.Longitude());
        if (distance > radius) continue;
        spots.push_back(make_pair(distance, loc));
      }

      sort(spots.begin(), spots.end());
      vector< pair<double, Location> >::const_iterator l;
      for (l = spots.begin(); l != spots.end(); ++l) {
        const Location &loc = l->second;
        cout << l->first << ":" 
             << loc.Latitude() << ":" 
             << loc.Longitude() << ":"
             << loc.Address() << endl;
      }
    }

    cout << endl;
  }

  return 0;
}

