package hr.fer.labust.diverapp.fragment;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import hr.fer.labust.diverapp.BluetoothService;
import hr.fer.labust.diverapp.MainActivity;
import hr.fer.labust.diverapp.R;
import hr.fer.labust.diverapp.agent.AgentList;

public class MapFragment extends Fragment implements OnMapReadyCallback, GoogleMap.OnMapClickListener{
    private View mView;
    private MainActivity mActivity;
    private GoogleMap mMap;
    private MapView mMapView;
    private Marker mOnClickMarker;
    private final AgentList mAgentList = AgentList.getInstace();
    private final HashMap<String, Marker> mMarkers = new HashMap<>();
    private BluetoothService mBluetoothService;

    private LinearLayout mStartLayout;
    private RelativeLayout mAddPointLayout;
    private RelativeLayout mSendCommandLayout;
    private RelativeLayout mGotoPointLayout;

    private EditText mAddPointName;
    private EditText mAddPointLat;
    private EditText mAddPointLng;

    private CheckBox mGotoTakePhotoCheckbox;
    private Spinner mGotoSpinner;
    private ArrayAdapter<String> mSpinnerAdapter;
    private EditText mGotoLat;
    private EditText mGotoLng;

    public static final int STATE_START = 0;
    public static final int STATE_SEND_COMMAND = 1;
    public static final int STATE_ADD_POINT = 2;
    public static final int STATE_GOTO_POINT = 3;
    private int mState = STATE_START;

    public int getState() {
        return mState;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivity = (MainActivity)getActivity();
        MapsInitializer.initialize(mActivity.getApplicationContext());
        mBluetoothService = mActivity.getBluetoothService();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
        if (mView == null) {
            mView = inflater.inflate(R.layout.fragment_map, container, false);

            mMapView = (MapView) mView.findViewById(R.id.map_mapview);
            mMapView.onCreate(savedInstanceState);
            mMapView.getMapAsync(this);
            mMapView.onResume();

            initializeLayouts(mView);
        }
        else {
            ViewGroup parent = (ViewGroup)mView.getParent();

            if (parent != null) {
                parent.removeView(mView);
            }
        }

        return mView;
    }

    @Override
    public void onStart() {
        super.onStart();
        mActivity.setPreviousTabIndex();
    }

    @Override
    public void onMapClick(LatLng point) {
        double latitude = point.latitude;
        double longitude = point.longitude;

        if (mState == STATE_ADD_POINT) {
            mAddPointLat.setText(Double.toString(latitude));
            mAddPointLng.setText(Double.toString(longitude));
        }
        else if (mState == STATE_GOTO_POINT) {
            mGotoLat.setText(Double.toString(latitude));
            mGotoLng.setText(Double.toString(longitude));
        }

        mOnClickMarker.setPosition(point);
        mOnClickMarker.setVisible(true);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        mMap.setMyLocationEnabled(true);

        SharedPreferences prefs = mActivity.getSharedPreferences(MainActivity.MY_PREFS_NAME, Context.MODE_PRIVATE);
        mMap.setMapType(prefs.getInt("map_type", GoogleMap.MAP_TYPE_NORMAL));

        int px = getResources().getDimensionPixelSize(R.dimen.map_onclick_marker_size);
        Bitmap markerBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(markerBitmap);
        Drawable shape = ContextCompat.getDrawable(mActivity.getBaseContext(), R.drawable.circle);
        shape.setBounds(0, 0, markerBitmap.getWidth(), markerBitmap.getHeight());
        shape.draw(canvas);

        mOnClickMarker = mMap.addMarker(new MarkerOptions()
                .icon(BitmapDescriptorFactory.fromBitmap(markerBitmap))
                .position(new LatLng(0.0, 0.0))
                .anchor(0.5f, 0.5f)
                .visible(false));

        setState(STATE_START);
        update();
    }

