package com.m2sdl.undercooked;

/*
 * Copyright 2010, 2011, 2012, 2013 mapsforge.org
 * Copyright 2013-2014 Ludwig M Brinckmann
 * Copyright 2014-2019 devemux86
 * Copyright 2017 usrusr
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.google.android.material.chip.Chip;
import com.m2sdl.undercooked.map.RestaurantLayerGroup;
import com.m2sdl.undercooked.map.SharedPreferencesWrapper;

import org.mapsforge.core.model.BoundingBox;
import org.mapsforge.core.model.LatLong;
import org.mapsforge.core.model.MapPosition;
import org.mapsforge.map.android.graphics.AndroidBitmap;
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
import org.mapsforge.map.android.util.AndroidUtil;
import org.mapsforge.map.android.view.MapView;
import org.mapsforge.map.datastore.MapDataStore;
import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.renderer.TileRendererLayer;
import org.mapsforge.map.model.MapViewPosition;
import org.mapsforge.map.reader.MapFile;
import org.mapsforge.map.rendertheme.XmlRenderTheme;
import org.mapsforge.map.rendertheme.XmlRenderThemeStyleMenu;
import org.mapsforge.map.rendertheme.internal.MapsforgeThemes;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * A abstract template map viewer activity that provides a standard life cycle and
 * modification points for mapsforge-based map activities.
 */
public class MapActivity extends AppCompatActivity {

    protected static final String LATITUDE = "latitude";
    protected static final String LONGITUDE = "longitude";
    protected static final String ZOOM_LEVEL = "zoomLevel";
    protected static final String TOULOUSE_MAP = "midi-pyrenees.map";
    protected static final BoundingBox Roughly_Capitole = new BoundingBox(43.603, 1.442275, 43.6038, 1.442779);

    protected MapView mapView;
    protected SharedPreferencesWrapper preferences;
    protected XmlRenderThemeStyleMenu renderThemeStyleMenu;
    protected List<TileCache> tileCaches = new ArrayList<>();

    /*
     * Abstract methods that must be implemented.
     */

    /**
     * Gets the name of the map file.
     * The directory for the file is supplied by getMapFileDirectory()
     *
     * @return the map file name to be used
     */
    protected String getMapFileName() {
        return TOULOUSE_MAP;
    }

    /**
     * This MapViewer uses the built-in default theme.
     *
     * @return the render theme to use
     */
    protected XmlRenderTheme getRenderTheme() {
        return MapsforgeThemes.OSMARENDER;
    }


    /**
     * The MaxTextWidthFactor determines how long a text may be before it is line broken. The
     * default setting should be good enough for most apps.
     *
     * @return the maximum text width factor for line breaking captions
     */
    protected float getMaxTextWidthFactor() {
        return 0.7f;
    }

    /**
     * @return the default starting zoom level if nothing is encoded in the map file.
     */
    protected byte getZoomLevelDefault() {
        return (byte) 15;
    }

    /**
     * @return the minimum zoom level of the map view.
     */
    protected byte getZoomLevelMin() {
        return (byte) 13;
    }

    /**
     * @return the maximum zoom level of the map view.
     */
    protected byte getZoomLevelMax() {
        return (byte) 19;
    }


    /**
     * Provides the directory of the map file, by default the Android external storage
     * directory: /Android/media/org.mapsforge.samples.android/
     */
    protected File getMapFileDirectory() {
        return getExternalMediaDirs()[0];
    }

    /**
     * Combines map file directory and map file to a map file.
     * This method usually will not need to be changed.
     *
     * @return a map file interface
     */
    protected MapDataStore getMapFile() {
        return new MapFile(new File(getMapFileDirectory(), getMapFileName()));
    }

    /**
     * The persistable ID is used to store settings information, like the center of the last view
     * and the zoomlevel. By default the simple name of the class is used. The value is not user
     * visibile.
     *
     * @return the id that is used to save this mapview.
     */
    protected String getPersistableId() {
        return getClass().getSimpleName();
    }

    /**
     * Returns the relative size of a map view in relation to the screen size of the device. This
     * is used for cache size calculations.
     * By default this returns 1.0, for a full size map view.
     *
     * @return the screen ratio of the mapview
     */
    protected float getScreenRatio() {
        return 1.0f;
    }

    /**
     * Configuration method to set if a map view activity will have zoom controls.
     *
     * @return true if the map has standard zoom controls.
     */
    protected boolean hasZoomControls() {
        return true;
    }

    /**
     * Configuration method to set if map view activity's zoom controls hide automatically.
     *
     * @return true if zoom controls hide automatically.
     */
    protected boolean isZoomControlsAutoHide() {
        return true;
    }

