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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;

import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;

import com.timsu.astrid.R;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.utility.Flags;

/**
 * Control set to manage adding and removing tags
 *
 * @author Tim Su <tim@todoroo.com>
 *
 */
public final class TagsControlSet extends PopupControlSet {

    // --- instance variables

    private static final String TRANSITORY_TAGS = "tags";//$NON-NLS-1$

    private final TagService tagService = TagService.getInstance();
    private ArrayList<String> allTagNames;

    private LinearLayout newTags;
    private ListView selectedTags;
    private boolean populated = false;
    private HashMap<String, Integer> tagIndices;

    //private final LinearLayout tagsContainer;
    private final TextView tagsDisplay;

    public TagsControlSet(Activity activity, int viewLayout, int displayViewLayout, int title) {
        super(activity, viewLayout, displayViewLayout, title);
        DependencyInjectionService.getInstance().inject(this);
        tagsDisplay = (TextView) getDisplayView().findViewById(R.id.display_row_edit);
        this.displayText.setText(activity.getString(R.string.TEA_tags_label));
    }

    private Tag[] getTagArray() {
        ArrayList<Tag> tagsList = TagService.getInstance().getTagList();
        return tagsList.toArray(new Tag[tagsList.size()]);
    }

    private HashMap<String, Integer> buildTagIndices(ArrayList<String> tagNames) {
        HashMap<String, Integer> indices = new HashMap<String, Integer>();
        for (int i = 0; i < tagNames.size(); i++) {
            indices.put(tagNames.get(i), i);
        }
        return indices;
    }

    private ArrayList<String> getTagNames(Tag[] tags) {
        ArrayList<String> names = new ArrayList<String>();
        for (int i = 0; i < tags.length; i++) {
            names.add(tags[i].toString());
        }
        return names;
    }

    private String buildTagString() {
        StringBuilder builder = new StringBuilder();

        LinkedHashSet<String> tags = getTagSet();
        for (String tag : tags) {
            if (builder.length() != 0)
                builder.append(", "); //$NON-NLS-1$
            builder.append(tag);
        }

        if (builder.length() == 0)
            builder.append(activity.getString(R.string.TEA_tags_none));
        return builder.toString();
    }


    private void setTagSelected(String tag) {
        Integer index = tagIndices.get(tag);
        if (index != null) {
            selectedTags.setItemChecked(index, true);
        } else {
            allTagNames.add(tag);
            tagIndices.put(tag, allTagNames.size() - 1);
            ((ArrayAdapter<String>)selectedTags.getAdapter()).notifyDataSetChanged();
        }
    }

    private LinkedHashSet<String> getTagSet() {
        LinkedHashSet<String> tags = new LinkedHashSet<String>();
        if (initialized) {
            for(int i = 0; i < selectedTags.getAdapter().getCount(); i++) {
                if (selectedTags.isItemChecked(i))
                    tags.add(allTagNames.get(i));
            }

            for(int i = 0; i < newTags.getChildCount(); i++) {
                TextView tagName = (TextView) newTags.getChildAt(i).findViewById(R.id.text1);
                if(tagName.getText().length() == 0)
                    continue;

                tags.add(tagName.getText().toString());
            }
        } else {
            if (model.getTransitory(TRANSITORY_TAGS) != null) {
                return (LinkedHashSet<String>) model.getTransitory(TRANSITORY_TAGS);
            }
        }
        return tags;
    }

