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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ListView;

import com.commonsware.cwac.tlv.TouchListView.DropListener;
import com.commonsware.cwac.tlv.TouchListView.GrabberClickListener;
import com.commonsware.cwac.tlv.TouchListView.SwipeListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.subtasks.OrderedMetadataListUpdater.Node;
import com.todoroo.astrid.subtasks.OrderedMetadataListUpdater.OrderedListNodeVisitor;
import com.todoroo.astrid.ui.DraggableListView;
import com.todoroo.astrid.utility.AstridPreferences;

public class OrderedMetadataListFragmentHelper<LIST> implements OrderedListFragmentHelperInterface<LIST> {

    private final DisplayMetrics metrics = new DisplayMetrics();
    private final OrderedMetadataListUpdater<LIST> updater;
    private final TaskListFragment fragment;

    @Autowired TaskService taskService;
    @Autowired MetadataService metadataService;

    private DraggableTaskAdapter taskAdapter;

    private LIST list;

    public OrderedMetadataListFragmentHelper(TaskListFragment fragment, OrderedMetadataListUpdater<LIST> updater) {
        DependencyInjectionService.getInstance().inject(this);
        this.fragment = fragment;
        this.updater = updater;
    }

    // --- ui component setup

    private Activity getActivity() {
        return fragment.getActivity();
    }

    private ListView getListView() {
        return fragment.getListView();
    }

    private Filter getFilter() {
        return fragment.getFilter();
    }

    public DraggableListView getTouchListView() {
        DraggableListView tlv = (DraggableListView) fragment.getListView();
        return tlv;
    }

    public void setUpUiComponents() {
        TypedValue tv = new TypedValue();
        getActivity().getTheme().resolveAttribute(R.attr.asThemeTextColor, tv, false);
        getTouchListView().setDragndropBackgroundColor(tv.data);
        getTouchListView().setDropListener(dropListener);
        getTouchListView().setClickListener(rowClickListener);
        getTouchListView().setSwipeListener(swipeListener);
        getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);

