/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.menu;

import java.io.IOException;

import org.mozilla.gecko.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.support.annotation.XmlRes;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.InflateException;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;

public class GeckoMenuInflater extends MenuInflater {
    private static final String TAG_MENU = "menu";
    private static final String TAG_ITEM = "item";
    private static final int NO_ID = 0;

    private final Context mContext;

    // Private class to hold the parsed menu item.
    private static final class ParsedItem {
        public int id;
        public int order;
        public CharSequence title;
        public int iconRes;
        public boolean checkable;
        public boolean checked;
        public boolean visible;
        public boolean enabled;
        public int showAsAction;
        public boolean hasSubMenu;
        public int itemType;
    }

    public GeckoMenuInflater(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public void inflate(@XmlRes int menuRes, Menu menu) {

        // This does not check for a well-formed XML.

        XmlResourceParser parser = null;
        try {
            parser = mContext.getResources().getXml(menuRes);
            AttributeSet attrs = Xml.asAttributeSet(parser);

            parseMenu(parser, attrs, menu);

        } catch (XmlPullParserException | IOException e) {
            throw new InflateException("Error inflating menu XML", e);
        } finally {
            if (parser != null)
                parser.close();
        }
    }

    private void parseMenu(XmlResourceParser parser, AttributeSet attrs, Menu menu)
                           throws XmlPullParserException, IOException {
        ParsedItem item = null;

        String tag;
        int eventType = parser.getEventType();

        do {
            tag = parser.getName();

            switch (eventType) {
                case XmlPullParser.START_TAG:
                    if (tag.equals(TAG_ITEM)) {
                        // Parse the menu item.
                        item = new ParsedItem();
                        parseItem(item, attrs);
                     } else if (tag.equals(TAG_MENU)) {
                        if (item != null) {
                            // Add the submenu item.
                            SubMenu subMenu = menu.addSubMenu(NO_ID, item.id, item.order, item.title);
                            item.hasSubMenu = true;

                            // Set the menu item in main menu.
                            MenuItem menuItem = subMenu.getItem();
                            setValues(item, menuItem);

                            // Start parsing the sub menu.
                            parseMenu(parser, attrs, subMenu);
                        }
                    }
                    break;

                case XmlPullParser.END_TAG:
                    if (parser.getName().equals(TAG_ITEM)) {
                        if (!item.hasSubMenu) {
                            // Add the item.
                            MenuItem menuItem = menu.add(NO_ID, item.id, item.order, item.title);
                            setValues(item, menuItem);
                        }
                    } else if (tag.equals(TAG_MENU)) {
                        return;
                    }
                    break;
            }

            eventType = parser.next();

        } while (eventType != XmlPullParser.END_DOCUMENT);
    }

    public void parseItem(ParsedItem item, AttributeSet attrs) {
        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuItem);

        item.id = a.getResourceId(R.styleable.MenuItem_android_id, NO_ID);
        item.order = a.getInt(R.styleable.MenuItem_android_orderInCategory, 0);
        item.title = a.getText(R.styleable.MenuItem_android_title);
        item.checkable = a.getBoolean(R.styleable.MenuItem_android_checkable, false);
        item.checked = a.getBoolean(R.styleable.MenuItem_android_checked, false);
        item.visible = a.getBoolean(R.styleable.MenuItem_android_visible, true);
        item.enabled = a.getBoolean(R.styleable.MenuItem_android_enabled, true);
        item.hasSubMenu = false;
        item.iconRes = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
        item.showAsAction = a.getInt(R.styleable.MenuItem_android_showAsAction, 0);
        item.itemType = a.getInt(R.styleable.MenuItem_itemType, GeckoMenuItem.ITEM_TYPE_DEFAULT);

        a.recycle();
    }

    public void setValues(ParsedItem item, MenuItem menuItem) {
        // We are blocking any presenter updates during inflation.
        GeckoMenuItem geckoItem = null;
        if (menuItem instanceof GeckoMenuItem) {
            geckoItem = (GeckoMenuItem) menuItem;
        }

        if (geckoItem != null) {
            geckoItem.stopDispatchingChanges();
            geckoItem.setItemType(item.itemType);
        }

        menuItem.setChecked(item.checked)
                .setVisible(item.visible)
                .setEnabled(item.enabled)
                .setCheckable(item.checkable)
                .setIcon(item.iconRes);

        menuItem.setShowAsAction(item.showAsAction);

        if (geckoItem != null) {
            // We don't need to allow presenter updates during inflation,
            // so we use the weak form of re-enabling changes.
            geckoItem.resumeDispatchingChanges();
        }
    }
}