    /** Adds a tag to the tag field */
    boolean addTag(String tagName, boolean reuse) {
        LayoutInflater inflater = activity.getLayoutInflater();

        // check if already exists
        TextView lastText = null;
        for(int i = 0; i < newTags.getChildCount(); i++) {
            View view = newTags.getChildAt(i);
            lastText = (TextView) view.findViewById(R.id.text1);
            if(lastText.getText().equals(tagName))
                return false;
        }

        final View tagItem;
        if(reuse && lastText != null && lastText.getText().length() == 0) {
            tagItem = (View) lastText.getParent();
        } else {
            tagItem = inflater.inflate(R.layout.tag_edit_row, null);
            newTags.addView(tagItem);
        }
        if(tagName == null)
            tagName = ""; //$NON-NLS-1$

        final AutoCompleteTextView textView = (AutoCompleteTextView)tagItem.
            findViewById(R.id.text1);
        textView.setText(tagName);

        textView.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable s) {
                //
            }
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
                //
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before,
                    int count) {
                if(count > 0 && newTags.getChildAt(newTags.getChildCount()-1) ==
                        tagItem)
                    addTag("", false); //$NON-NLS-1$
            }
        });

        textView.setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) {
                if(actionId != EditorInfo.IME_NULL)
                    return false;
                if(getLastTextView().getText().length() != 0) {
                    addTag("", false); //$NON-NLS-1$
                }
                return true;
            }
        });

        ImageButton reminderRemoveButton;
        reminderRemoveButton = (ImageButton)tagItem.findViewById(R.id.button1);
        reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                TextView lastView = getLastTextView();
                if(lastView == textView && textView.getText().length() == 0)
                    return;

                if(newTags.getChildCount() > 1)
                    newTags.removeView(tagItem);
                else
                    textView.setText(""); //$NON-NLS-1$
            }
        });

        return true;
    }

    /**
     * Get tags container last text view. might be null
     * @return
     */
    private TextView getLastTextView() {
        if(newTags.getChildCount() == 0)
            return null;
        View lastItem = newTags.getChildAt(newTags.getChildCount()-1);
        TextView lastText = (TextView) lastItem.findViewById(R.id.text1);
        return lastText;
    }

    @Override
    public void readFromTask(Task task) {
        super.readFromTask(task);
        if(model.getId() != AbstractModel.NO_ID) {
            TodorooCursor<Metadata> cursor = tagService.getTags(model.getId(), false);
            LinkedHashSet<String> tags = new LinkedHashSet<String>(cursor.getCount());
            try {
                for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
                    String tag = cursor.get(TagService.TAG);
                    tags.add(tag);
                }
            } finally {
                cursor.close();
            }
            model.putTransitory(TRANSITORY_TAGS, tags);
            refreshDisplayView();
        }
    }

    @Override
    protected void readFromTaskOnInitialize() {
        newTags.removeAllViews();

        for (int i = 0; i < selectedTags.getCount(); i++) { // clear all selected items
            selectedTags.setItemChecked(i, false);
        }
        if(model.getId() != AbstractModel.NO_ID) {
            selectTagsFromModel();
        }
        addTag("", false); //$NON-NLS-1$
        refreshDisplayView();
        populated = true;
    }

    private void selectTagsFromModel() {
        LinkedHashSet<String> tags = (LinkedHashSet<String>) model.getTransitory(TRANSITORY_TAGS);
        if (tags != null) {
            for (String tag : tags) {
                setTagSelected(tag);
            }
        }
    }

    @Override
    protected void afterInflate() {
        Tag[] allTags = getTagArray();
        allTagNames = getTagNames(allTags);
        tagIndices = buildTagIndices(allTagNames);

        selectedTags = (ListView) getView().findViewById(R.id.existingTags);
        selectedTags.setAdapter(new ArrayAdapter<String>(activity,
                R.layout.simple_list_item_multiple_choice_themed, allTagNames));
        selectedTags.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

        this.newTags = (LinearLayout) getView().findViewById(R.id.newTags);
    }

    @Override
    protected String writeToModelAfterInitialized(Task task) {
        // this is a case where we're asked to save but the UI was not yet populated
        if(!populated)
            return null;

        LinkedHashSet<String> tags = getTagSet();

        if(TagService.getInstance().synchronizeTags(task.getId(), tags)) {
            Flags.set(Flags.TAGS_CHANGED);
            task.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
        }

        return null;
    }

    @Override
    protected void refreshDisplayView() {
        tagsDisplay.setText(buildTagString());
    }

    public boolean hasLists() {
        LinkedHashSet<String> tags = getTagSet();
        return !tags.isEmpty();
    }

}