    public void setState(int state) {
        if (state == STATE_ADD_POINT) {
            mState = state;
            mGotoPointLayout.setVisibility(RelativeLayout.GONE);
            mSendCommandLayout.setVisibility(RelativeLayout.GONE);
            mStartLayout.setVisibility(LinearLayout.GONE);
            mAddPointLayout.setVisibility(RelativeLayout.VISIBLE);
            mMap.setOnMapClickListener(this);
            return;
        }
        else if (state == STATE_GOTO_POINT) {
            mState = state;
            mSendCommandLayout.setVisibility(RelativeLayout.GONE);
            mStartLayout.setVisibility(LinearLayout.GONE);
            mAddPointLayout.setVisibility(RelativeLayout.GONE);
            mGotoPointLayout.setVisibility(RelativeLayout.VISIBLE);
            mMap.setOnMapClickListener(this);

            mSpinnerAdapter.clear();
            mSpinnerAdapter.add("Select Point");
            mSpinnerAdapter.addAll(mAgentList.getPointNames());
            mGotoSpinner.setSelection(0);
            return;
        }
        else if (state == STATE_SEND_COMMAND) {
            mState = state;
            mGotoPointLayout.setVisibility(RelativeLayout.GONE);
            mStartLayout.setVisibility(LinearLayout.GONE);
            mAddPointLayout.setVisibility(RelativeLayout.GONE);
            mSendCommandLayout.setVisibility(RelativeLayout.VISIBLE);
            mMap.setOnMapClickListener(null);
            mOnClickMarker.setVisible(false);
            return;
        }

        mState = STATE_START;
        mAddPointLayout.setVisibility(RelativeLayout.GONE);
        mGotoPointLayout.setVisibility(RelativeLayout.GONE);
        mSendCommandLayout.setVisibility(RelativeLayout.GONE);
        mStartLayout.setVisibility(LinearLayout.VISIBLE);
        mMap.setOnMapClickListener(null);
        mOnClickMarker.setVisible(false);
    }

    private void initializeLayouts(View view) {
        initializeStartLayout(view);
        initializeAddPointLayout(view);
        initializeSendCommandLayout(view);
        initializeGotoLayout(view);
    }

