/**
 * Copyright (c) 2012 Todoroo Inc
 *
 * See the file "LICENSE" for the full license governing this code.
 */
package com.todoroo.astrid.activity;

import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.speech.SpeechRecognizer;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.view.WindowManager.BadTokenException;
import android.widget.EditText;
import android.widget.Toast;

import com.timsu.astrid.R;
import com.todoroo.aacenc.RecognizerApi.RecognizerApiListener;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.actfm.CommentsActivity;
import com.todoroo.astrid.actfm.CommentsFragment;
import com.todoroo.astrid.actfm.TagCommentsFragment;
import com.todoroo.astrid.actfm.TaskCommentsFragment;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.api.IntentFilter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.core.SearchFilter;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.ui.DateChangedAlerts;
import com.todoroo.astrid.ui.QuickAddBar;
import com.todoroo.astrid.voice.VoiceRecognizer;

/**
 * This wrapper activity contains all the glue-code to handle the callbacks between the different
 * fragments that could be visible on the screen in landscape-mode.
 * So, it basically contains all the callback-code from the filterlist-fragment, the tasklist-fragments
 * and the taskedit-fragment (and possibly others that should be handled).
 * Using this AstridWrapperActivity helps to avoid duplicated code because its all gathered here for sub-wrapperactivities
 * to use.
 *
 * @author Arne
 *
 */
