package com.ctpodp;

import static com.ctpodp.TypeConversions.getBaseModelArray;
import static com.ctpodp.TypeConversions.getFloatArray;
import static com.ctpodp.TypeConversions.getIntArray;
import static com.ctpodp.TypeConversions.getStyle;
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.jibestream.jmapandroidsdk.astar.PathPerFloor;
import com.jibestream.jmapandroidsdk.components.BaseModel;
import com.jibestream.jmapandroidsdk.components.Destination;
import com.jibestream.jmapandroidsdk.components.Waypoint;
import com.jibestream.jmapandroidsdk.jcontroller.JController;
import com.jibestream.jmapandroidsdk.jcore.JCore;
import com.jibestream.jmapandroidsdk.jmap.JMap;
import com.jibestream.jmapandroidsdk.jmap.JMapOptions;
import com.jibestream.jmapandroidsdk.main.PointD;
import com.jibestream.jmapandroidsdk.main.Utilities;
import com.jibestream.jmapandroidsdk.oauth.JAuth;
import com.jibestream.jmapandroidsdk.rendering_engine.MapView;
import com.jibestream.jmapandroidsdk.rendering_engine.Stage;
import com.jibestream.jmapandroidsdk.rendering_engine.Transform;
import com.jibestream.jmapandroidsdk.rendering_engine.drawables.JShapeDrawable;
import com.jibestream.jmapandroidsdk.rendering_engine.moving_objects.UserLocation;
import com.jibestream.jmapandroidsdk.styles.JStyle;
import com.jibestream.mapuikit.Compass;
import com.jibestream.mapuikit.CompassOptions;
import com.jibestream.mapuikit.FloorSelector;
import com.jibestream.mapuikit.FloorSelectorOptions;
import com.jibestream.mapuikit.MapUiKit;
import com.jibestream.mapuikit.Search;
import com.jibestream.mapuikit.SearchOptions;
import com.jibestream.navigationkit.NavigationKit;
import com.jibestream.navigationkit.instructionfactory.Instruction;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class MapFragment extends Fragment {

  ThemedReactContext _context;
  Stage stage;
  ProgressBar progressBar;
  JController controller;
  MapUiKit mapUiKit;
  Search search;
  NavigationKit navigationKit;
  PathPerFloor[] currentPath;
  Instruction[] currentInstructions;
  TextView directionsTextView;
  boolean isNavigating = false;

  public MapFragment(ThemedReactContext context) {
    _context = context;
    // Required empty public constructor
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                           Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_map, container, false);
    return view;
  }

  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {
//    _context = this.getContext();
    stage = view.findViewById(R.id.stage);
    progressBar = view.findViewById(R.id.progressBar);
//    initializeMap("Neaera Consulting - Fort Collins");
    Toast.makeText(_context, "View Created", Toast.LENGTH_SHORT).show();
//    new OdpModule(_context);
  }

  public void sendEvent(String topic, WritableMap data) {
    Log.d("EVENT", "Sending Event To " + topic + ", " + data.toString());
    _context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
      .emit(topic, data);
  }

  public void sendEvent(String topic, WritableArray data) {
    Log.d("EVENT", "Sending Event To " + topic + ", " + data.toString());
    _context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
      .emit(topic, data);
  }

  public void initializeMap(ReadableArray args) {
    ReadableMap credentials = args.getMap(0);

    String CLIENT_ID = credentials.getString("clientId");
    String CLIENT_SECRET = credentials.getString("clientSecret");
    int CUSTOMER_ID = credentials.getInt("customerId");
    int VENUE_ID = credentials.getInt("venueId");
    String HOST = credentials.getString("host");

    JAuth jAuth = new JAuth(CLIENT_ID, CLIENT_SECRET);

    JMapOptions mapOptions = new JMapOptions(jAuth, CUSTOMER_ID, VENUE_ID, HOST, stage);

    mapOptions.shouldParseAllMaps(true);
    mapOptions.shouldApplyDisplayMode(true);

    final JMap map = new JMap(_context, mapOptions);

    UserLocation.getInstance().snapToWayfindingPathWithThreshold(50);
//            UserLocation.getInstance().autoRerouteWayfindingPathWithThreshold(getWayfindingRerouteThreshold());
    UserLocation.getInstance().setSpeed(50);

//    Log.d("EVENT", "Sending Data Soon");
//    WritableMap params = Arguments.createMap();
//    params.putDouble("dVal", 1.23);
//    params.putString("sVal", "string");
//    WritableMap obj = Arguments.createMap();
//    obj.putString("key", "value");
//    params.putMap("map", obj);
//    WritableArray pointsArray = Arguments.createArray();
//
//    WritableMap obj2 = Arguments.createMap();
//    obj2.putString("key", "value");
//    pointsArray.pushMap(obj2);
//    params.putArray("arr", pointsArray);
//
//    sendEvent("TESTING", params);

    map.init(
      new JMap.OnMapInitCallback() {
        @Override
        public void onReady() {
          runOnUiThread(new Runnable() {
            @Override
            public void run() {
              progressBar.setVisibility(View.GONE);
            }
          });

          controller = map.getController();
          navigationKit = new NavigationKit(controller);

          final PointF centerOfMap = new PointF(1000, 1000); //controller.getCurrentMap().getWidth()/2, controller.getCurrentMap().getHeight()/2
          UserLocation.getInstance().setPosition(centerOfMap);

          Waypoint userLocationWaypoint = controller.getActiveVenue().getClosestWaypointToCoordinatesOnMap(centerOfMap, controller.getCurrentMap());

          controller.addComponent(UserLocation.getInstance(), controller.getCurrentMap(), new PointF(userLocationWaypoint.getCoordinates()[0].floatValue(), userLocationWaypoint.getCoordinates()[1].floatValue()));

          JCore jCore = map.getCore();
          mapUiKit = new MapUiKit(jCore, controller);
//          renderMapUiComponents(args);
        }

        @Override
        public void onError(String s) {
          runOnUiThread(new Runnable() {
            @Override
            public void run() {
              progressBar.setVisibility(View.GONE);
            }
          });
        }
      }
    );
  }

  public void setUserPosition(double lat, double lng) {
    Log.d("Moving User Before", lat + "," + lng);
    PointD convertPoint = new PointD(lng, lat);
    Utilities.convertEpsg4326ToLocal(convertPoint, controller.getActiveVenue());
    PointF userLocation = new PointF((float) convertPoint.x, (float) convertPoint.y);
    Log.d("Moving User", userLocation.x + "," + userLocation.y);
    controller.updateUserLocationPosition(userLocation, 0, 100, controller.getCurrentMap());

    WritableMap params = Arguments.createMap();
    WritableArray data = Arguments.createArray();
    data.pushDouble(userLocation.x);
    data.pushDouble(userLocation.y);
//    params.putArray("data", data);
    sendEvent("USER_POSITION_UPDATED", data);
  }

  public void centerMapOnUser(double orientation, int animationDuration) {
    Log.d("Command", "Centering On Map, " + orientation + ", " + animationDuration);
    controller.setMapTransform(new Transform(5, UserLocation.getInstance().getPosition(), (float)orientation), animationDuration, new MapView.OnAnimationCallback() {
      @Override
      public void onAnimationComplete() {

      }

      @Override
      public void onAnimationInterrupted() {

      }
    });
    PointD convertPoint = new PointD(UserLocation.getInstance().getPosition().x, UserLocation.getInstance().getPosition().y);
    Utilities.convertLocalToEpsg4326(convertPoint, controller.getActiveVenue());
  }

  private void highlightUnit(Destination destination) {
    JStyle highlightStyle = new JStyle();
    highlightStyle.setColor(Color.parseColor("#CDDC39"));
    highlightStyle.setStrokeColor(Color.parseColor("#202020"));
    highlightStyle.setStrokeWidth(1);

    JShapeDrawable[] units = controller.getUnitsFromDestination(destination);
    controller.styleShapes(units, highlightStyle);
  }

  private void focusToWaypoint(Waypoint userLocationWaypoint) {
    Double[] waypointCoords = userLocationWaypoint.getCoordinates();
    controller.setMapTransform(new Transform(2, new PointF(waypointCoords[0].floatValue(), waypointCoords[1].floatValue()), 0), 1500, new MapView.OnAnimationCallback() {
      @Override
      public void onAnimationComplete() {

      }

      @Override
      public void onAnimationInterrupted() {

      }
    });
  }

  public void wayfindToWaypoint(int waypointId) {

  }

  public PathPerFloor[] getPathToDestination(int destinationId) {
    Waypoint userLocationWaypoint = controller.getActiveVenue().getClosestWaypointToCoordinatesOnMap(UserLocation.getInstance().getPosition(), controller.getCurrentMap());
    return getPathBetweenWaypoints(userLocationWaypoint.id, destinationId);
  }

  private PathPerFloor[] getPathBetweenWaypoints(int sourceId, int destinationId) {
    Waypoint sourceWaypoint = Arrays.stream(controller.getActiveVenue().getMaps().getAllWaypoints()).filter(wp -> wp.id == sourceId).findFirst().get();
    Waypoint destinationWaypoint = Arrays.stream(controller.getActiveVenue().getMaps().getAllWaypoints()).filter(wp -> wp.id == destinationId).findFirst().get();
    PathPerFloor[] pathPerFloors = controller.wayfindBetweenWaypoints(sourceWaypoint, destinationWaypoint);
    return pathPerFloors;

//                    currentInstructions = processInstructions(navigationKit.createInstructionsFromPaths(pathPerFloors));
//                    onLocationChange(UserLocation.getInstance().getPosition());
  }

  private void renderMapUiComponents(ReadableArray args) {
    renderCompass(null, null, null);
    renderFloorSelector(null, null, null, null, null, null);
    renderSearchBar(null, "placeholder", null, 5);
//    if (args.size() > 1) {
//      ReadableMap arg = args.getMap(1);
//      // Float[] position, Float[] padding, Float width, Float height
//      Float[] position = new Float[2];
//      arg.getArray("position").toArrayList().toArray(position);
//      renderCompass(position, (float) arg.getDouble("width"), (float) arg.getDouble("height"));
//    }
//    if (args.size() > 2) {
//      ReadableMap arg = args.getMap(2);
//      // JStyle inactiveStyle, JStyle inactiveFont, JStyle activeStyle, JStyle activeFont, int[] position, Boolean vertical
//      renderFloorSelector(getStyle(arg.getMap("inactiveStyle")), getStyle(arg.getMap("inactiveFont")), getStyle(arg.getMap("activeStyle")), getStyle(arg.getMap("activeFont")), getIntArray(arg.getArray("position")), arg.getBoolean("vertical"));
//    }
//    if (args.size() > 3) {
//      ReadableMap arg = args.getMap(3);
//      // Float position, String placeholder, BaseModel[] searchArray, Integer maxResults
//      renderSearchBar((float) arg.getDouble("position"), arg.getString("placeholder"), getBaseModelArray(arg.getArray("searchArray")), arg.getInt("maxResults"));
//    }
  }

  public void renderFloorSelector(JStyle inactiveStyle, JStyle inactiveFont, JStyle activeStyle, JStyle activeFont, int[] position, Boolean vertical) {
    FloorSelectorOptions floorSelectorOptions = new FloorSelectorOptions();

    if (inactiveStyle != null) {
      floorSelectorOptions.setInactiveStyle(inactiveStyle);
    }
    if (inactiveFont != null) {
      floorSelectorOptions.setInactiveFontStyle(inactiveFont);
    }
    if (activeStyle != null) {
      floorSelectorOptions.setActiveStyle(activeStyle);
    }
    if (activeFont != null) {
      floorSelectorOptions.setActiveFontStyle(activeFont);
    }
    if (position != null) {
      floorSelectorOptions.setPosition(position); // new int[]{RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.ALIGN_PARENT_BOTTOM}, 9, 12
    }
    if (vertical != null) {
      floorSelectorOptions.setVertical(vertical);
    }

    FloorSelector floorSelector = mapUiKit.renderFloorSelector(floorSelectorOptions);
  }

  public void renderCompass(Float[] position, Float width, Float height) {
    CompassOptions compassOptions = new CompassOptions();
    compassOptions.setResetRotationOnTap(true);
    if (width != null) {
      compassOptions.setWidth(width);
    }
    if (width != null) {
      compassOptions.setHeight(height);
    }
    if (position != null) {
      compassOptions.setPosition(position);
    }

    Compass compass = mapUiKit.renderCompass(compassOptions);

    if(compass != null) {
      RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
      params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
      compass.setLayoutParams(params);
    }
  }

  public void renderSearchBar(Float position, String placeholder, BaseModel[] searchArray, Integer maxResults) {
    final SearchOptions searchOptions;
    if (searchArray != null) {
      searchOptions = new SearchOptions(searchArray);
    }
    else {
      searchOptions = new SearchOptions(controller.getActiveVenue().getDestinations().getAll());
    }

    if (position != null) {
      searchOptions.setPosition(position);
    }
    if (placeholder != null) {
      searchOptions.setHint(placeholder);
    }
    if (maxResults != null) {
      searchOptions.setMaxResults(maxResults);
    }

    Search.OnItemSelectListener onItemSelectListener = new Search.OnItemSelectListener() {
      @Override
      public void onItemClick(BaseModel o) {
        if (o instanceof Destination) {
          Destination destination = (Destination) o;
          if (search != null) {
            search.setQuery(destination.getName(), false);
          }

          WritableMap destinationMap = Arguments.createMap();
          destinationMap.putInt("id", destination.id);
          destinationMap.putString("name", destination.getName());
          destinationMap.putString("description", destination.getDescription());

          WritableArray waypoints = Arguments.createArray();
          for (Waypoint wp : destination.getWaypoints()) {
            WritableMap wpm = Arguments.createMap();
            wpm.putInt("id", wp.id);

            WritableMap waypointMap = Arguments.createMap();
            waypointMap.putMap("_", wpm);
            waypoints.pushMap(waypointMap);
          }
          destinationMap.putArray("waypoints", waypoints);
//          try {
//            Gson gson = new GsonBuilder().create();
//            params = Utilities.jsonToReact(new JSONObject(Gson));
//          } catch (IllegalAccessException e) {
//            throw new RuntimeException(e);
//          } catch (java.lang.InstantiationException e) {
//            throw new RuntimeException(e);
//          } catch (JSONException e) {
//            throw new RuntimeException(e);
//          }

          sendEvent("DESTINATION_SELECTED", destinationMap);


////
//          controller.clearWayfindingPath();
//          controller.drawWayfindingPath(pathPerFloors);
//          isNavigating = true;
////
//          highlightUnit(destination);
//          focusToWaypoint(userLocationWaypoint);
        }
      }
    };

    search = mapUiKit.renderSearch(searchOptions, onItemSelectListener);
    if (search != null) {
      ImageView closeButton = search.findViewById(com.jibestream.mapuikit.R.id.search_close_btn);
      closeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          EditText editText = search.findViewById(com.jibestream.mapuikit.R.id.search_src_text);
          editText.setText("");
          search.setQuery("", false);
          search.onActionViewCollapsed();
          search.setIconified(false);
          search.clearFocus();

          directionsTextView.setVisibility(View.INVISIBLE);

          if (controller == null) {return;}

//                            controller.resetAllShapeStyles();
          controller.clearWayfindingPath();
          isNavigating = true;

          currentPath = null;
          currentInstructions = null;
        }
      });
    }
  }

  @Override
  public void onResume() {
    super.onResume();
    stage.getMapView().resumeMap();
  }

  @Override
  public void onPause() {
    super.onPause();
    try {
      stage.getMapView().pauseMap();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
