text: /*
   *
   * Copyright 2015 gRPC authors.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   */
  
  const async = require('async');
  const fs = require('fs');
  const parseArgs = require('minimist');
  const path = require('path');
  const _ = require('lodash');
  const grpc = require('grpc');
  
  // Import the protocol buffer node_module such that the importpath is
  // "{package}/{rule-name}".  In this case the {package} is
  // "node/example/routeguide" (because there is a
  // node/example/routeguide/BUILD.bazel file) and the {rule-name} is "routeguide"
  // (because the BUILD.bazel file has a node_grpc_library target with name =
  // "routeguide").
  const routeguide = require('node/example/routeguide/routeguide')
  
  // At this point V8 has just loaded an index.js file like this:
  //
  // module.exports = {'routeguide_pb':
  //  require('./node/example/routeguide/routeguide_pb/example/proto/routeguide_pb.js'),
  //  'routeguide_grpc_pb':
  //  require('./node/example/routeguide/routeguide_pb/example/proto/routeguide_grpc_pb.js'),
  //}
  //
  // Internally, this node_module has copied over the generated files as they
  // appeared in the bazel-genfiles directly such that these generated files can
  // satisfy their own require statements successfully.  The index.js exports and
  // object that makes them available by their basename(s).
  //
  
  // The protobuf 'messages' for the proto file 'routeguide.proto' are therefore
  // imported by:
  //
  const messages = routeguide.routeguide_pb;
  //console.log("routeguide messages", messages);
  
  // The protobuf 'services' for the proto file 'routeguide.proto' are therefore
  // imported by:
  //
  const services = routeguide.routeguide_grpc_pb;
  //console.log("routeguide services", services);
  
  // This is included as data in the client node_module, so we can load 
  // this database as a constant.
  const featureList = require('./example/proto/routeguide_features.json');
  console.log(`Loaded ${featureList.length} from feature database`);
  
  const client = new services.RouteGuideClient(
    'localhost:50051',
    grpc.credentials.createInsecure());
  
  const COORD_FACTOR = 1e7;
  
  /**
   * Run the getFeature demo. Calls getFeature with a point known to have a
   * feature and a point known not to have a feature.
   * @param {function} callback Called when this demo is complete
   */
  function runGetFeature(callback) {
    const next = _.after(2, callback);
    function featureCallback(error, feature) {
      if (error) {
        console.warn("ERROR occured while attempting to get feature", error);
        if (callback) {
          callback(error);
          callback = undefined  
        }
        return;
      }
  
      // console.log("Feature", feature.toObject());
  
      if (feature.getName() && feature.getName() != "undefined") {
        console.log('Found feature called "' + feature.getName() + '" at ' +
            feature.getLocation().getLatitude()/COORD_FACTOR + ', ' +
            feature.getLocation().getLongitude()/COORD_FACTOR);
      } else {
        console.log('Found no feature at ' +
            feature.getLocation().getLatitude()/COORD_FACTOR + ', ' +
            feature.getLocation().getLongitude()/COORD_FACTOR);
      }
      next();
    }
    const point1 = newPoint(409146138, -746188906);
    const point2 = newPoint(1, 1);
    client.getFeature(point1, featureCallback);
    client.getFeature(point2, featureCallback);
  }
  
  /**
   * @param {number} latitude
   * @param {number} longitude
   * @returns {!messages.Point}
   */
  function newPoint(latitude, longitude) {
    const point = new messages.Point()
    point.setLatitude(latitude);
    point.setLongitude(longitude);
    return point;
  }
  
  /**
   * @param {!messages.Point} lo
   * @param {!messages.Point} hi
   * @returns {!messages.Rectangle}
   */
  function newRectangle(lo, hi) {
    const rect = new messages.Rectangle()
    rect.setLo(lo);
    rect.setHi(hi);
    return rect;
  }
  
  /**
   * @param {!messages.Point} point
   * @param {string} message
   * @returns {!messages.Note}
   */
  function newNote(point, message) {
    const note = new messages.RouteNote()
    note.setLocation(point);
    note.setMessage(message);
    return note;
  }
  
  /**
   * Run the listFeatures demo. Calls listFeatures with a rectangle containing all
   * of the features in the pre-generated database. Prints each response as it
   * comes in.
   * @param {function} callback Called when this demo is complete
   */
  function runListFeatures(callback) {
    const rectangle = newRectangle(
      newPoint(400000000, -750000000),
      newPoint(420000000, -73000000));
    console.log('Looking for features between 40, -75 and 42, -73');
    const call = client.listFeatures(rectangle);
    call.on('data', function(feature) {
      console.log('Found feature called "' + feature.getName() + '" at ' +
            feature.getLocation().getLatitude()/COORD_FACTOR + ', ' +
            feature.getLocation().getLongitude()/COORD_FACTOR);
    });
    call.on('end', callback);
  }
  
  /**
   * Run the recordRoute demo. Sends several randomly chosen points from the
   * pre-generated feature database with a constiable delay in between. Prints the
   * statistics when they are sent from the server.
   * @param {function} callback Called when this demo is complete
   */
  function runRecordRoute(callback) {
  
    const num_points = 10;
  
    const call = client.recordRoute(function(error, stats) {
      if (error) {
        callback(error);
        return;
      }
      console.log('Finished trip with', stats.getPointCount(), 'points');
      console.log('Passed', stats.getFeatureCount(), 'features');
      console.log('Traveled', stats.getDistance(), 'meters');
      console.log('It took', stats.getElapsedTime(), 'seconds');
      callback();
    });
  
    /**
     * Constructs a function that asynchronously sends the given point and then
     * delays sending its callback
     * @param {number} lat The latitude to send
     * @param {number} lng The longitude to send
     * @return {function(function)} The function that sends the point
     */
    function pointSender(lat, lng) {
      /**
       * Sends the point, then calls the callback after a delay
       * @param {function} callback Called when complete
       */
      return function(callback) {
        console.log('Visiting point ' + lat/COORD_FACTOR + ', ' +
            lng/COORD_FACTOR);
        call.write(newPoint(lat, lng));
        _.delay(callback, _.random(100, 500));
      };
    }
  
    const pointSenders = [];
  
    for (let i = 0; i < num_points; i++) {
      const randIndex = _.random(0, featureList.length - 1)
      console.log("randomIndex", randIndex);
      const randomPointJson = featureList[randIndex];
      const randomPoint = newPoint(randomPointJson.location.latitude, randomPointJson.location.longitude)
      console.log("randomPoint", randomPointJson, randomPoint.toObject());
      pointSenders[i] = pointSender(randomPoint.getLatitude(),
                                      randomPoint.getLongitude());
    }
  
    async.series(pointSenders, function() {
      call.end();
    });
  
  }
  
  /**
   * Run the routeChat demo. Send some chat messages, and print any chat messages
   * that are sent from the server.
   * @param {function} callback Called when the demo is complete
   */
  function runRouteChat(callback) {
    const call = client.routeChat();
    call.on('data', function(note) {
      console.log('Got message "' + note.getMessage() + '" at ' +
          note.getLocation().getLatitude() + ', ' + note.getLocation().getLongitude());
    });
  
    call.on('end', callback);
  
    const notes = [
      newNote(newPoint(0, 0), 'First message'),
      newNote(newPoint(0, 1), 'Second message'),
      newNote(newPoint(1, 0), 'Third message'),
      newNote(newPoint(0, 0), 'Fourth message'),
    ]
    for (let i = 0; i < notes.length; i++) {
      const note = notes[i];
      console.log('Sending message "' + note.getMessage() + '" at ' +
          note.getLocation().getLatitude() + ', ' + note.getLocation().getLongitude());
      call.write(note);
    }
    call.end();
  }
  
  /**
   * Run all of the demos in order
   */
  function main() {
    async.series([
      runGetFeature,
      runListFeatures,
      runRecordRoute,
      runRouteChat
    ]);
  }
  
  if (require.main === module) {
    main();
  }
  
  exports.runGetFeature = runGetFeature;
  
  exports.runListFeatures = runListFeatures;
  
  exports.runRecordRoute = runRecordRoute;
  
  exports.runRouteChat = runRouteChat;