package hr.fer.labust.diverapp;

import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.os.Bundle;
import android.text.InputFilter;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.FrameLayout;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.maps.GoogleMap;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import hr.fer.labust.diverapp.adapter.MainListAdapter;
import hr.fer.labust.diverapp.agent.AgentList;
import hr.fer.labust.diverapp.common.BluetoothMessage;
import hr.fer.labust.diverapp.common.ChatMessage;
import hr.fer.labust.diverapp.common.Colors;
import hr.fer.labust.diverapp.common.Constants;
import hr.fer.labust.diverapp.fragment.BluetoothFragment;
import hr.fer.labust.diverapp.fragment.ChatFragment;
import hr.fer.labust.diverapp.fragment.IdleFragment;
import hr.fer.labust.diverapp.fragment.MapFragment;
import hr.fer.labust.diverapp.fragment.MessagesFragment;

public class MainActivity extends FragmentActivity {
    public static final String MY_PREFS_NAME = "hr.fer.labust.diverapp";
    private String mName;

    private BluetoothService mBluetoothService;
    private AlertDialog.Builder mAlertDialogBuilder;
    private InputMethodManager mInputMethodManager;
    private FragmentTabHost mTabHost;
    private BlockingQueue<String> mMessagesQueue;
    private XMLParser mXMLParser;
    private AgentList mAgentList;
    private MainListAdapter mMainListAdapter;
    private TabWidget mTabWidget;
    private ObjectAnimator mChatTabAnimator;
    private Drawable mTabBackground;
    private SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private int screenWidth;
    private int screenHeight;

    private BluetoothFragment mBluetoothFragment;
    private MessagesFragment mMessagesFragment;
    private ChatFragment mChatFragment;
    private IdleFragment mIdleFragment;
    private MapFragment mMapFragment;

    private String TAB_MAP;
    private String TAB_CHAT;
    private String TAB_IDLE;
    private String TAB_MESSAGES;
    private String TAB_BLUETOOTH;

    private boolean wasBluetoothEnabled;
    private int mPreviousTabIndex = 0;
    private boolean showQueueFullWarning = true;

