/FileExtraction/Java_unseen/labexp_osmtracker-android/app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java
package net.osmtracker.util;

import java.io.IOException;
import java.util.HashMap;

import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.activity.TrackLogger;
import net.osmtracker.layout.DisablableTableLayout;
import net.osmtracker.layout.UserDefinedLayout;
import net.osmtracker.listener.PageButtonOnClickListener;
import net.osmtracker.listener.StillImageOnClickListener;
import net.osmtracker.listener.TagButtonOnClickListener;
import net.osmtracker.listener.TextNoteOnClickListener;
import net.osmtracker.listener.VoiceRecOnClickListener;
import net.osmtracker.service.resources.IconResolver;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;

/**
 * Reads an user defined layout, using a pull parser,
 * and instantiate corresponding objects (Layouts, Buttons)
 * 
 * @author Nicolas Guillaumin
 * 
 */
public class UserDefinedLayoutReader {

	@SuppressWarnings("unused")
	private static final String TAG = UserDefinedLayoutReader.class.getSimpleName();

	/**
	 * Map containing parsed layouts
	 */
	private HashMap<String, ViewGroup> layouts = new HashMap<String, ViewGroup>();

	/**
	 * Source parser
	 */
	private XmlPullParser parser;

	/**
	 * Context for accessing resources
	 */
	private Context context;

	/**
	 * The user defined Layout
	 */
	private UserDefinedLayout userDefinedLayout;
	
	/**
	 * {@link IconResolver} to retrieve button icons.
	 */
	private IconResolver iconResolver;

	/**
	 * Listener bound to text note buttons
	 */
	private TextNoteOnClickListener textNoteOnClickListener;
	
	/**
	 * Listener bound to voice record buttons
	 */
	private VoiceRecOnClickListener voiceRecordOnClickListener;
	
	/**
	 * Lister bound to picture buttons
	 */
	private StillImageOnClickListener stillImageOnClickListener;
	
	/**
	 * {@link Resources} to retrieve String resources
	 */
	private Resources resources;
	
	/** 
	 * representing ScreenOrientation
	 * see {@link Configuration.orientation}
	 */
	private int orientation;
	
	private static final int ICON_POS_AUTO = 0;
	private static final int ICON_POS_TOP = 1;
	private static final int ICON_POS_RIGHT = 2;
	private static final int ICON_POS_BOTTOM = 3;
	private static final int ICON_POS_LEFT = 4;
	
	/**
	 * the icon position for the current layout
	 */
	private int currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_AUTO;

	/**
	 * Current track id
	 */
	private long currentTrackId;
	
	/**
	 * Constructor
	 * 
	 * @param udl
	 *				User defined layout
	 * @param c
	 *				Context for accessing resources
	 * @param tl
	 *				TrackLogger activity
	 * @param trackId
	 * 			  Current track id
	 * @param input
	 *				Parser for reading layout
	 * @param ir
	 * 			  Icon resolver to use to fetch icons 
	 */
	public UserDefinedLayoutReader(UserDefinedLayout udl, Context c, TrackLogger tl, long trackId, XmlPullParser input, IconResolver ir) {
		parser = input;
		context = c;
		resources = context.getResources();
		userDefinedLayout = udl;
		iconResolver = ir;
		currentTrackId = trackId;
		orientation = resources.getConfiguration().orientation;
		
		// Initialize listeners which will be bound to buttons
		textNoteOnClickListener = new TextNoteOnClickListener(tl);
		voiceRecordOnClickListener = new VoiceRecOnClickListener(tl);
		stillImageOnClickListener = new StillImageOnClickListener(tl);
	}

