package com.nctu.bikeline.activity;

import android.app.ActivityManager;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.location.Address;
import android.location.Geocoder;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.nctu.bikeline.R;
import com.nctu.bikeline.gps.GPSService;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ChooseLocationActivity extends AppCompatActivity implements OnMapReadyCallback,
        SearchView.OnQueryTextListener, GoogleMap.OnMapLongClickListener, SearchView.OnSuggestionListener {
    public static final String TITLE_EXTRA = "com.nctu.bikeline.activity.ChooseLocationActivity.TITLE_EXTRA";
    public static final String ADDRESS_EXTRA = "com.nctu.bikeline.activity.ChooseLocationActivity.ADDRESS_EXTRA";
    public static final String LONGITUDE_EXTRA = "com.nctu.bikeline.activity.ChooseLocationActivity.LONGITUDE";
    public static final String LATITUDE_EXTRA = "com.nctu.bikeline.activity.ChooseLocationActivity.LATITUDE";
    public static final String PREFERENCE_SEARCH_HISTORY = "com.nctu.bikeline.activity.ChooseLocationActivity.PREFERENCE_SEARCH_HISTORY";
    public static final int RESULT_OK = 1;
    private static final LatLng DEFAULT_LOCATION = new LatLng(24.786594f, 120.998084f);
    private static final float DEFAULT_ZOOM_LEVEL = 17f;

    // UI
    private ProgressDialog progressDialog = null;
    private SearchView searchView;
    private SearchResultsAdaptor adaptor;
    private static final String columns[] = new String[]{"_id", "query"};
    private FloatingActionButton finish;

    // GPS
    private Intent serviceIntent;
    private GoogleMap mMap;
    private GPSService gpsService;
    private Marker nowLocationMarker = null;

    private SharedPreferences sharedPreferences;
    private Set<String> searchHistory;
    private LatLng searchPosition;
    private String address = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.choose_location);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        if(getSupportActionBar() != null) {
            getSupportActionBar().setTitle(getIntent().getStringExtra(TITLE_EXTRA));
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
        if(!isNetworkAvailabel()) {
            View parentLayout = findViewById(R.id.choose_layout_view);
            final Snackbar snackBar = Snackbar.make(parentLayout, getResources().getString(R.string.no_network_msg), Snackbar.LENGTH_INDEFINITE);
            snackBar.setAction(getResources().getString(R.string.retry_msg), new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(isNetworkAvailabel())
                        snackBar.dismiss();
                }
            });
            snackBar.show();
        }

        sharedPreferences = getSharedPreferences(MainActivity.GLOBAL_PREFERENCE, MODE_PRIVATE);
        searchHistory = sharedPreferences.getStringSet(PREFERENCE_SEARCH_HISTORY, new HashSet<String>());

        finish = (FloatingActionButton)findViewById(R.id.finish_choose_location_button);
        finish.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent result = new Intent();
                result.putExtra(ADDRESS_EXTRA, searchView.getQuery().toString());
                if(searchPosition != null) {
                    result.putExtra(LATITUDE_EXTRA, Double.toString(searchPosition.latitude));
                    result.putExtra(LONGITUDE_EXTRA, Double.toString(searchPosition.longitude));
                }
                setResult(RESULT_OK, result);
                finishAfterTransition();
            }
        });

        adaptor = new SearchResultsAdaptor(this, R.layout.search_view_item, buildCursor(""), columns, null, 0);
        searchView = (SearchView)findViewById(R.id.choose_location_search_view);
        searchView.setSuggestionsAdapter(adaptor);
        searchView.setOnSuggestionListener(this);
        searchView.setIconifiedByDefault(false);
        ((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)).
                toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
        searchView.setOnQueryTextListener(this);
        searchView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                searchView.setIconified(false);
            }
        });
        searchView.setQueryHint(getResources().getString(R.string.search_hint));
        searchView.setOnSearchClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onQueryTextSubmit(searchView.getQuery().toString());
            }
        });
        AutoCompleteTextView searchAutoCompleteTextView = (AutoCompleteTextView) searchView.findViewById(getResources().getIdentifier("search_src_text", "id", getPackageName()));
        searchAutoCompleteTextView.setThreshold(0);
        EditText editSearch = ((EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text));
        editSearch.setHintTextColor(ContextCompat.getColor(this, R.color.colorHint));
        try {
            Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
            f.setAccessible(true);
            f.set(editSearch, R.drawable.cursor);// set textCursorDrawable to null
        } catch (Exception e) {
            e.printStackTrace();
        }

        address = getIntent().getStringExtra(ADDRESS_EXTRA);
        if(address != null) {
            if(!address.equals(getResources().getString(R.string.now_location_tag))) {
                searchView.setQuery(address, true);
            } else {
                searchView.setQuery(address, false);
                if(!isMyServiceRunning(GPSService.class))
                    Toast.makeText(this, "無法取得現在位置", Toast.LENGTH_SHORT).show();
            }
        }

        serviceIntent = new Intent(this, GPSService.class);
        if(isMyServiceRunning(GPSService.class)) {
            progressDialog = ProgressDialog.show(this, "", getResources().getString(R.string.please_wait), true, false);
            bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
        }

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.choose_location_map_fragment);
        mapFragment.getMapAsync(this);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                //finish();
                finishAfterTransition();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        mMap.getUiSettings().setMapToolbarEnabled(false);
        mMap.setOnMapLongClickListener(this);
        if(address != null)
            if (address.equals(getString(R.string.now_location_tag))) {
                if (gpsService != null) {
                    searchPosition = new LatLng(gpsService.getLatitude(), gpsService.getLongitude());
                    updateNowLocation(searchPosition, getResources().getString(R.string.now_location_tag), false);
                }
            } else
                onQueryTextSubmit(address);
        else if(gpsService != null)
            updateNowLocation(new LatLng(gpsService.getLatitude(), gpsService.getLongitude()), getString(R.string.now_location_tag), false);
        else
            updateNowLocation(DEFAULT_LOCATION, "", false);
    }

    @Override
    public void onMapLongClick(LatLng point) {
        try {
            Geocoder geocoder = new Geocoder(this);
            List<Address> addresses = geocoder.getFromLocation(point.latitude, point.longitude, 1);
            String address = addresses.get(0).getAddressLine(0);
            searchView.setQuery(address, false);
            //String city = addresses.get(0).getAddressLine(1);
            //String country = addresses.get(0).getAddressLine(2);
            searchPosition = point;
            updateNowLocation(point, "選擇目標", true);
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        if(query.length() == 0)
            return false;
        // get location from api
        Geocoder geocoder = new Geocoder(this);
        List<Address> addresses;
        try {
            addresses = geocoder.getFromLocationName(query, 1);
        } catch (IOException e) {
            return true;
        }
        if(addresses.size() > 0) {
            searchPosition = new LatLng(addresses.get(0).getLatitude(), addresses.get(0).getLongitude());
        } else {
            // no result was found
            Toast.makeText(this, getString(R.string.no_result), Toast.LENGTH_SHORT).show();
            searchPosition = null;
            return true;
        }
        searchView.clearFocus();
        searchHistory.add(query);
        updateNowLocation(searchPosition, getString(R.string.choose_location_tag), true);
        return true;
    }

    @Override
    public boolean onQueryTextChange(String query) {
        adaptor.changeCursor(buildCursor(query));
        return true;
    }

    @Override
    public boolean onSuggestionSelect(int position) {
        return false;
    }

    @Override
    public boolean onSuggestionClick(int position) {
        if(position != 0) {
            Cursor c = (Cursor) adaptor.getItem(position);
            String query = c.getString(1);
            searchView.setQuery(query, true);
        } else {
            if(gpsService != null) {
                searchView.setQuery(getString(R.string.now_location_tag), false);
                searchPosition = new LatLng(gpsService.getLatitude(), gpsService.getLongitude());
                searchView.clearFocus();
                updateNowLocation(searchPosition, getString(R.string.choose_location_tag), true);
            } else
                Toast.makeText(this, "無法取得現在位置", Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    protected void onDestroy() {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putStringSet(PREFERENCE_SEARCH_HISTORY, searchHistory);
        editor.apply();
        if(gpsService != null) {
            super.unbindService(mConnection);
            gpsService = null;
        }
        super.onDestroy();
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            GPSService.GPSBinder binder = (GPSService.GPSBinder) service;
            gpsService = binder.getService();
            gpsService.startListener(ChooseLocationActivity.this);
            progressDialog.dismiss();
            if(address == null)
                updateNowLocation(new LatLng(gpsService.getLatitude(), gpsService.getLongitude()),
                                                getResources().getString(R.string.now_location_tag), false);
            if(address != null && address.equals(getResources().getString(R.string.now_location_tag))) {
                Log.i("ChooseLocationActivity", "here");
                searchPosition = new LatLng(gpsService.getLatitude(), gpsService.getLongitude());
                updateNowLocation(searchPosition, getResources().getString(R.string.now_location_tag), false);
            }
        }
        public void onServiceDisconnected(ComponentName className) { }
    };

    private boolean updateNowLocation(LatLng location, String tag, boolean animation) {
        if(mMap != null) {
            if(nowLocationMarker != null)
                nowLocationMarker.remove();
            nowLocationMarker = mMap.addMarker(new MarkerOptions().position(location).title(tag));
            if(animation)
                mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(location, DEFAULT_ZOOM_LEVEL));
            else
                mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, DEFAULT_ZOOM_LEVEL));
            return true;
        } else
            return false;
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
            if (serviceClass.getName().equals(service.service.getClassName()))
                return true;
        return false;
    }

    public Cursor buildCursor(String query) {
        Log.i("buildCursor", query);
        MatrixCursor cursor = new MatrixCursor(columns);
        cursor.addRow(new String[]{Integer.toString(0), ""});
        int counter = 1;
        if(query.length() != 0)
            for(String history: searchHistory)
                if (history.contains(query))
                    cursor.addRow(new String[]{Integer.toString(counter++), history});
        return cursor;
    }

    public boolean isNetworkAvailabel() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        return activeNetworkInfo != null && activeNetworkInfo.isConnected();
    }

    private class SearchResultsAdaptor extends SimpleCursorAdapter {
        SearchResultsAdaptor(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
            super(context, layout, c, from, to, flags);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            if(cursor.getInt(0) != 0) {
                TextView textView = (TextView) view.findViewById(R.id.search_item_text);
                textView.setText(cursor.getString(1));
                View image = view.findViewById(R.id.search_item_image);
                image.setVisibility(View.GONE);
            } else {
                TextView textView = (TextView) view.findViewById(R.id.search_item_text);
                textView.setText(getString(R.string.now_location_tag));
                View image = view.findViewById(R.id.search_item_image);
                image.setVisibility(View.VISIBLE);
            }
        }
    }
}