        if(Preferences.getInt(AstridPreferences.P_SUBTASKS_HELP, 0) == 0)
            showSubtasksHelp();
    }

    @SuppressWarnings("nls")
    private void showSubtasksHelp() {
        String body = String.format("<h3>%s</h3><img src='%s'>" +
                "<br>%s<br><br><br><img src='%s'><br>%s",
                getActivity().getString(R.string.subtasks_help_1),
                "subtasks_vertical.png",
                getActivity().getString(R.string.subtasks_help_2),
                "subtasks_horizontal.png",
                getActivity().getString(R.string.subtasks_help_3));

        String color = ThemeService.getDialogTextColorString();
        String html = String.format("<html><body style='text-align:center;color:%s'>%s</body></html>",
                color, body);

        DialogUtilities.htmlDialog(getActivity(), html, R.string.subtasks_help_title);
        Preferences.setInt(AstridPreferences.P_SUBTASKS_HELP, 1);
    }

    public void beforeSetUpTaskList(Filter filter) {
        updater.initialize(list, filter);
    }

    public Property<?>[] taskProperties() {
        Property<?>[] baseProperties = TaskAdapter.PROPERTIES;
        if (Preferences.getIntegerFromString(R.string.p_taskRowStyle_v2, 0) == 2)
            baseProperties = TaskAdapter.BASIC_PROPERTIES;

        ArrayList<Property<?>> properties = new ArrayList<Property<?>>(Arrays.asList(baseProperties));
        properties.add(updater.indentProperty());
        properties.add(updater.orderProperty());
        return properties.toArray(new Property<?>[properties.size()]);
    }


    private final DropListener dropListener = new DropListener() {
        @Override
        public void drop(int from, int to) {
            long targetTaskId = taskAdapter.getItemId(from);
            if (targetTaskId <= 0) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
            long destinationTaskId = taskAdapter.getItemId(to);

            try {
                if(to >= getListView().getCount())
                    updater.moveTo(getFilter(), list, targetTaskId, -1);
                else
                    updater.moveTo(getFilter(), list, targetTaskId, destinationTaskId);
            } catch (Exception e) {
                Log.e("drag", "Drag Error", e); //$NON-NLS-1$ //$NON-NLS-2$
            }

            fragment.loadTaskListContent(true);
        }
    };

    private final SwipeListener swipeListener = new SwipeListener() {
        @Override
        public void swipeRight(int which) {
            indent(which, 1);
        }

        @Override
        public void swipeLeft(int which) {
            indent(which, -1);
        }

        protected void indent(int which, int delta) {
            long targetTaskId = taskAdapter.getItemId(which);
            if (targetTaskId <= 0) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
            try {
                updater.indent(getFilter(), list, targetTaskId, delta);
            } catch (Exception e) {
                Log.e("drag", "Indent Error", e); //$NON-NLS-1$ //$NON-NLS-2$
            }
            fragment.loadTaskListContent(true);
        }
    };

    private final GrabberClickListener rowClickListener = new GrabberClickListener() {
        @Override
        public void onLongClick(final View v) {
            if(v == null)
                return;

            fragment.registerForContextMenu(getListView());
            getListView().showContextMenuForChild(v);
            fragment.unregisterForContextMenu(getListView());
        }

        @Override
        public void onClick(View v) {
            if(v == null)
                return;
            ((DraggableTaskAdapter) taskAdapter).getListener().onClick(v);
        }
    };

    public TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor,
            AtomicReference<String> sqlQueryTemplate) {

        taskAdapter = new DraggableTaskAdapter(fragment, TaskListFragment.getTaskRowResource(),
                cursor, sqlQueryTemplate, false, null);

        taskAdapter.addOnCompletedTaskListener(new OnCompletedTaskListener() {
            @Override
            public void onCompletedTask(Task item, boolean newState) {
                setCompletedForItemAndSubtasks(item, newState);
            }
        });

        return taskAdapter;
    }

    private final class DraggableTaskAdapter extends TaskAdapter {

        private DraggableTaskAdapter(TaskListFragment activity, int resource,
                Cursor c, AtomicReference<String> query, boolean autoRequery,
                OnCompletedTaskListener onCompletedTaskListener) {
            super(activity, resource, c, query, autoRequery,
                    onCompletedTaskListener);

            applyListeners = APPLY_LISTENERS_NONE;
        }

        @Override
        protected ViewHolder getTagFromCheckBox(View v) {
            return (ViewHolder)((View)v.getParent()).getTag();
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            View view = super.newView(context, cursor, parent);
            view.getLayoutParams().height = Math.round(45 * metrics.density);

            ViewHolder vh = (ViewHolder) view.getTag();

            MarginLayoutParams rowParams = (MarginLayoutParams) vh.rowBody.getLayoutParams();
            rowParams.topMargin = rowParams.bottomMargin = 0;

            ViewGroup.LayoutParams pictureParams = vh.picture.getLayoutParams();
            pictureParams.width = pictureParams.height = Math.round(38 * metrics.density);

            pictureParams = vh.pictureBorder.getLayoutParams();
            pictureParams.width = pictureParams.height = Math.round(38 * metrics.density);

            return view;
        }

        @Override
        public synchronized void setFieldContentsAndVisibility(View view) {
            super.setFieldContentsAndVisibility(view);

            ViewHolder vh = (ViewHolder) view.getTag();
            int indent = vh.task.getValue(updater.indentProperty());
            vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
        }

        @Override
        protected void addListeners(View container) {
            super.addListeners(container);
        }

        public TaskRowListener getListener() {
            return listener;
        }
    }

    private final Map<Long, ArrayList<Long>> chainedCompletions =
        Collections.synchronizedMap(new HashMap<Long, ArrayList<Long>>());

    private void setCompletedForItemAndSubtasks(final Task item, final boolean completedState) {
        final long itemId = item.getId();

        final Task model = new Task();
        final long completionDate = completedState ? DateUtilities.now() : 0;

        if(completedState == false) {
            ArrayList<Long> chained = chainedCompletions.get(itemId);
            if(chained != null) {
                for(Long taskId : chained) {
                    model.setId(taskId);
                    model.setValue(Task.COMPLETION_DATE, completionDate);
                    taskService.save(model);
                    model.clear();

                    taskAdapter.getCompletedItems().put(taskId, false);
                }
                taskAdapter.notifyDataSetInvalidated();
            }
            return;
        }

        final ArrayList<Long> chained = new ArrayList<Long>();
        final int parentIndent = item.getValue(updater.indentProperty());
        updater.applyToChildren(getFilter(), list, itemId, new OrderedListNodeVisitor() {
            @Override
            public void visitNode(Node node) {
                Task childTask = taskService.fetchById(node.taskId, Task.RECURRENCE);
                if(!TextUtils.isEmpty(childTask.getValue(Task.RECURRENCE))) {
                    Metadata metadata = updater.getTaskMetadata(list, node.taskId);
                    metadata.setValue(updater.indentProperty(), parentIndent);
                    metadataService.save(metadata);
                }

                model.setId(node.taskId);
                model.setValue(Task.COMPLETION_DATE, completionDate);
                taskService.save(model);
                model.clear();

                taskAdapter.getCompletedItems().put(node.taskId, true);
                chained.add(node.taskId);
            }
        });

        if(chained.size() > 0) {
            chainedCompletions.put(itemId, chained);
            taskAdapter.notifyDataSetInvalidated();
        }
    }

    public void setList(LIST list) {
        this.list = list;
    }

    @Override
    public void onCreateTask(Task task) {
        //
    }

    public void onDeleteTask(Task task) {
        updater.onDeleteTask(getFilter(), list, task.getId());
        taskAdapter.notifyDataSetInvalidated();
    }

}