    /**
     * initializes the map view position.
     *
     * @param mvp the map view position to be set
     */
    protected void initializePosition(MapViewPosition mvp) {

        mvp.setMapPosition(restoreLastPositionFromDiskOrDefault());
        // TODO make sure to go back to Capitole if we quit while out of bounds
        mvp.setZoomLevelMax(getZoomLevelMax());
        mvp.setZoomLevelMin(getZoomLevelMin());
    }


    /**
     * Creates the shared preferences that are being used to store map view data over
     * activity restarts.
     */
    protected void createSharedPreferences() {
        preferences = new SharedPreferencesWrapper(getSharedPreferences(getPersistableId(), MODE_PRIVATE));
    }

    /**
     * Template method to create the map views.
     */
    protected void createMapViews() {
        mapView = getMapView();
        initializePosition(mapView.getModel().mapViewPosition);
    }

    /**
     * Creates a simple tile renderer layer with the AndroidUtil helper.
     */
    protected void createLayers() {
        TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCaches.get(0), getMapFile(), mapView.getModel().mapViewPosition, false, true, false, AndroidGraphicFactory.INSTANCE);
        tileRendererLayer.setXmlRenderTheme(getRenderTheme());
        mapView.addLayer(tileRendererLayer);

        RestaurantLayerGroup restaurantLayerGroup = new RestaurantLayerGroup(this, tileCaches.get(0), getMapFile(), mapView.getModel().mapViewPosition, new AndroidBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.restaurant_marker_2)), mapView);
        mapView.addLayer(restaurantLayerGroup);
    }

    /**
     * Hook to check for Android Runtime Permissions.
     */
    private void checkPermission() {
        // TODO check permissions
    }

    protected void createTileCaches() {
        tileCaches.add(AndroidUtil.createTileCache(this, getPersistableId(), mapView.getModel().displayModel.getTileSize(), getScreenRatio(), mapView.getModel().frameBufferModel.getOverdrawFactor()));
    }

    protected void createControls() {
        mapView.getMapScaleBar().setVisible(true);
        mapView.setBuiltInZoomControls(hasZoomControls());
        mapView.getMapZoomControls().setZoomLevelMin(getZoomLevelMin());
        mapView.getMapZoomControls().setZoomLevelMax(getZoomLevelMax());
        mapView.getMapZoomControls().setZoomControlsGravity(Gravity.BOTTOM | Gravity.END);
//        mapView.getMapZoomControls().setMarginHorizontal(50);
        mapView.getMapZoomControls().setMarginVertical(200);
    }


    /**
     * Android Activity life cycle method.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        createSharedPreferences();
        createMapViews();
        createTileCaches();
        checkPermission();
        createControls();
        createLayers();

        // Android Setup
        setTitle(getClass().getSimpleName());
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.map), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
    }


    /**
     * Android Activity life cycle method.
     */
    @Override
    protected void onPause() {
        dumpLastPositionToDiskBeforeExit();
        super.onPause();
    }

    private void dumpLastPositionToDiskBeforeExit() {
        final MapViewPosition mapViewPosition = mapView.getModel().mapViewPosition;
        preferences.putDouble(LATITUDE, mapViewPosition.getCenter().latitude);
        preferences.putDouble(LONGITUDE, mapViewPosition.getCenter().longitude);
        preferences.putByte(ZOOM_LEVEL, mapViewPosition.getZoomLevel());
        preferences.save();
    }

    private MapPosition restoreLastPositionFromDiskOrDefault() {
        var defaultPosition = Roughly_Capitole.getCenterPoint();
        var lastSavedPositionOrDefault = new LatLong(preferences.getDouble(LATITUDE, defaultPosition.latitude), preferences.getDouble(LONGITUDE, defaultPosition.longitude));
        var zoom = preferences.getByte(ZOOM_LEVEL, getZoomLevelDefault());
        return new MapPosition(lastSavedPositionOrDefault, zoom);
    }

    /**
     * Android Activity life cycle method.
     */
    @Override
    protected void onDestroy() {
        mapView.destroyAll();
        AndroidGraphicFactory.clearResourceMemoryCache();
        tileCaches.clear();
        super.onDestroy();
    }

    /**
     * Hook to purge tile caches.
     * By default we purge every tile cache that has been added to the tileCaches list.
     */
    protected void purgeTileCaches() {
        for (TileCache tileCache : tileCaches) {
            tileCache.purge();
        }
        tileCaches.clear();
    }

    protected void redrawLayers() {
        mapView.getLayerManager().redrawLayers();
    }

    /**
     * Creates a map view using an XML layout file supplied by getLayoutId() and finds
     * the map view component inside it with getMapViewId().
     *
     * @return the Android MapView for this activity.
     */
    protected MapView getMapView() {
        setContentView(R.layout.activity_map);
        return findViewById(R.id.mapView);
    }

    public void displayRestaurant(View view) {
        Chip c = (Chip) view;
        mapView.getLayerManager().getLayers().get(1).setVisible(c.isChecked());
    }
}