	/**
	 * Parses an XML layout
	 * 
	 * @return An HashMap of {@link ViewGroup} with layout name as key.
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	public HashMap<String, ViewGroup> parseLayout() throws XmlPullParserException, IOException {
		int eventType = parser.getEventType();
		while (eventType != XmlPullParser.END_DOCUMENT) {
			switch (eventType) {
			case XmlPullParser.START_TAG:
				String tagName = parser.getName();
				if (XmlSchema.TAG_LAYOUT.equals(tagName)) {
					// <layout> tag has been encountered. Inflate this layout
					inflateLayout();
				}
				break;
			case XmlPullParser.END_TAG:
				break;
			}
			eventType = parser.next();
			
		}

		return layouts;
	}

	/**
	 * Inflates a <layout> into a {@link TableLayout}
	 * 
	 * @throws IOException
	 * @throws XmlPullParserException
	 */
	private void inflateLayout() throws IOException, XmlPullParserException {
		String layoutName = parser.getAttributeValue(null, XmlSchema.ATTR_NAME);
		String layoutIconPosValue = parser.getAttributeValue(null, XmlSchema.ATTR_ICONPOS);

		// find out the correct icon position for this layout
		if(XmlSchema.ATTR_VAL_ICONPOS_TOP.equals(layoutIconPosValue)){
			// TOP position
			this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_TOP;
		} else if (XmlSchema.ATTR_VAL_ICONPOS_RIGHT.equals(layoutIconPosValue)){
			// RIGHT position
			this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_RIGHT;
		} else if (XmlSchema.ATTR_VAL_ICONPOS_BOTTOM.equals(layoutIconPosValue)){
			// BOTTOM position
			this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_BOTTOM;
		} else if (XmlSchema.ATTR_VAL_ICONPOS_LEFT.equals(layoutIconPosValue)){
			// LEFT position
			this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_LEFT;
		} else {
			// if no or an undefined value is given for the current layout
			// AUTO position depending on screen orientation
			this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_AUTO;
		}

		// Create a new table layout and set default parameters
		DisablableTableLayout tblLayout = new DisablableTableLayout(context);
		tblLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
				LinearLayout.LayoutParams.FILL_PARENT, 1));

		String currentTagName = null;
		while (!XmlSchema.TAG_LAYOUT.equals(currentTagName)) {
			int eventType = parser.next();
			switch (eventType) {
			case XmlPullParser.START_TAG:
				String name = parser.getName();
				if (XmlSchema.TAG_ROW.equals(name)) {
					// <row> tag has been encountered, inflates it
					inflateRow(tblLayout);
				}
				break;
			case XmlPullParser.END_TAG:
				currentTagName = parser.getName();
				break;
			}
		}

		// Add the new inflated layout to the list
		layouts.put(layoutName, tblLayout);
	}

	/**
	 * Inflates a <row> into a {@link TableRow}
	 * 
	 * @param layout
	 *				{@link TableLayout} to rattach the row to
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	private void inflateRow(TableLayout layout) throws XmlPullParserException, IOException {
		TableRow tblRow = new TableRow(layout.getContext());
		tblRow.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.FILL_PARENT,
				TableLayout.LayoutParams.FILL_PARENT, 1));

		String currentTagName = null;
		// int eventType = parser.next();
		while (!XmlSchema.TAG_ROW.equals(currentTagName)) {
			int eventType = parser.next();
			switch (eventType) {
			case XmlPullParser.START_TAG:
				String name = parser.getName();
				if (XmlSchema.TAG_BUTTON.equals(name)) {
					// <button> tag has been encountered, inflates it.
					inflateButton(tblRow);
				}
				break;
			case XmlPullParser.END_TAG:
				currentTagName = parser.getName();
				break;
			}

		}

		// Add the inflated table row to the current layout
		layout.addView(tblRow);
	}

	/**
	 * Inflates a <button>
	 * 
	 * @param row
	 *				The table row to attach the button to
	 */
	public void inflateButton(TableRow row) {
		Button button = new Button(row.getContext());
		button.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.FILL_PARENT,
				TableRow.LayoutParams.FILL_PARENT, 1));

		// TODO Use kind of ButtonFactory here

		String buttonType = parser.getAttributeValue(null, XmlSchema.ATTR_TYPE);
		Drawable buttonIcon = null;
		if (XmlSchema.ATTR_VAL_PAGE.equals(buttonType)) {
			// Page button
			button.setText(findLabel(parser.getAttributeValue(null, XmlSchema.ATTR_LABEL), resources));				
			buttonIcon = iconResolver.getIcon(parser.getAttributeValue(null, XmlSchema.ATTR_ICON));
			button.setOnClickListener(new PageButtonOnClickListener(userDefinedLayout, parser.getAttributeValue(null,
					XmlSchema.ATTR_TARGETLAYOUT)));
		} else if (XmlSchema.ATTR_VAL_TAG.equals(buttonType)) {
			// Standard tag button
			button.setText(findLabel(parser.getAttributeValue(null, XmlSchema.ATTR_LABEL), resources));			
			buttonIcon = iconResolver.getIcon(parser.getAttributeValue(null, XmlSchema.ATTR_ICON));
			button.setOnClickListener(new TagButtonOnClickListener(currentTrackId));
		} else if (XmlSchema.ATTR_VAL_VOICEREC.equals(buttonType)) {
			// Voice record button
			button.setText(resources.getString(R.string.gpsstatus_record_voicerec));
			buttonIcon = resources.getDrawable(R.drawable.voice_32x32);
			button.setOnClickListener(voiceRecordOnClickListener);
		} else if (XmlSchema.ATTR_VAL_TEXTNOTE.equals(buttonType)) {
			// Text note button
			button.setText(resources.getString(R.string.gpsstatus_record_textnote));
			buttonIcon = resources.getDrawable(R.drawable.text_32x32);
			button.setOnClickListener(textNoteOnClickListener);
		} else if (XmlSchema.ATTR_VAL_PICTURE.equals(buttonType)) {
			// Picture button
			button.setText(resources.getString(R.string.gpsstatus_record_stillimage));
			buttonIcon = resources.getDrawable(R.drawable.camera_32x32);
			button.setOnClickListener(stillImageOnClickListener);
		}
		
		// Where to draw the button's icon (depending on the current layout)
		switch(this.currentLayoutIconPos){
		case UserDefinedLayoutReader.ICON_POS_TOP:
			// TOP position
			button.setCompoundDrawablesWithIntrinsicBounds(null, buttonIcon, null, null);
			break;
		case UserDefinedLayoutReader.ICON_POS_RIGHT:
			// RIGHT position
			button.setCompoundDrawablesWithIntrinsicBounds(null, null, buttonIcon, null);
			break;
		case UserDefinedLayoutReader.ICON_POS_BOTTOM:
			// BOTTOM position
			button.setCompoundDrawablesWithIntrinsicBounds(null, null, null, buttonIcon);
			break;
		case UserDefinedLayoutReader.ICON_POS_LEFT:
			// LEFT position
			button.setCompoundDrawablesWithIntrinsicBounds(buttonIcon, null, null, null);
			break;
		case UserDefinedLayoutReader.ICON_POS_AUTO:
		default:
			// if no or an undefined value is given for the current layout
			// AUTO position depending on screen orientation
			if(orientation == Configuration.ORIENTATION_LANDSCAPE){
				// in landscape mode draw icon to the LEFT
				button.setCompoundDrawablesWithIntrinsicBounds(buttonIcon, null, null,null);
			}else{
				// in portrait mode draw icon to the TOP
				button.setCompoundDrawablesWithIntrinsicBounds(null, buttonIcon, null, null);
			}
			break;
		}


		row.addView(button);
	}
	
	/**
	 * Finds a label if it's a reference to an internal resource (@string/label) 
	 * @param text Resource reference or plain label
	 * @param r {@link Resources} to lookup from
	 * @return Plain label, or corresponding text extracted from {@link Resources}
	 */
	private String findLabel(String text, Resources r) {
		if (text != null) {
			if (text.startsWith("@")) {
				// Check if it's a resource identifier
				int resId = resources.getIdentifier(text.replace("@", ""), null, OSMTracker.PACKAGE_NAME);
				if (resId != 0) {
					return resources.getString(resId);
				}
			}
		}
		return text;
	}

	/**
	 * XML Schema
	 */
	private static final class XmlSchema {
		public static final String TAG_LAYOUT = "layout";
		public static final String TAG_ROW = "row";
		public static final String TAG_BUTTON = "button";

		public static final String ATTR_NAME = "name";
		public static final String ATTR_TYPE = "type";
		public static final String ATTR_LABEL = "label";
		public static final String ATTR_TARGETLAYOUT = "targetlayout";
		public static final String ATTR_ICON = "icon";
		public static final String ATTR_ICONPOS = "iconpos";

		public static final String ATTR_VAL_TAG = "tag";
		public static final String ATTR_VAL_PAGE = "page";
		public static final String ATTR_VAL_VOICEREC = "voicerec";
		public static final String ATTR_VAL_TEXTNOTE = "textnote";
		public static final String ATTR_VAL_PICTURE = "picture";
		
		public static final String ATTR_VAL_ICONPOS_TOP = "top";
		public static final String ATTR_VAL_ICONPOS_RIGHT = "right";
		public static final String ATTR_VAL_ICONPOS_BOTTOM = "bottom";
		public static final String ATTR_VAL_ICONPOS_LEFT = "left";
	}

}
