/**
   * @param stop
   * @param format {0} - ref, {1} - type, {2} - name, {3} - name_en
   * @return null if something goes wrong
   */
  @Override
  public List<String> getRouteDescriptionsForStop(TransportStop stop, String format) {
    assert acceptTransportStop(stop);
    long now = System.currentTimeMillis();

    MessageFormat f = new MessageFormat(format);
    List<String> res = new ArrayList<String>();
    try {
      List<TransportRoute> routes = file.getTransportRouteDescriptions(stop);
      if (routes != null) {
        for (TransportRoute route : routes) {
          res.add(
              f.format(
                  new String[] {
                    route.getRef() + "",
                    route.getType() + "",
                    route.getName() + "",
                    route.getEnName() + ""
                  })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
        }
      }
    } catch (IOException e) {
      log.error("Disk error ", e); // $NON-NLS-1$
    }

    if (log.isDebugEnabled()) {
      log.debug(
          String.format(
              "Search for stop %s done in %s ms found %s.", //$NON-NLS-1$
              stop.getId() + "", System.currentTimeMillis() - now, res.size())); // $NON-NLS-1$
    }
    return res;
  }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View row = convertView;
      if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.search_transport_list_item, parent, false);
      }
      LatLon locationToGo = getLocationToGo();
      LatLon locationToStart = getLocationToStart();

      TextView label = (TextView) row.findViewById(R.id.label);
      ImageView icon = (ImageView) row.findViewById(R.id.search_icon);
      RouteInfoLocation stop = getItem(position);

      TransportRoute route = stop.getRoute();
      StringBuilder labelW = new StringBuilder(150);
      labelW.append(route.getType()).append(" ").append(route.getRef()); // $NON-NLS-1$
      labelW.append(" - ["); // $NON-NLS-1$

      if (locationToGo != null) {
        labelW.append(
            OsmAndFormatter.getFormattedDistance(
                stop.getDistToLocation(), SearchTransportActivity.this));
      } else {
        labelW.append(getString(R.string.transport_search_none));
      }
      labelW.append("]\n").append(route.getName(settings.usingEnglishNames())); // $NON-NLS-1$

      // TODO icons
      if (locationToGo != null && stop.getDistToLocation() < 400) {
        icon.setImageResource(R.drawable.opened_poi);
      } else {
        icon.setImageResource(R.drawable.poi);
      }

      int dist =
          locationToStart == null
              ? 0
              : (int) (MapUtils.getDistance(stop.getStart().getLocation(), locationToStart));
      String distance =
          OsmAndFormatter.getFormattedDistance(dist, SearchTransportActivity.this)
              + " "; //$NON-NLS-1$
      label.setText(distance + labelW.toString(), TextView.BufferType.SPANNABLE);
      ((Spannable) label.getText())
          .setSpan(
              new ForegroundColorSpan(getResources().getColor(R.color.color_distance)),
              0,
              distance.length() - 1,
              0);
      return (row);
    }
 private void writeRouteStops(TransportRoute r, List<TransportStop> stops, boolean direction)
     throws SQLException {
   int i = 0;
   for (TransportStop s : stops) {
     if (!visitedStops.contains(s.getId())) {
       transStopsStat.setLong(1, s.getId());
       transStopsStat.setDouble(2, s.getLocation().getLatitude());
       transStopsStat.setDouble(3, s.getLocation().getLongitude());
       transStopsStat.setString(4, s.getName());
       transStopsStat.setString(5, s.getEnName());
       int x = (int) MapUtils.getTileNumberX(24, s.getLocation().getLongitude());
       int y = (int) MapUtils.getTileNumberY(24, s.getLocation().getLatitude());
       addBatch(transStopsStat);
       try {
         transportStopsTree.insert(new LeafElement(new Rect(x, y, x, y), s.getId()));
       } catch (RTreeInsertException e) {
         throw new IllegalArgumentException(e);
       } catch (IllegalValueException e) {
         throw new IllegalArgumentException(e);
       }
       visitedStops.add(s.getId());
     }
     transRouteStopsStat.setLong(1, r.getId());
     transRouteStopsStat.setLong(2, s.getId());
     transRouteStopsStat.setInt(3, direction ? 1 : 0);
     transRouteStopsStat.setInt(4, i++);
     addBatch(transRouteStopsStat);
   }
 }
  private void insertTransportIntoIndex(TransportRoute route) throws SQLException {
    transRouteStat.setLong(1, route.getId());
    transRouteStat.setString(2, route.getType());
    transRouteStat.setString(3, route.getOperator());
    transRouteStat.setString(4, route.getRef());
    transRouteStat.setString(5, route.getName());
    transRouteStat.setString(6, route.getEnName());
    transRouteStat.setInt(7, route.getAvgBothDistance());
    addBatch(transRouteStat);

    writeRouteStops(route, route.getForwardStops(), true);
    writeRouteStops(route, route.getBackwardStops(), false);
  }
  public net.osmand.data.TransportRoute getTransportRoute(int filePointer, TransportIndex ind)
      throws IOException {
    codedIS.seek(filePointer);
    int routeLength = codedIS.readRawVarint32();
    int old = codedIS.pushLimit(routeLength);
    net.osmand.data.TransportRoute dataObject = new net.osmand.data.TransportRoute();
    boolean end = false;
    int name = -1;
    int nameEn = -1;
    int operator = -1;
    int type = -1;
    long rid = 0;
    int rx = 0;
    int ry = 0;
    long did = 0;
    int dx = 0;
    int dy = 0;
    while (!end) {
      int t = codedIS.readTag();
      int tag = WireFormat.getTagFieldNumber(t);
      switch (tag) {
        case 0:
          end = true;
          break;
        case OsmandOdb.TransportRoute.DISTANCE_FIELD_NUMBER:
          dataObject.setDistance(codedIS.readUInt32());
          break;
        case OsmandOdb.TransportRoute.ID_FIELD_NUMBER:
          dataObject.setId(codedIS.readUInt64());
          break;
        case OsmandOdb.TransportRoute.REF_FIELD_NUMBER:
          dataObject.setRef(codedIS.readString());
          break;
        case OsmandOdb.TransportRoute.TYPE_FIELD_NUMBER:
          type = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.NAME_EN_FIELD_NUMBER:
          nameEn = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.NAME_FIELD_NUMBER:
          name = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.OPERATOR_FIELD_NUMBER:
          operator = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.REVERSESTOPS_FIELD_NUMBER:
          int length = codedIS.readRawVarint32();
          int olds = codedIS.pushLimit(length);
          TransportStop stop = readTransportRouteStop(dx, dy, did);
          dataObject.getBackwardStops().add(stop);
          did = stop.getId();
          dx =
              (int) MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, stop.getLocation().getLongitude());
          dy = (int) MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, stop.getLocation().getLatitude());
          codedIS.popLimit(olds);
          break;
        case OsmandOdb.TransportRoute.DIRECTSTOPS_FIELD_NUMBER:
          length = codedIS.readRawVarint32();
          olds = codedIS.pushLimit(length);
          stop = readTransportRouteStop(rx, ry, rid);
          dataObject.getForwardStops().add(stop);
          rid = stop.getId();
          rx =
              (int) MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, stop.getLocation().getLongitude());
          ry = (int) MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, stop.getLocation().getLatitude());
          codedIS.popLimit(olds);
          break;
        default:
          skipUnknownField(t);
          break;
      }
    }
    codedIS.popLimit(old);
    if (name != -1) {
      dataObject.setName(getStringFromStringTable(ind.stringTable, name));
    }
    if (nameEn != -1) {
      dataObject.setEnName(getStringFromStringTable(ind.stringTable, nameEn));
    } else {
      dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
    }

    if (operator != -1) {
      dataObject.setOperator(getStringFromStringTable(ind.stringTable, operator));
    }
    if (type != -1) {
      dataObject.setType(getStringFromStringTable(ind.stringTable, type));
    }
    for (int i = 0; i < 2; i++) {
      List<TransportStop> stops =
          i == 0 ? dataObject.getForwardStops() : dataObject.getBackwardStops();
      for (TransportStop s : stops) {
        if (s.getName().length() > 0) {
          s.setName(getStringFromStringTable(ind.stringTable, s.getName().charAt(0)));
        }
        if (s.getEnName().length() > 0) {
          s.setEnName(getStringFromStringTable(ind.stringTable, s.getEnName().charAt(0)));
        } else {
          s.setEnName(Junidecode.unidecode(s.getName()));
        }
      }
    }

    return dataObject;
  }
  private TransportRoute indexTransportRoute(Relation rel) {
    String ref = rel.getTag(OSMTagKey.REF);
    String route = rel.getTag(OSMTagKey.ROUTE);
    String operator = rel.getTag(OSMTagKey.OPERATOR);

    Relation master = masterRoutes.get(rel.getId());
    if (master != null) {
      if (ref == null) ref = master.getTag(OSMTagKey.REF);
      if (route == null) route = master.getTag(OSMTagKey.ROUTE);
      if (operator == null) operator = master.getTag(OSMTagKey.OPERATOR);
    }

    if (route == null || ref == null) {
      return null;
    }
    if (!acceptedRoutes.contains(route)) {
      return null;
    }
    TransportRoute r = new TransportRoute(rel, ref);
    r.setOperator(operator);
    r.setType(route);

    if (operator != null) {
      route = operator + " : " + route; // $NON-NLS-1$
    }

    final Map<TransportStop, Integer> forwardStops = new LinkedHashMap<TransportStop, Integer>();
    final Map<TransportStop, Integer> backwardStops = new LinkedHashMap<TransportStop, Integer>();
    int currentStop = 0;
    int forwardStop = 0;
    int backwardStop = 0;
    for (Entry<Entity, String> e : rel.getMemberEntities().entrySet()) {
      if (e.getValue().contains("stop")) { // $NON-NLS-1$
        if (e.getKey() instanceof Node) {
          TransportStop stop = new TransportStop(e.getKey());
          boolean forward = e.getValue().contains("forward"); // $NON-NLS-1$
          boolean backward = e.getValue().contains("backward"); // $NON-NLS-1$
          currentStop++;
          if (forward || !backward) {
            forwardStop++;
          }
          if (backward) {
            backwardStop++;
          }
          boolean common = !forward && !backward;
          int index = -1;
          int i = e.getValue().length() - 1;
          int accum = 1;
          while (i >= 0 && Character.isDigit(e.getValue().charAt(i))) {
            if (index < 0) {
              index = 0;
            }
            index = accum * Character.getNumericValue(e.getValue().charAt(i)) + index;
            accum *= 10;
            i--;
          }
          if (index < 0) {
            index = forward ? forwardStop : (backward ? backwardStop : currentStop);
          }
          if (forward || common) {
            forwardStops.put(stop, index);
            r.getForwardStops().add(stop);
          }
          if (backward || common) {
            if (common) {
              // put with negative index
              backwardStops.put(stop, -index);
            } else {
              backwardStops.put(stop, index);
            }

            r.getBackwardStops().add(stop);
          }
        }

      } else if (e.getKey() instanceof Way) {
        r.addWay((Way) e.getKey());
      }
    }
    if (forwardStops.isEmpty() && backwardStops.isEmpty()) {
      return null;
    }
    Collections.sort(
        r.getForwardStops(),
        new Comparator<TransportStop>() {
          @Override
          public int compare(TransportStop o1, TransportStop o2) {
            return forwardStops.get(o1) - forwardStops.get(o2);
          }
        });
    // all common stops are with negative index (reeval them)
    for (TransportStop s : new ArrayList<TransportStop>(backwardStops.keySet())) {
      if (backwardStops.get(s) < 0) {
        backwardStops.put(s, backwardStops.size() + backwardStops.get(s) - 1);
      }
    }
    Collections.sort(
        r.getBackwardStops(),
        new Comparator<TransportStop>() {
          @Override
          public int compare(TransportStop o1, TransportStop o2) {
            return backwardStops.get(o1) - backwardStops.get(o2);
          }
        });

    return r;
  }
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
      View row = convertView;
      final RouteInfoLocation info = getItem(position);
      if (info == null) {
        TextView text = new TextView(getContext());
        LatLon st = getStartStop(position + 1);
        LatLon end = getEndStop(position - 1);

        if (st != null && end != null) {
          int dist = (int) MapUtils.getDistance(st, end);
          text.setText(
              MessageFormat.format(
                  getString(R.string.transport_searching_route),
                  OsmAndFormatter.getFormattedDistance(dist, SearchTransportActivity.this)));
        } else {
          text.setText(getString(R.string.transport_searching_transport));
        }
        text.setTextSize(21);
        text.setTypeface(null, Typeface.ITALIC);
        text.setOnClickListener(
            new View.OnClickListener() {

              @Override
              public void onClick(View v) {
                if (intermediateListAdapater.getCount() > 1) {
                  intermediateListAdapater.remove(null);
                  searchTransport();
                } else {
                  if (selectedDestinationLocation == null) {
                    selectedDestinationLocation = destinationLocation;
                  } else {
                    selectedDestinationLocation = null;
                  }
                  searchTransport();
                }
              }
            });
        return text;
      }
      int currentRouteLocation = getCurrentRouteLocation();
      if (row == null || row instanceof TextView) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.search_transport_route_item, parent, false);
      }

      TextView label = (TextView) row.findViewById(R.id.label);
      ImageButton icon = (ImageButton) row.findViewById(R.id.remove);

      TransportRoute route = info.getRoute();
      icon.setVisibility(View.VISIBLE);
      StringBuilder labelW = new StringBuilder(150);
      labelW.append(route.getType()).append(" ").append(route.getRef()); // $NON-NLS-1$
      boolean en = settings.usingEnglishNames();
      labelW
          .append(" : ")
          .append(info.getStart().getName(en))
          .append(" - ")
          .append(info.getStop().getName(en)); // $NON-NLS-1$ //$NON-NLS-2$
      // additional information  if route is calculated
      if (currentRouteLocation == -1) {
        labelW.append(" ("); // $NON-NLS-1$
        labelW
            .append(info.getStopNumbers())
            .append(" ")
            .append(getString(R.string.transport_stops))
            .append(", "); // $NON-NLS-1$ //$NON-NLS-2$
        int startDist =
            (int) MapUtils.getDistance(getEndStop(position - 1), info.getStart().getLocation());
        labelW
            .append(getString(R.string.transport_to_go_before))
            .append(" ")
            .append(
                OsmAndFormatter.getFormattedDistance(
                    startDist, SearchTransportActivity.this)); // $NON-NLS-1$
        if (position == getCount() - 1) {
          LatLon stop = getStartStop(position + 1);
          if (stop != null) {
            int endDist = (int) MapUtils.getDistance(stop, info.getStop().getLocation());
            labelW
                .append(", ")
                .append(getString(R.string.transport_to_go_after))
                .append(" ")
                .append(
                    OsmAndFormatter.getFormattedDistance(
                        endDist, SearchTransportActivity.this)); // $NON-NLS-1$ //$NON-NLS-2$
          }
        }

        labelW.append(")"); // $NON-NLS-1$
      }
      label.setText(labelW.toString());
      icon.setOnClickListener(
          new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              int p = position;
              intermediateListAdapater.remove(null);
              if (!isRouteCalculated() && getCurrentRouteLocation() < p) {
                p--;
              }
              intermediateListAdapater.insert(null, p);
              intermediateListAdapater.remove(info);
              intermediateListAdapater.notifyDataSetChanged();
              zoom = initialZoom;
              searchTransport();
            }
          });
      View.OnClickListener clickListener =
          new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              showContextMenuOnRoute(info, position);
            }
          };
      label.setOnClickListener(clickListener);
      return row;
    }
  @Override
  public List<RouteInfoLocation> searchTransportRouteStops(
      double latitude, double longitude, LatLon locationToGo, int zoom) {
    long now = System.currentTimeMillis();
    final LatLon loc = new LatLon(latitude, longitude);
    double tileNumberX = MapUtils.getTileNumberX(zoom, longitude);
    double tileNumberY = MapUtils.getTileNumberY(zoom, latitude);
    double topLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY - 0.5);
    double bottomLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY + 0.5);
    double leftLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX - 0.5);
    double rightLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX + 0.5);
    SearchRequest<TransportStop> req =
        BinaryMapIndexReader.buildSearchTransportRequest(
            MapUtils.get31TileNumberX(leftLongitude),
            MapUtils.get31TileNumberX(rightLongitude),
            MapUtils.get31TileNumberY(topLatitude),
            MapUtils.get31TileNumberY(bottomLatitude),
            -1,
            null);
    List<RouteInfoLocation> listRoutes = new ArrayList<RouteInfoLocation>();
    try {
      List<TransportStop> stops = file.searchTransportIndex(req);

      TIntObjectHashMap<TransportStop> stopsToProcess = new TIntObjectHashMap<TransportStop>();
      for (TransportStop s : stops) {
        for (int ref : s.getReferencesToRoutes()) {
          TransportStop exist = stopsToProcess.get(ref);
          if (exist == null
              || MapUtils.getDistance(loc, s.getLocation())
                  < MapUtils.getDistance(loc, exist.getLocation())) {
            stopsToProcess.put(ref, s);
          }
        }
      }
      TIntObjectHashMap<TransportRoute> transportRoutes =
          file.getTransportRoutes(stopsToProcess.keys());
      for (int ref : stopsToProcess.keys()) {
        TransportRoute route = transportRoutes.get(ref);
        TransportStop s = stopsToProcess.get(ref);
        for (int i = 0; i < 2; i++) {
          boolean direction = i == 0;
          List<TransportStop> stps = direction ? route.getForwardStops() : route.getBackwardStops();
          // load only part

          while (!stps.isEmpty() && (stps.get(0).getId().longValue() != s.getId().longValue())) {
            stps.remove(0);
          }
          if (!stps.isEmpty()) {
            RouteInfoLocation r = new RouteInfoLocation();
            r.setRoute(route);
            r.setStart(stps.get(0));
            r.setDirection(direction);
            if (locationToGo != null) {
              int distToLoc = Integer.MAX_VALUE;
              for (TransportStop st : stps) {
                double ndist = MapUtils.getDistance(locationToGo, st.getLocation());
                if (ndist < distToLoc) {
                  distToLoc = (int) ndist;
                  r.setStop(st);
                  r.setDistToLocation(distToLoc);
                }
              }
            }
            listRoutes.add(r);
          }
        }
      }
      if (log.isDebugEnabled()) {
        log.debug(
            String.format(
                "Search for routes done in %s ms found %s.", //$NON-NLS-1$
                System.currentTimeMillis() - now, listRoutes.size()));
      }

      if (locationToGo != null) {
        Collections.sort(
            listRoutes,
            new Comparator<RouteInfoLocation>() {
              @Override
              public int compare(RouteInfoLocation object1, RouteInfoLocation object2) {
                int x =
                    (int)
                        (MapUtils.getDistance(loc, object1.getStart().getLocation())
                            + object1.getDistToLocation());
                int y =
                    (int)
                        (MapUtils.getDistance(loc, object2.getStart().getLocation())
                            + object2.getDistToLocation());
                return x - y;
              }
            });
      } else {
        Collections.sort(
            listRoutes,
            new Comparator<RouteInfoLocation>() {
              @Override
              public int compare(RouteInfoLocation object1, RouteInfoLocation object2) {
                return Double.compare(
                    MapUtils.getDistance(loc, object1.getStart().getLocation()),
                    MapUtils.getDistance(loc, object2.getStart().getLocation()));
              }
            });
      }
    } catch (IOException e) {
      log.error("Disk error", e); // $NON-NLS-1$
    }
    return listRoutes;
  }