/*
 * Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

package com.google.android.gnd.persistence.geojson;

import static com.google.android.gnd.util.ImmutableListCollector.toImmutableList;
import static java8.util.J8Arrays.stream;

import android.util.Log;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gnd.model.basemap.tile.Tile;
import com.google.android.gnd.model.basemap.tile.Tile.State;
import com.google.android.gnd.persistence.uuid.OfflineUuidGenerator;
import com.google.common.collect.ImmutableList;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.inject.Inject;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class GeoJsonParser {

  private static final String TAG = GeoJsonParser.class.getSimpleName();
  private final OfflineUuidGenerator uuidGenerator;

  @Inject
  GeoJsonParser(OfflineUuidGenerator uuidGenerator) {
    this.uuidGenerator = uuidGenerator;
  }

  private String readJsonFile(File file) throws IOException {
    try {
      InputStream is = new FileInputStream(file);
      BufferedReader buf = new BufferedReader(new InputStreamReader(is));
      StringBuilder sb = new StringBuilder();

      int res = buf.read();
      while (res != -1) {
        sb.append(res);
        res = buf.read();
      }
      return sb.toString();
    } catch (IOException e) {
      Log.e(TAG, "Unable to load JSON", e);
      throw e;
    }
  }

  /**
   * Returns the immutable list of tiles specified in {@param geojson} that intersect {@param
   * bounds}.
   */
  public ImmutableList<Tile> intersectingTiles(LatLngBounds bounds, File file) {
    try {
      String fileContents = readJsonFile(file);
      JSONObject geoJson = new JSONObject(fileContents);
      JSONArray features = geoJson.getJSONArray("features");

      return stream(toArray(features))
          .map(GeoJsonTile::new)
          .filter(tile -> tile.boundsIntersect(bounds))
          .map(this::jsonToTile)
          .collect(toImmutableList());

    } catch (JSONException | IOException e) {
      Log.e(TAG, "Unable to parse JSON", e);
    }

    return ImmutableList.of();
  }

  /**
   * Converts a JSONArray to an array of JSONObjects. Provided for compatibility with java8 streams.
   * JSONArray itself only inherits from Object, and is not convertible to a stream.
   */
  private static JSONObject[] toArray(JSONArray arr) {
    JSONObject[] result = new JSONObject[arr.length()];

    for (int i = 0; i < arr.length(); i++) {
      try {
        result[i] = arr.getJSONObject(i);
      } catch (JSONException e) {
        Log.e(TAG, "couldn't parse json", e);
      }
    }

    return result;
  }

  /** Returns the {@link Tile} specified by {@param json}. */
  private Tile jsonToTile(GeoJsonTile json) {
    // TODO: Instead of returning tiles with invalid state (empty URL/ID values)
    // Throw an exception here and handle it downstream.
    return Tile.newBuilder()
        .setId(uuidGenerator.generateUuid())
        .setUrl(json.getUrl().orElse(""))
        .setState(State.PENDING)
        .setPath(Tile.pathFromId(json.getId().orElse("")))
        .build();
  }
}

@@ -753,16 +753,28 @@
 til.
-J8Arrays
+stream.StreamSupport
 .str
@@ -1258,32 +1258,92 @@
 utStreamReader;%0A
+import java.lang.reflect.Array;%0Aimport java.util.ArrayList;%0A
 import javax.inj
@@ -1768,28 +1768,16 @@
 ption %7B%0A
-    try %7B%0A  
     Inpu
@@ -1812,26 +1812,24 @@
 ream(file);%0A
-  
     Buffered
@@ -1888,18 +1888,16 @@
 r(is));%0A
-  
     Stri
@@ -1937,18 +1937,16 @@
 );%0A%0A    
-  
 int res 
@@ -1955,26 +1955,24 @@
 buf.read();%0A
-  
     while (r
@@ -1989,18 +1989,16 @@
 %7B%0A      
-  
 sb.appen
@@ -2001,26 +2001,24 @@
 ppend(res);%0A
-  
       res = 
@@ -2033,22 +2033,18 @@
 ();%0A    
-  %7D%0A  
+%7D%0A
     retu
@@ -2065,103 +2065,8 @@
 ();%0A
-    %7D catch (IOException e) %7B%0A      Log.e(TAG, %22Unable to load JSON%22, e);%0A      throw e;%0A    %7D%0A
   %7D%0A
@@ -2471,16 +2471,20 @@
 (toArray
+List
 (feature
@@ -2998,16 +2998,26 @@
  static 
+ArrayList%3C
 JSONObje
@@ -3022,18 +3022,17 @@
 ject
-%5B%5D
+%3E
  toArray
 (JSO
@@ -3027,16 +3027,20 @@
  toArray
+List
 (JSONArr
@@ -3049,24 +3049,34 @@
  arr) %7B%0A    
+ArrayList%3C
 JSONObject%5B%5D
@@ -3077,10 +3077,9 @@
 ject
-%5B%5D
+%3E
  res
@@ -3092,32 +3092,21 @@
 new 
-JSONObject%5Barr.length
+ArrayList%3C%3E
 ()
-%5D
 ;%0A%0A 
@@ -3179,14 +3179,13 @@
 sult
-%5Bi%5D = 
+.add(
 arr.
@@ -3200,16 +3200,17 @@
 bject(i)
+)
 ;%0A      
@@ -3261,27 +3261,36 @@
 G, %22
-couldn't parse json
+Ignoring error in JSON array
 %22, e

Use ArrayList instead of Array to avoid nulls