    public void setPreviousTabIndex() {
        mPreviousTabIndex = mTabHost.getCurrentTab();
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAlertDialogBuilder = new AlertDialog.Builder(this);

        if (BluetoothService.isAvailable()) {
            getActionBar().hide();
            mBluetoothService = new BluetoothService(mBluetoothHandler, getString(R.string.app_name));
            mInputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            initializeTabHost(savedInstanceState);
            getScreenSize();

            mMessagesQueue = new ArrayBlockingQueue<String>(100);
            mMainListAdapter = new MainListAdapter(getBaseContext());

            ExpandableListView list = (ExpandableListView)findViewById(R.id.mainlist);
            list.setAdapter(mMainListAdapter);
            mAgentList  = AgentList.getInstace();

            list.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
                @Override
                public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                    mMainListAdapter.expandAll(groupPosition);
                    return false;
                }
            });

            TAB_MAP = getString(R.string.tab_map);
            TAB_CHAT = getString(R.string.tab_chat);
            TAB_IDLE = getString(R.string.tab_idle);
            TAB_MESSAGES = getString(R.string.tab_messages);
            TAB_BLUETOOTH = getString(R.string.tab_bluetooth);
        }
        else {
            noBluetooth();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        Colors.reset();
        mXMLParser = new XMLParser(getBaseContext(), mMessagesQueue, mUIHandler, this);
        mXMLParser.start();

        wasBluetoothEnabled = mBluetoothService.isEnabled();

        SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
        mName = prefs.getString("name", "Diver");
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (mBluetoothService.isEnabled()) {
            mBluetoothService.disconnectAll();
            mBluetoothService.closeServer();

            if (!wasBluetoothEnabled) {
                mBluetoothService.disable();
            }
        }

        mXMLParser.interrupt();
        mMessagesQueue.clear();
    }

    @Override
    public void onBackPressed() {
        if (isSelectedTab(TAB_BLUETOOTH)) {
            mTabHost.setCurrentTab(mPreviousTabIndex);
            return;
        }

        if (isSelectedTab(TAB_MAP)) {
            mMapFragment.reset();
            if (mMapFragment.getState() == mMapFragment.STATE_GOTO_POINT) {
                mMapFragment.setState(mMapFragment.STATE_SEND_COMMAND);
                return;
            }
            else if (mMapFragment.getState() != mMapFragment.STATE_START) {
                mMapFragment.setState(mMapFragment.STATE_START);
                return;
            }
        }

        super.onBackPressed();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_main, menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
            case R.id.bluetooth_settings:
                mTabHost.setCurrentTabByTag(getString(R.string.tab_bluetooth));
               return true;
            case R.id.maptype_normal:
                mMapFragment.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                return true;
            case R.id.maptype_satellite:
                mMapFragment.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
                return true;
            case R.id.maptype_terrain:
                mMapFragment.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
                return true;
            case R.id.maptype_hybrid:
                mMapFragment.setMapType(GoogleMap.MAP_TYPE_HYBRID);
                return true;
            case R.id.remove_points:
                removePointsDialog();
                return true;
            case R.id.change_name:
                changeNameDialog();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void initializeTabHost(Bundle bundle) {
        mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
        mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
        mTabWidget = (TabWidget)findViewById(android.R.id.tabs);
        mChatTabAnimator = getObjectAnimator(Color.YELLOW);

        mTabHost.addTab(
                mTabHost.newTabSpec(getString(R.string.tab_map)).setIndicator(getString(R.string.tab_map)),
                MapFragment.class,
                null);

        mTabHost.addTab(
                mTabHost.newTabSpec(getString(R.string.tab_chat)).setIndicator(getString(R.string.tab_chat)),
                ChatFragment.class,
                null);
        mTabBackground = mTabWidget.getChildTabViewAt(1).getBackground();
        mChatTabAnimator.setTarget(mTabWidget.getChildTabViewAt(1));
        mChatTabAnimator.end();
        mTabWidget.getChildTabViewAt(1).setBackground(mTabBackground);

        mTabHost.addTab(
                mTabHost.newTabSpec(getString(R.string.tab_idle)).setIndicator(getString(R.string.tab_idle)),
                IdleFragment.class,
                null);

        mTabHost.addTab(
                mTabHost.newTabSpec(getString(R.string.tab_messages)).setIndicator(getString(R.string.tab_messages)),
                MessagesFragment.class,
                null);

        mTabHost.addTab(
                mTabHost.newTabSpec(getString(R.string.tab_bluetooth)).setIndicator(""),
                BluetoothFragment.class,
                null);
        mTabHost.getTabWidget().getChildTabViewAt(mTabHost.getTabWidget().getTabCount() - 1).setVisibility(View.GONE);

        mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
                mInputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
                if (tabId.equals(getString(R.string.tab_chat))) {
                    if (IdleFragment.getState() == IdleFragment.STATE_MESSAGE_RECEIVED) {
                        IdleFragment.setState(IdleFragment.STATE_IDLE);
                    }
                    mChatTabAnimator.end();
                    mTabWidget.getChildTabViewAt(1).setBackground(mTabBackground);
                }
            }
        });
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);

        if (fragment instanceof BluetoothFragment) {
            mBluetoothFragment = (BluetoothFragment)fragment;
        }
        else if (fragment instanceof MessagesFragment) {
            mMessagesFragment = (MessagesFragment)fragment;
        }
        else if (fragment instanceof IdleFragment) {
            mIdleFragment = (IdleFragment)fragment;
        }
        else if (fragment instanceof ChatFragment) {
            mChatFragment = (ChatFragment)fragment;
        }
        else if (fragment instanceof MapFragment) {
            mMapFragment = (MapFragment)fragment;
        }
    }

    private AlertDialog makeDialog(String title, String message, String buttonText) {
        mAlertDialogBuilder.setTitle(title);
        mAlertDialogBuilder.setMessage(message);
        mAlertDialogBuilder.setPositiveButton(buttonText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        return mAlertDialogBuilder.create();
    }

    private void noBluetooth() {
        String title = getString(R.string.no_bluetooth);
        String message = getString(R.string.no_bluetooth_message);

        mAlertDialogBuilder.setTitle(title);
        mAlertDialogBuilder.setMessage(message);
        mAlertDialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        });

        AlertDialog dialog = mAlertDialogBuilder.create();
        dialog.show();
    }

    public BluetoothService getBluetoothService() {
        return mBluetoothService;
    }

    private final Handler mBluetoothHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BluetoothService.STATE_CHANGED:
                    int state = msg.arg1;
                    if (mTabHost.getCurrentTabTag().equals(getString(R.string.tab_bluetooth))) {
                        if (mBluetoothFragment != null) {
                            mBluetoothFragment.connectionStateChanged((BluetoothDevice)msg.obj);
                        }
                    }
                    else {
                        if (state == BluetoothService.STATE_CONNECTED) {
                            Toast.makeText(getBaseContext(), getString(R.string.connection_established), Toast.LENGTH_SHORT).show();
                        }
                        else if (state == BluetoothService.STATE_DISCONNECTED) {
                            Toast.makeText(getBaseContext(), getString(R.string.connection_lost), Toast.LENGTH_SHORT).show();
                        }
                        else if (state == BluetoothService.LOST_CONNECTION) {

                        }
                    }
                    break;

                case BluetoothService.MESSAGE_RECEIVED:
                    String message = ((BluetoothMessage) msg.obj).message;
                    String name = ((BluetoothMessage) msg.obj).device.getName();
                    MessagesFragment.addMessage(mDateFormat.format(Calendar.getInstance().getTime()) + "\n" + name + ":\n" + message);
                    if (isSelectedTab(TAB_MESSAGES)) {
                        mMessagesFragment.update();
                    }

                    try {
                        mMessagesQueue.add(message);
                    }
                    catch(NullPointerException e) {}
                    catch(IllegalStateException e) {
                        if (showQueueFullWarning) {
                            showQueueFullWarning = false;

                            String title = getString(R.string.warning);
                            String text = getString(R.string.queue_full);
                            AlertDialog dialog = makeDialog(title, text, "OK");
                            dialog.show();

                            Handler handler = new Handler();
                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    showQueueFullWarning = true;
                                }
                            }, 10000);
                        }
                    }

                    break;

                case BluetoothService.MESSAGE_SENT:

                    break;
            }
        }
    };

    private final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case Constants.UI_ADD_MESSAGE:
                    MessagesFragment.addMessage((String)msg.obj);
                    if (isSelectedTab(TAB_MESSAGES)) {
                        mMessagesFragment.update();
                    }
                    break;

                case Constants.UI_ADD_CHAT_MESSAGE:
                    ChatFragment.addMessage((ChatMessage)msg.obj);
                    if (isSelectedTab(TAB_CHAT)) {
                        mChatFragment.update();
                        IdleFragment.messageReceived = false;
                    }
                    else {
                        IdleFragment.setState(IdleFragment.STATE_MESSAGE_RECEIVED);
                        if (mChatTabAnimator.isRunning() == false) {
                            mChatTabAnimator.start();
                        }
                        IdleFragment.messageReceived = true;
                        if (isSelectedTab(TAB_IDLE)) {
                            mIdleFragment.update();
                        }
                    }
                    break;

                case Constants.FLAG:
                    String description = (String)msg.obj;
                    if (description == null) {
                        description = "";
                    }
                    switch(msg.arg1) {
                        case Constants.FLAG_MESSAGE_START:

                            break;
                        case Constants.FLAG_ACKNOWLEDGED:
                            IdleFragment.setState(IdleFragment.STATE_ACKNOWLEDGED);
                            break;
                        case Constants.FLAG_ERROR:
                            IdleFragment.setState(IdleFragment.STATE_ERROR);
                            showStatusDialog("ERROR", description, Color.RED);
                            break;
                        case Constants.FLAG_LOST_COMMUNICATION:
                            IdleFragment.setState(IdleFragment.STATE_LOST_COMMUNICATION);
                            showStatusDialog("LOST COMMUNICATION", description, Color.BLUE);
                            break;
                    }
                    if (isSelectedTab(TAB_IDLE)) {
                        mIdleFragment.update();
                    }

                    break;

                case Constants.UI_UPDATE_STATS:
                    mMainListAdapter.update((String)msg.obj);
                    mMainListAdapter.notifyDataSetChanged();

                    if (msg.arg1 == 1) {
                        mMapFragment.update((String)msg.obj);
                    }
                    break;

            }
        }
    };

    private boolean isSelectedTab(String name) {
        return mTabHost.getCurrentTabTag().equals(name);
    }

    public void updateList(String name) {
        mMainListAdapter.update(name);
        mMainListAdapter.notifyDataSetChanged();
    }

    private void removePointsDialog() {
        final ArrayList<String> names = mAgentList.getAgentNames();
        final ArrayList<Integer> selectedItems = new ArrayList<Integer>();
        final CharSequence[] namesSequence = new CharSequence[names.size()];
        for (int i=0; i<names.size(); i++) {
            namesSequence[i] = names.get(i);
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(getString(R.string.settings_remove_points));
        builder.setMultiChoiceItems(namesSequence, null, new DialogInterface.OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                if (isChecked) {
                    selectedItems.add(which);
                } else if (selectedItems.contains(which)) {
                    selectedItems.remove(Integer.valueOf(which));
                }
            }
        }).setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                for (Integer i: selectedItems) {
                    mMainListAdapter.removed(names.get(i));
                    mAgentList.remove(names.get(i));
                    mMapFragment.removeMarker(names.get(i));
                }
                mMainListAdapter.notifyDataSetChanged();
            }
        }).setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        builder.create().show();
    }

    private void changeNameDialog() {
        final EditText input = new EditText(this);
        input.setText(mName);
        input.setHint(getString(R.string.enter_name));
        input.selectAll();
        input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(25)});
        input.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                    return true;
                }
                return false;
            }
        });

        final AlertDialog dialog = new AlertDialog.Builder(this)
                .setTitle(getString(R.string.settings_change_name))
                .setView(input)
                .setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .create();

        dialog.show();
        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String inputName = input.getText().toString().trim();
                if (inputName.length() > 0) {
                    SharedPreferences.Editor edit = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
                    edit.putString("name", inputName);
                    edit.commit();

                    mName = inputName;
                    if (mChatFragment != null) {
                        mChatFragment.setName(inputName);
                    }

                    dialog.dismiss();
                }
            }
        });
    }

    public String getName() {
        return mName;
    }

    private ObjectAnimator getObjectAnimator(int color) {
        int r = Color.red(color) / 2;
        int g = Color.green(color) / 2;
        int b = Color.blue(color) / 2;

        ObjectAnimator animator = new ObjectAnimator();
        animator.setRepeatCount(ObjectAnimator.INFINITE);
        animator.setPropertyName("backgroundColor");
        animator.setIntValues(color, Color.rgb(r, g, b));
        animator.setRepeatMode(ObjectAnimator.REVERSE);
        animator.setDuration(200);
        animator.setEvaluator(new ArgbEvaluator());

        return animator;
    }

    private void showStatusDialog(String title, String text, int color) {
        final Dialog dialog = new Dialog(this);
        dialog.setContentView(R.layout.dialog_status);

        //Set title
        dialog.setTitle(title);
        ((TextView)dialog.findViewById(R.id.dialog_message)).setText(text);

        //Button function
        ((Button)dialog.findViewById((R.id.dialog_button))).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                IdleFragment.setState(IdleFragment.STATE_IDLE);
                if (isSelectedTab(TAB_IDLE)) {
                    mIdleFragment.update();
                }

                dialog.dismiss();
            }
        });

        //Set blinking frame
        FrameLayout blinkingFrame = (FrameLayout)dialog.findViewById(R.id.dialog_blinking_frame);
        ObjectAnimator animator = getObjectAnimator(color);
        animator.setTarget(blinkingFrame);
        animator.start();

        //Set dialog to fill 90% of screen
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
        lp.copyFrom(dialog.getWindow().getAttributes());
        lp.width = (int)(0.9*screenWidth);
        lp.height = (int)(0.9*screenHeight);

        //Show dialog
        dialog.show();
        dialog.getWindow().setAttributes(lp);
    }

    private void getScreenSize() {
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);

        screenWidth = metrics.widthPixels;
        screenHeight = metrics.heightPixels;
    }
}
