// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.googlemaps;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.maps.model.Tile;
import com.google.android.gms.maps.model.TileProvider;
import io.flutter.plugin.common.MethodChannel;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

class TileProviderController implements TileProvider {

  private static final String TAG = "TileProviderController";

  protected final String tileOverlayId;
  protected final MethodChannel methodChannel;
  protected final Handler handler = new Handler(Looper.getMainLooper());

  TileProviderController(MethodChannel methodChannel, String tileOverlayId) {
    this.tileOverlayId = tileOverlayId;
    this.methodChannel = methodChannel;
  }

  @Override
  public Tile getTile(final int x, final int y, final int zoom) {
    Worker worker = new Worker(x, y, zoom);
    return worker.getTile();
  }

  private final class Worker implements MethodChannel.Result {

    private final CountDownLatch countDownLatch = new CountDownLatch(1);
    private final int x;
    private final int y;
    private final int zoom;
    private Map<String, ?> result;

    Worker(int x, int y, int zoom) {
      this.x = x;
      this.y = y;
      this.zoom = zoom;
    }

    @NonNull
    Tile getTile() {
      handler.post(
          () ->
              methodChannel.invokeMethod(
                  "tileOverlay#getTile",
                  Convert.tileOverlayArgumentsToJson(tileOverlayId, x, y, zoom),
                  this));
      try {
        // Because `methodChannel.invokeMethod` is async, we use a `countDownLatch` make it synchronized.
        countDownLatch.await();
      } catch (InterruptedException e) {
        Log.e(
            TAG,
            String.format("countDownLatch: can't get tile: x = %d, y= %d, zoom = %d", x, y, zoom),
            e);
        return TileProvider.NO_TILE;
      }
      try {
        return Convert.interpretTile(result);
      } catch (Exception e) {
        Log.e(TAG, "Can't parse tile data", e);
        return TileProvider.NO_TILE;
      }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void success(Object data) {
      result = (Map<String, ?>) data;
      countDownLatch.countDown();
    }

    @Override
    public void error(String errorCode, String errorMessage, Object data) {
      Log.e(
          TAG,
          "Can't get tile: errorCode = "
              + errorCode
              + ", errorMessage = "
              + errorCode
              + ", date = "
              + data);
      result = null;
      countDownLatch.countDown();
    }

    @Override
    public void notImplemented() {
      Log.e(TAG, "Can't get tile: notImplemented");
      result = null;
      countDownLatch.countDown();
    }
  }
}
