/** 
 ** Copyright (c) 2010 Ushahidi Inc
 ** All rights reserved
 ** Contact: team@ushahidi.com
 ** Website: http://www.ushahidi.com
 ** 
 ** GNU Lesser General Public License Usage
 ** This file may be used under the terms of the GNU Lesser
 ** General Public License version 3 as published by the Free Software
 ** Foundation and appearing in the file LICENSE.LGPL included in the
 ** packaging of this file. Please review the following information to
 ** ensure the GNU Lesser General Public License version 3 requirements
 ** will be met: http://www.gnu.org/licenses/lgpl.html.	
 **	
 **
 ** If you have questions regarding the use of this file, please contact
 ** Ushahidi developers at team@ushahidi.com.
 ** 
 **/

package com.ushahidi.android.app;

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

import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;
import com.ushahidi.android.app.data.Database;
import com.ushahidi.android.app.data.IncidentsData;
import com.ushahidi.android.app.util.Util;

public class MapLocation extends MapActivity {
    private MapView mapView = null;

    private MapController mapController;

    private static Geocoder gc;

    private GeoPoint defaultLocation;

    private double latitude;

    private double longitude;

    private List<IncidentsData> mNewIncidents;

    private List<IncidentsData> mOldIncidents;

    private Button btnReset;

    private Button btnSave;

    private Button btnFind;

    private Bundle bundle = new Bundle();

    private List<Address> foundAddresses;

    private String locationName;

    private String title;

    private String date;

    private String description;

    private String location;

    private String categories;

    private String thumbnail;