public class AstridActivity extends FragmentActivity
    implements FilterListFragment.OnFilterItemClickedListener,
    TaskListFragment.OnTaskListItemClickedListener,
    RecognizerApiListener {

    public static final int LAYOUT_SINGLE = 0;
    public static final int LAYOUT_DOUBLE = 1;
    public static final int LAYOUT_TRIPLE = 2;

    public static final int RESULT_RESTART_ACTIVITY = 50;

    protected int fragmentLayout = LAYOUT_SINGLE;

    private final RepeatConfirmationReceiver repeatConfirmationReceiver = new RepeatConfirmationReceiver();

    @Autowired
    private TaskDao taskDao;

    public FilterListFragment getFilterListFragment() {
        FilterListFragment frag = (FilterListFragment) getSupportFragmentManager()
                .findFragmentByTag(FilterListFragment.TAG_FILTERLIST_FRAGMENT);

        return frag;
    }

    public TaskListFragment getTaskListFragment() {
        TaskListFragment frag = (TaskListFragment) getSupportFragmentManager()
                .findFragmentByTag(TaskListFragment.TAG_TASKLIST_FRAGMENT);

        return frag;
    }

    public TaskEditFragment getTaskEditFragment() {
        TaskEditFragment frag = (TaskEditFragment) getSupportFragmentManager()
                .findFragmentByTag(TaskEditFragment.TAG_TASKEDIT_FRAGMENT);

        return frag;
    }

    public CommentsFragment getTagUpdatesFragment() {
        CommentsFragment frag = (CommentsFragment) getSupportFragmentManager()
                .findFragmentByTag(CommentsFragment.TAG_UPDATES_FRAGMENT);

        return frag;
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        DependencyInjectionService.getInstance().inject(this);
        super.onCreate(savedInstanceState);
        ContextManager.setContext(this);
        StatisticsService.sessionStart(this);

        new StartupService().onStartupApplication(this);
    }

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

        android.content.IntentFilter repeatFilter = new android.content.IntentFilter(
                AstridApiConstants.BROADCAST_EVENT_TASK_REPEATED);
        repeatFilter.addAction(AstridApiConstants.BROADCAST_EVENT_TASK_REPEAT_FINISHED);
        registerReceiver(repeatConfirmationReceiver, repeatFilter);
    }

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

        StatisticsService.sessionPause();
        AndroidUtilities.tryUnregisterReceiver(this, repeatConfirmationReceiver);
    }

    @Override
    protected void onStop() {
        super.onStop();
        StatisticsService.sessionStop(this);
    }

    /**
     * Handles items being clicked from the filterlist-fragment. Return true if item is handled.
     */
    public boolean onFilterItemClicked(FilterListItem item) {
        if (this instanceof TaskListActivity && (item instanceof Filter) ) {
            ((TaskListActivity) this).setSelectedItem((Filter) item);
        }
        if (item instanceof SearchFilter) {
            onSearchRequested();
            StatisticsService.reportEvent(StatisticsConstants.FILTER_SEARCH);
            return false;
        } else {
            // If showing both fragments, directly update the tasklist-fragment
            Intent intent = getIntent();

            if(item instanceof Filter) {
                Filter filter = (Filter)item;

                Bundle extras = configureIntentAndExtrasWithFilter(intent, filter);
                if (fragmentLayout == LAYOUT_TRIPLE && getTaskEditFragment() != null) {
                    onBackPressed(); // remove the task edit fragment when switching between lists
                }
                setupTasklistFragmentWithFilter(filter, extras);

                // no animation for dualpane-layout
                AndroidUtilities.callOverridePendingTransition(this, 0, 0);
                StatisticsService.reportEvent(StatisticsConstants.FILTER_LIST);
                return true;
            } else if(item instanceof IntentFilter) {
                try {
                    ((IntentFilter)item).intent.send();
                } catch (CanceledException e) {
                    // ignore
                }
            }
            return false;
        }
    }

    protected Bundle configureIntentAndExtrasWithFilter(Intent intent, Filter filter) {
        if(filter instanceof FilterWithCustomIntent) {
            int lastSelectedList = intent.getIntExtra(FilterListFragment.TOKEN_LAST_SELECTED, 0);
            intent = ((FilterWithCustomIntent)filter).getCustomIntent();
            intent.putExtra(FilterListFragment.TOKEN_LAST_SELECTED, lastSelectedList);
        } else {
            intent.putExtra(TaskListFragment.TOKEN_FILTER, filter);
        }

        setIntent(intent);

        Bundle extras = intent.getExtras();
        if (extras != null)
            extras = (Bundle) extras.clone();
        return extras;
    }

    public void setupActivityFragment(TagData tagData) {
        if (fragmentLayout == LAYOUT_SINGLE)
            return;

        if (fragmentLayout == LAYOUT_TRIPLE)
            findViewById(R.id.taskedit_fragment_container).setVisibility(View.VISIBLE);
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction transaction = manager.beginTransaction();

        TagCommentsFragment updates = new TagCommentsFragment(tagData);
        transaction.replace(R.id.taskedit_fragment_container, updates, CommentsFragment.TAG_UPDATES_FRAGMENT);
        transaction.commit();
    }

    public void setupTasklistFragmentWithFilter(Filter filter, Bundle extras) {
        Class<?> customTaskList = null;

        if (SubtasksHelper.shouldUseSubtasksFragmentForFilter(filter))
            customTaskList = SubtasksHelper.subtasksClassForFilter(filter);

        setupTasklistFragmentWithFilterAndCustomTaskList(filter, extras, customTaskList);
    }

    public void setupTasklistFragmentWithFilterAndCustomTaskList(Filter filter, Bundle extras, Class<?> customTaskList) {
        TaskListFragment newFragment = TaskListFragment.instantiateWithFilterAndExtras(filter, extras, customTaskList);

        try {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction.replace(R.id.tasklist_fragment_container, newFragment,
                    TaskListFragment.TAG_TASKLIST_FRAGMENT);
            transaction.commit();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    getSupportFragmentManager().executePendingTransactions();
                }
            });
        } catch (Exception e) {
            // Don't worry about it
            e.printStackTrace();
        }
    }

    @Override
    public void onTaskListItemClicked(long taskId) {
        Task task = taskDao.fetch(taskId, Task.FLAGS, Task.USER_ID);
        if (task != null)
            onTaskListItemClicked(taskId, task.isEditable());
    }

    @Override
    public void onTaskListItemClicked(long taskId, boolean editable) {
        if (editable) {
            editTask(taskId);
        } else {
            showComments(taskId);
        }
    }

    private void editTask(long taskId) {
        Intent intent = new Intent(this, TaskEditActivity.class);
        intent.putExtra(TaskEditFragment.TOKEN_ID, taskId);
        getIntent().putExtra(TaskEditFragment.TOKEN_ID, taskId); // Needs to be in activity intent so that TEA onResume doesn't create a blank activity
        if (getIntent().hasExtra(TaskListFragment.TOKEN_FILTER))
            intent.putExtra(TaskListFragment.TOKEN_FILTER, getIntent().getParcelableExtra(TaskListFragment.TOKEN_FILTER));

        if (fragmentLayout != LAYOUT_SINGLE) {
            TaskEditFragment editActivity = getTaskEditFragment();
            findViewById(R.id.taskedit_fragment_container).setVisibility(View.VISIBLE);

            if(editActivity == null) {
                editActivity = new TaskEditFragment();
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.add(R.id.taskedit_fragment_container, editActivity, TaskEditFragment.TAG_TASKEDIT_FRAGMENT);
                transaction.addToBackStack(null);
                transaction.commit();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // Force the transaction to occur so that we can be guaranteed of the fragment existing if we try to present it
                        getSupportFragmentManager().executePendingTransactions();
                    }
                });
            } else {
                editActivity.save(true);
                editActivity.repopulateFromScratch(intent);
            }

            TaskListFragment tlf = getTaskListFragment();
            if (tlf != null)
                tlf.loadTaskListContent(true);

        } else {
            startActivityForResult(intent, TaskListFragment.ACTIVITY_EDIT_TASK);
            AndroidUtilities.callOverridePendingTransition(this, R.anim.slide_left_in, R.anim.slide_left_out);
        }
    }

    private void showComments(long taskId) {
        Intent intent = new Intent(this, CommentsActivity.class);
        intent.putExtra(TaskCommentsFragment.EXTRA_TASK, taskId);
        startActivity(intent);
        AndroidUtilities.callOverridePendingTransition(this, R.anim.slide_left_in, R.anim.slide_left_out);
    }

    @Override
    public void onBackPressed() {
        if (isFinishing())
            return;
        super.onBackPressed();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_RESTART_ACTIVITY) {
            finish();
            startActivity(getIntent());
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    // --- fragment helpers

    protected void removeFragment(String tag) {
        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentByTag(tag);
        if(fragment != null) {
            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.remove(fragment);
            ft.commit();
        }
    }

    protected Fragment setupFragment(String tag, int container, Class<? extends Fragment> cls, boolean createImmediate, boolean replace) {
        final FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentByTag(tag);
        if(fragment == null || replace) {
            Fragment oldFragment = fragment;
            try {
                fragment = cls.newInstance();
            } catch (InstantiationException e) {
                return null;
            } catch (IllegalAccessException e) {
                return null;
            }

            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            if (container == 0) {
                if (oldFragment != null && replace)
                    ft.remove(oldFragment);
                ft.add(fragment, tag);
            }
            else
                ft.replace(container, fragment, tag);
            ft.commit();
            if (createImmediate)
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        fm.executePendingTransactions();
                    }
                });
        }
        return fragment;
    }

    // Voice recognizer callbacks
    @Override
    public void onSpeechResult(String result) {
        TaskListFragment tlf = getTaskListFragment();
        if (tlf != null) {
            EditText box = tlf.quickAddBar.getQuickAddBox();
            if (box != null)
                box.setText(result);
        }

    }

    @Override
    public void onSpeechError(int error) {
        TaskListFragment tlf = getTaskListFragment();
        if (tlf != null) {
            QuickAddBar quickAdd = tlf.quickAddBar;
            if (quickAdd != null) {
                VoiceRecognizer vr = quickAdd.getVoiceRecognizer();
                if (vr != null)
                    vr.cancel();
            }
        }

        int errorStr = 0;
        switch(error) {
        case SpeechRecognizer.ERROR_NETWORK:
        case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
            errorStr = R.string.speech_err_network;
            break;
        case SpeechRecognizer.ERROR_NO_MATCH:
            Toast.makeText(this, R.string.speech_err_no_match, Toast.LENGTH_LONG).show();
            break;
        default:
            errorStr = R.string.speech_err_default;
            break;
        }

        if (errorStr > 0)
            DialogUtilities.okDialog(this, getString(errorStr), null);
    }

    /**
     * @return LAYOUT_SINGLE, LAYOUT_DOUBLE, or LAYOUT_TRIPLE
     */
    public int getFragmentLayout() {
        return fragmentLayout;
    }

    private class RepeatConfirmationReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, final Intent intent) {
            long taskId = intent.getLongExtra(
                    AstridApiConstants.EXTRAS_TASK_ID, 0);

            if (taskId > 0) {
                long oldDueDate = intent.getLongExtra(
                        AstridApiConstants.EXTRAS_OLD_DUE_DATE, 0);
                long newDueDate = intent.getLongExtra(
                        AstridApiConstants.EXTRAS_NEW_DUE_DATE, 0);
                Task task = PluginServices.getTaskService().fetchById(taskId,
                        DateChangedAlerts.REPEAT_RESCHEDULED_PROPERTIES);

                try {
                    boolean lastTime = AstridApiConstants.BROADCAST_EVENT_TASK_REPEAT_FINISHED.equals(intent.getAction());
                    DateChangedAlerts.showRepeatTaskRescheduledDialog(
                            AstridActivity.this, task, oldDueDate, newDueDate, lastTime);

                } catch (BadTokenException e) { // Activity not running when tried to show dialog--rebroadcast
                    new Thread() {
                        @Override
                        public void run() {
                            sendBroadcast(intent);
                        }
                    }.start();
                }
            }
        }
    }

}