    private void initializeStartLayout(View view) {
        mStartLayout = (LinearLayout)mView.findViewById(R.id.map_layout_start);

        Button button = (Button)view.findViewById(R.id.map_layout_start_button_send_command);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setState(STATE_SEND_COMMAND);
            }
        });

        button = (Button)view.findViewById(R.id.map_layout_start_button_add_point);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setState(STATE_ADD_POINT);
            }
        });

        button = (Button)view.findViewById(R.id.map_layout_start_button_view_all);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                updateCamera();
            }
        });
    }

    private void initializeAddPointLayout(View view) {
        mAddPointLayout = (RelativeLayout)view.findViewById(R.id.map_layout_add_point);

        mAddPointName = (EditText)mAddPointLayout.findViewById(R.id.map_layout_add_point_edittext_name);
        mAddPointLat = (EditText)mAddPointLayout.findViewById(R.id.map_layout_add_point_edittext_latitude);
        mAddPointLng = (EditText)mAddPointLayout.findViewById(R.id.map_layout_add_point_edittext_longitude);

        mAddPointName.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                    InputMethodManager inputManager =(InputMethodManager)mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
                    inputManager.hideSoftInputFromWindow(mAddPointName.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                    return true;
                }
                return false;
            }
        });

        Button button = (Button)view.findViewById(R.id.map_layout_add_point_button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = mAddPointName.getText().toString();
                String latitude = mAddPointLat.getText().toString();
                String longitude = mAddPointLng.getText().toString();

                if (name.length() == 0) {
                    Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_point_name), Toast.LENGTH_SHORT).show();
                    return;
                }

                if (mAgentList.contains(name)) {
                    Toast.makeText(mActivity.getBaseContext(), getString(R.string.point_exists), Toast.LENGTH_SHORT).show();
                    return;
                }

                if ((latitude.length() == 0) || (longitude.length() == 0)) {
                    Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_point_value), Toast.LENGTH_SHORT).show();
                    return;
                }

                mAgentList.setPosition(name, latitude, longitude);
                mActivity.updateList(name);
                update(name);

                mAddPointName.setText("");
                mAddPointLat.setText("");
                mAddPointLng.setText("");
                setState(STATE_START);
                HideKeyboard();
            }
        });
    }

    private void initializeSendCommandLayout(View view) {
        mSendCommandLayout = (RelativeLayout)view.findViewById(R.id.map_layout_commands);
        Button button;

        button = (Button)mSendCommandLayout.findViewById(R.id.map_layout_commands_button_goto_point);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setState(STATE_GOTO_POINT);
            }
        });

        button = (Button)mSendCommandLayout.findViewById(R.id.map_layout_commands_button_take_photo);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (sendCommand("take_photo")) {
                    Toast.makeText(mActivity.getBaseContext(), "Sent Command: Take Photo", Toast.LENGTH_LONG).show();
                    setState(STATE_START);
                }
            }
        });

        button = (Button)mSendCommandLayout.findViewById(R.id.map_layout_commands_button_goto_surface);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (sendCommand("goto_surface")) {
                    Toast.makeText(mActivity.getBaseContext(), "Sent Command: Go To Surface", Toast.LENGTH_LONG).show();
                    setState(STATE_START);
                }
            }
        });

        button = (Button)mSendCommandLayout.findViewById(R.id.map_layout_commands_button_hold_position);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (sendCommand("hold_position")) {
                    Toast.makeText(mActivity.getBaseContext(), "Sent Command: Hold Position", Toast.LENGTH_LONG).show();
                    setState(STATE_START);
                }
            }
        });

        button = (Button)mSendCommandLayout.findViewById(R.id.map_layout_commands_button_resume_operation);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (sendCommand("resume_operation")) {
                    Toast.makeText(mActivity.getBaseContext(), "Sent Command: Resume Operation", Toast.LENGTH_LONG).show();
                    setState(STATE_START);
                }
            }
        });
    }

    private void initializeGotoLayout(View view) {
        mGotoPointLayout = (RelativeLayout)view.findViewById(R.id.map_layout_goto_point);

        mGotoTakePhotoCheckbox = (CheckBox)mGotoPointLayout.findViewById(R.id.map_layout_goto_point_checkbox_take_photo);
        mGotoLat= (EditText)mGotoPointLayout.findViewById(R.id.map_layout_goto_point_edittext_latitude);
        mGotoLng= (EditText)mGotoPointLayout.findViewById(R.id.map_layout_goto_point_edittext_longitude);

        mGotoSpinner = (Spinner)mGotoPointLayout.findViewById(R.id.map_layout_goto_point_spinner_points);
        mSpinnerAdapter = new ArrayAdapter<String>(mActivity.getBaseContext(), android.R.layout.simple_list_item_1) {
            @Override
            public View getDropDownView(int position, View convertView, ViewGroup parent) {
                View v;

                if (position == 0) {
                    TextView tv = new TextView(mActivity.getBaseContext());
                    tv.setHeight(0);
                    tv.setVisibility(View.GONE);
                    v = tv;
                }
                else {
                    v = super.getDropDownView(position, null, parent);
                }

                return v;
            }
        };

        mSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mGotoSpinner.setAdapter(mSpinnerAdapter);
        mGotoSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (position == 0) {
                    return;
                }

                String selection = ((TextView)view).getText().toString();
                //LatLng point = mActivity.getPoint(selection);

                LatLng point;
                if (!mAgentList.contains(selection)) {
                    selection = selection.substring(0, selection.lastIndexOf(' '));
                    point = mAgentList.getAgent(selection).getDestination();
                }
                else {
                    point = mAgentList.getAgent(selection).getPosition();
                }

                if (point != null) {
                    mGotoLat.setText(Double.toString(point.latitude));
                    mGotoLng.setText(Double.toString(point.longitude));
                }

                mGotoSpinner.setSelection(0);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

        Button button = (Button)mGotoPointLayout.findViewById(R.id.map_layout_goto_point_button_send_command);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String latitude = mGotoLat.getText().toString();
                String longitude = mGotoLng.getText().toString();

                if (mGotoTakePhotoCheckbox.isChecked()) {
                    if (sendCommand("goto_take_photo", latitude, longitude)) {
                        mGotoTakePhotoCheckbox.setChecked(false);
                        mGotoLat.setText("");
                        mGotoLng.setText("");
                        setState(STATE_START);
                        Toast.makeText(mActivity.getBaseContext(), "Sent Command: Go To Point + Take Photo", Toast.LENGTH_SHORT).show();
                        HideKeyboard();
                    }
                }
                else {
                    if (sendCommand("goto", latitude, longitude)) {
                        mGotoTakePhotoCheckbox.setChecked(false);
                        mGotoLat.setText("");
                        mGotoLng.setText("");
                        setState(STATE_START);
                        Toast.makeText(mActivity.getBaseContext(), "Sent Command: Go To Point", Toast.LENGTH_SHORT).show();
                        HideKeyboard();
                    }
                }
            }
        });
    }

    public void update() {
        for(String s: mAgentList.getPointNames()) {
            update(s);
        }
    }

    public void update(String name) {
        if (!mAgentList.contains(name)) {
            return;
        }

        LatLng position = mAgentList.getAgent(name).getPosition();
        LatLng destination = mAgentList.getAgent(name).getDestination();

        if (position != null) {
            if (mMarkers.containsKey(name)) {
                mMarkers.get(name).setPosition(position);
            }
            else {
                addMarker(name, mAgentList.getAgent(name).getColor(), position);
            }
        }

        if (destination != null) {
            if (mMarkers.containsKey(name + " Destination")) {
                mMarkers.get(name + " Destination").setPosition(destination);
            }
            else {
                addMarker(name + " Destination", mAgentList.getAgent(name).getDestinationColor(), destination);
            }
        }
    }

    private void addMarker(String name, int color, LatLng position) {
        Marker marker = mMap.addMarker(new MarkerOptions()
                .position(position)
                .icon(BitmapDescriptorFactory.fromBitmap(createMarkerCircle(color)))
                //.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory))
                .anchor(0.5f, 0.5f));

        mMarkers.put(name, marker);
    }

    private Bitmap createMarkerCircle(int color) {
        int px = getResources().getDimensionPixelSize(R.dimen.map_marker_size);
        Bitmap markerBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(markerBitmap);
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);

        px /= 2;
        canvas.drawCircle(px, px, px, paint);
        paint.setColor(color);
        canvas.drawCircle(px, px, px - 3, paint);

        return markerBitmap;
    }

    public boolean sendCommand(String id) {
        if (mBluetoothService.numberOfConnections() > 0) {
            String xml = "";

            xml += "<DiverApp from=\"BuddyTab\">";
            xml += "<Command id=\"" + id + "\"/>";
            xml += "</DiverApp>";

            if (mBluetoothService.sendCommand(xml.getBytes())) {
                return true;
            }
            else {
                Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_command_clients), Toast.LENGTH_SHORT).show();
                return false;
            }
        }
        else {
            Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_connection), Toast.LENGTH_SHORT).show();
            return false;
        }
    }

    private boolean sendCommand(String id, String latitude, String longitude) {
        if ((latitude.length() == 0) || (longitude.length() == 0)) {
            Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_point_value), Toast.LENGTH_SHORT).show();
            return false;
        }

        if (mBluetoothService.numberOfConnections() > 0) {
            String xml = "";

            xml += "<DiverApp from=\"BuddyTab\">";
            xml += "<Command id=\"" + id + "\" latitude=\"" + latitude + "\" longitude=\"" + longitude + "\"/>";
            xml += "</DiverApp>";

            if (mBluetoothService.sendCommand(xml.getBytes())) {
                return true;
            }
            else {
                Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_command_clients), Toast.LENGTH_SHORT).show();
                return false;
            }
        }
        else {
            Toast.makeText(mActivity.getBaseContext(), getString(R.string.no_connection), Toast.LENGTH_SHORT).show();
            return false;
        }
    }

    public void reset() {
        if (mState == STATE_ADD_POINT) {
            mAddPointName.setText("");
            mAddPointLat.setText("");
            mAddPointLng.setText("");
        }
        else if (mState == STATE_GOTO_POINT) {
            mGotoTakePhotoCheckbox.setChecked(false);
            mGotoLat.setText("");
            mGotoLng.setText("");
        }
    }

    public void setMapType(int mapType) {
        mMap.setMapType(mapType);

        SharedPreferences.Editor editor = mActivity.getSharedPreferences(mActivity.MY_PREFS_NAME, Context.MODE_PRIVATE).edit();
        editor.putInt("map_type", mapType);
        editor.commit();
    }

    private void HideKeyboard() {
        InputMethodManager inputMethodManager = (InputMethodManager)mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0);
    }

    private void updateCamera() {
        // If there is no marker on the map
        if (mMarkers.size() == 0) {
            return;
        }

        // If only one marker exists
        if (mMarkers.size() == 1) {
            CameraUpdate cu = CameraUpdateFactory.newLatLngZoom(
                    mMarkers.entrySet().iterator().next().getValue().getPosition(), 10.0f);
            mMap.animateCamera(cu);
            return;
        }

        // Else
        LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder();
        for (Map.Entry<String, Marker> entry: mMarkers.entrySet()) {
            boundsBuilder.include(entry.getValue().getPosition());
        }

        LatLngBounds bounds = boundsBuilder.build();

        CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 100);
        mMap.animateCamera(cu);
    }

    public void removeMarker(String name) {
        if (mMarkers.containsKey(name)) {
            mMarkers.get(name).remove();
            mMarkers.remove(name);
        }

        name += " Destination";
        if (mMarkers.containsKey(name)) {
            mMarkers.get(name).remove();
            mMarkers.remove(name);
        }
    }
}