    // Need handler for callbacks to the UI thread
    final Handler mHandler = new Handler();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.view_map);

        mapView = (MapView)findViewById(R.id.location_map);
        locationName = "";

        foundAddresses = new ArrayList<Address>();
        gc = new Geocoder(this);

        btnSave = (Button)findViewById(R.id.btn_save);

        btnSave.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                bundle.putDouble("latitude", latitude);
                bundle.putDouble("longitude", longitude);
                bundle.putString("location", locationName);

                // Pass the data to the calling activity
                Intent intent = new Intent();
                intent.putExtra("locations", bundle);
                setResult(RESULT_OK, intent);
                finish();
            }
        });

        btnFind = (Button)findViewById(R.id.btn_find);
        btnFind.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Util.showToast(MapLocation.this, R.string.find_location);
                updateLocation();
            }
        });

        mapController = mapView.getController();

        mOldIncidents = new ArrayList<IncidentsData>();
        mNewIncidents = showIncidents("All");

        if (mNewIncidents.size() > 0) {
            latitude = Double.parseDouble(mNewIncidents.get(0).getIncidentLocLatitude());
            longitude = Double.parseDouble(mNewIncidents.get(0).getIncidentLocLongitude());
        }

        defaultLocation = getPoint(latitude, longitude);
        centerLocation(defaultLocation);

        btnReset = (Button)findViewById(R.id.btn_reset);
        btnReset.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                centerLocation(defaultLocation);
            }
        });
    }

    // Create runnable for posting
    final Runnable mUpdateResults = new Runnable() {
        public void run() {
            updateResultsInUi();
        }
    };

    protected void startLongRunningOperation() {

        // Fire off a thread to do some work that we shouldn't do directly in
        // the UI thread
        Thread t = new Thread() {
            @Override
            public void run() {
                updateLocation();
                mHandler.post(mUpdateResults);
            }
        };
        t.start();
    }

    private void updateResultsInUi() {
        Toast.makeText(MapLocation.this, "Found you at " + locationName, Toast.LENGTH_SHORT).show();
    }

    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }

    private void placeMarker(int markerLatitude, int markerLongitude) {

        Drawable marker = getResources().getDrawable(R.drawable.map_marker_red);

        marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());
        mapView.getController().setZoom(14);

        mapView.setBuiltInZoomControls(true);
        mapView.getOverlays().add(new MapMarker(marker, markerLatitude, markerLongitude));
    }

    public GeoPoint getPoint(double lat, double lon) {
        return (new GeoPoint((int)(lat * 1000000.0), (int)(lon * 1000000.0)));
    }

    private void centerLocation(GeoPoint centerGeoPoint) {

        mapController.animateTo(centerGeoPoint);

        // initilaize latitude and longitude for them to be passed to the
        // AddIncident Activity.
        this.latitude = centerGeoPoint.getLatitudeE6() / 1.0E6;
        this.longitude = centerGeoPoint.getLongitudeE6() / 1.0E6;
        mapView.getOverlays().clear();
        placeMarker(centerGeoPoint.getLatitudeE6(), centerGeoPoint.getLongitudeE6());

    }

    /**
     * get the real location name from the latitude and longitude.
     */
    private String getLocationFromLatLon(double lat, double lon) {

        try {
            Address address;
            foundAddresses = gc.getFromLocation(lat, lon, 5);
            if (foundAddresses.size() > 0) {
                address = foundAddresses.get(0);
                return address.getFeatureName();

            } else {
                return "";
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    // get incidents from the db
    public List<IncidentsData> showIncidents(String by) {
        Cursor cursor;

        if (by.equals("All"))
            cursor = MainApplication.mDb.fetchAllIncidents();
        else
            cursor = MainApplication.mDb.fetchIncidentsByCategories(by);

        if (cursor.moveToFirst()) {
            int idIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_ID);
            int titleIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_TITLE);
            int dateIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_DATE);
            int verifiedIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_VERIFIED);
            int locationIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_LOC_NAME);

            int descIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_DESC);

            int categoryIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_CATEGORIES);

            int mediaIndex = cursor.getColumnIndexOrThrow(Database.INCIDENT_MEDIA);

            int latitudeIndex = cursor
                    .getColumnIndexOrThrow(Database.INCIDENT_LOC_LATITUDE);

            int longitudeIndex = cursor
                    .getColumnIndexOrThrow(Database.INCIDENT_LOC_LONGITUDE);

            do {

                IncidentsData incidentData = new IncidentsData();
                mOldIncidents.add(incidentData);

                int id = Util.toInt(cursor.getString(idIndex));
                incidentData.setIncidentId(id);

                title = Util.capitalizeString(cursor.getString(titleIndex));
                incidentData.setIncidentTitle(title);

                description = cursor.getString(descIndex);
                incidentData.setIncidentDesc(description);

                categories = cursor.getString(categoryIndex);
                incidentData.setIncidentCategories(categories);

                location = cursor.getString(locationIndex);
                incidentData.setIncidentLocLongitude(location);

                date = Util.joinString("Date: ", Util.formatDate("yyyy-MM-dd hh:mm:ss",
                        cursor.getString(dateIndex), "MMMM dd, yyyy 'at' hh:mm:ss aaa"));
                incidentData.setIncidentDate(date);

                thumbnail = cursor.getString(mediaIndex);
                incidentData.setIncidentThumbnail(thumbnail);

                incidentData.setIncidentVerified(Util.toInt(cursor.getString(verifiedIndex)));

                incidentData.setIncidentLocLatitude(cursor.getString(latitudeIndex));
                incidentData.setIncidentLocLongitude(cursor.getString(longitudeIndex));

            } while (cursor.moveToNext());
        }

        cursor.close();
        return mOldIncidents;

    }

    // update the device current location
    private void updateLocation() {
        MyLocationListener listener = new MyLocationListener();
        LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        long updateTimeMsec = 1000L;

        LocationProvider low = locationManager.getProvider(locationManager.getBestProvider(
                Util.createCoarseCriteria(), true));

        // get high accuracy provider
        LocationProvider high = locationManager.getProvider(locationManager.getBestProvider(
                Util.createFineCriteria(), true));

        // Register for GPS location if enabled or if neither is enabled
        locationManager.requestLocationUpdates(low.getName(), updateTimeMsec, 500.0f, listener);

        locationManager.requestLocationUpdates(high.getName(), updateTimeMsec, 500.0f, listener);

    }

    // get the current location of the user
    public class MyLocationListener implements LocationListener {
        public void onLocationChanged(Location location) {
            double latitude = 0;
            double longitude = 0;
            String locName = "";
            if (location != null) {
                // Dipo Fix
                // Stop asking for updates when location has been retrieved

                latitude = location.getLatitude();
                longitude = location.getLongitude();

                locName = getLocationFromLatLon(latitude, longitude);
                centerLocation(getPoint(latitude, longitude));
                ((LocationManager)getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
                if (locName == null) {

                    Util.showToast(MapLocation.this, R.string.location_not_found);
                } else {

                    locationName = locName;
                    Toast.makeText(MapLocation.this, "Found you at " + locationName,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }

        public void onProviderDisabled(String provider) {
            Util.showToast(MapLocation.this, R.string.location_not_found);
        }

        public void onProviderEnabled(String provider) {

        }

        public void onStatusChanged(String provider, int status, Bundle extras) {

        }
    }

   

    private class MapMarker extends ItemizedOverlay<OverlayItem> {

        private List<OverlayItem> locations = new ArrayList<OverlayItem>();

        private Drawable marker;

        private OverlayItem myOverlayItem;

        private boolean MoveMap = false;

        private long timer;

        public MapMarker(Drawable defaultMarker, int LatitudeE6, int LongitudeE6) {
            super(defaultMarker);
            this.timer = 0;
            this.marker = defaultMarker;

            // create locations of interest
            GeoPoint myPlace = new GeoPoint(LatitudeE6, LongitudeE6);

            myOverlayItem = new OverlayItem(myPlace, " ", " ");

            locations.add(myOverlayItem);

            populate();

        }

        @Override
        protected OverlayItem createItem(int i) {
            return locations.get(i);
        }

        @Override
        public int size() {
            return locations.size();
        }

        @Override
        public void draw(Canvas canvas, MapView mapView, boolean shadow) {
            super.draw(canvas, mapView, shadow);
            boundCenterBottom(marker);
        }

        /**
         * Fixed by Joey at http://goo.gl/UUiN
         */
        @Override
        public boolean onTouchEvent(MotionEvent motionEvent, MapView mapview) {

            int Action = motionEvent.getAction();
            String foundLoc = "";

            if (Action == MotionEvent.ACTION_UP) {

                if (!MoveMap && (System.currentTimeMillis() - timer <= 1000)) {
                    Projection proj = mapView.getProjection();
                    GeoPoint loc = proj
                            .fromPixels((int)motionEvent.getX(), (int)motionEvent.getY());
                    
                    
                    foundLoc = getLocationFromLatLon(loc.getLatitudeE6() / 1.0E6,
                            loc.getLatitudeE6() / 1.0E6);
                    if (foundLoc == "") {
                        locationName = "";
                        Util.showToast(MapLocation.this, R.string.loc_not_found);
                    } else {
                        locationName = foundLoc;
                        Toast.makeText(MapLocation.this, locationName, Toast.LENGTH_SHORT).show();
                    }
                    // remove the last marker
                    
                    centerLocation(loc);
                }

            } else if (Action == MotionEvent.ACTION_DOWN) {
                timer = System.currentTimeMillis();
                MoveMap = false;
            } else if (Action == MotionEvent.ACTION_MOVE) {
                float difX = 0.0f;
                float difY = 0.0f;

                if (motionEvent.getHistorySize() >= 2) {

                    difX = motionEvent.getHistoricalX(0)
                            - motionEvent.getHistoricalX(motionEvent.getHistorySize() - 1);

                    difY = motionEvent.getHistoricalY(0)
                            - motionEvent.getHistoricalY(motionEvent.getHistorySize() - 1);
                }
                if (difX >= 5 || difX <= -5 || difY >= 5 || difY <= -5) {

                    MoveMap = true;

                }

            }

            return super.onTouchEvent(motionEvent, mapview);
        }
    }

}
