package javax.microedition.lcdui;

import java.util.ArrayList;

/**
 * 
 * @author Andre Nijholt
 */
public class Form extends Screen implements CommandListener {
	private static final Command EDIT_COMMAND = new Command(1, Command.SCREEN, 0);
	private ItemStateListener itemStateListener;
	
	private ArrayList items = new ArrayList();
	private int curItemIdx = 0;
	private boolean selectedItem;
	private int height;
	private int width;
	
	private TextBox editBox;
	
	public Form(String title) {
		this.title = title;
		editBox = new TextBox(null, null, 0, TextField.ANY);
		editBox.setCommandListener(this);
		editBox.addCommand(EDIT_COMMAND);
	}
	
	public Form(String title, Item[] items) {
		this.title = title;
		for (int i = 0; (items != null) && (i < items.length); i++) {
			this.items.add(items[i]);
		}
	}
	
	public int append(Image img) {
		items.add(img);
		return (items.size() - 1);
	}

	public int append(Item item) {
		items.add(item);
		return (items.size() - 1);
	}

	public int append(String str)  {
		items.add(str);
		return (items.size() - 1);
	}
	
	public void delete(int itemNum) {
		items.remove(itemNum);
	}
	
	public void deleteAll() {
		items.clear();
	}
	
	public Object get(int itemNum) {
		return items.get(itemNum);
	}
	
	public void set(int itemNum, Item item) {
		items.set(itemNum, item);
	}
	
	public void insert(int itemNum, Item item) {
		items.add(itemNum, item);
	}
	
	public int getHeight() {
		return height;
	}
	
	public int getWidth() {
		return width;
	}
		
	public int size() {
		return items.size();
	}

	public void setItemStateListener(ItemStateListener i) {
		this.itemStateListener = i;
	}

	protected void callItemStateListener() {
		Object o = items.get(curItemIdx);
		if ((itemStateListener != null) && (o instanceof Item)) {
			itemStateListener.itemStateChanged((Item) o);
		}
	}

	protected void keyPressed(int keyCode) {
		if (selectedItem && curItemIdx >= 0) {
			if ((keyCode == Screen.KEY_RIGHT) 
					|| (keyCode == Screen.KEY_LEFT) 
					|| (keyCode == Screen.KEY_ENTER)) {
				// Update currently selected item
				((Item) items.get(curItemIdx)).keyPressed(keyCode);
			} else if (keyCode == Screen.KEY_BACK) {				
				Object o = items.get(curItemIdx);
				if (o instanceof TextField) {
					// Update currently selected TextField until keyboard enter pressed
					editBox.keyPressed(keyCode);
				} else {
					// Notify item changed and leave current selection
					callItemStateListener();
					selectedItem = false;
				}
			}				
		} else {
			// Select new item
			if (keyCode == Screen.KEY_RIGHT) {
				for (int i = curItemIdx + 1; i != curItemIdx; i++) {
					// Wrap when last item checked
					if (i >= items.size()) {
						i = 0;
					}
					
					Object o = items.get(i);
					if ((o instanceof Item) && (((Item) o).isInteractive())) {
						curItemIdx = i;
						break;
					}
				}
			} else if (keyCode == Screen.KEY_LEFT) {
				for (int i = curItemIdx - 1; i != curItemIdx; i--) {
					// Wrap when first item checked
					if (i < 0) {
						i = (items.size() - 1);
					}
					
					Object o = items.get(i);
					if ((o instanceof Item) && (((Item) o).isInteractive())) {
						curItemIdx = i;
						break;
					}
				}
			} else if (keyCode == Screen.KEY_BACK) {
				for (int i = 0; i < commands.size(); i++) {
					callCommandListener();
				}
			} else if (keyCode == Screen.KEY_ENTER) {
				Object o = items.get(curItemIdx);
				if (o instanceof TextField) {
					// Show text box for editing
					editBox.setTitle(((TextField) o).getLabel());
					editBox.setText(((TextField) o).getText());
					editBox.setMaxSize(((TextField) o).getMaxSize());
					editBox.setConstraints(((TextField) o).getConstraints());
					Display.getDisplay().setCurrent(editBox);
				} else {
					// Set current selection
					selectedItem = true;
				}
			}
		}
		repaint();
	}

	public void commandAction(Command c, Displayable d) {
		if ((c == EDIT_COMMAND) && (d == editBox)) {
			// Update textfield and return to form display
			TextField tf = (TextField) items.get(curItemIdx);
			tf.setText(editBox.getText());
			callItemStateListener();
			Display.getDisplay().setCurrent(this);
		}
	}

	public void paint(Graphics g) {
		int curX = 0;
		int curY = 0;
		int curWidth;
		int curHeight;

		ChoiceGroup activePopup = null;
		int popupX = 0;
		int popupY = 0;

		// Draw title on entire line
		if (title != null) {
			g.drawString(title, 0, 0);
			curY += Display.CHAR_HEIGHT;
		}
		
		// Draw all items
		for (int i = 0; i < items.size(); i++) {
			// Calculate position and size for current item
			curWidth = getItemWidth(i);
			curHeight = getItemHeight(i);
			if (((curX + curWidth) < Display.SCREEN_WIDTH)
					&& ((curX + curWidth + getItemWidth(i + 1)) > Display.SCREEN_WIDTH)) {
				// Next item doesn't fit on current line: allow entire line for current item
				curWidth = Display.SCREEN_WIDTH - curX;
			}

			// Draw current item
			Object o = items.get(i);
			if (o instanceof Image) {
				g.drawImage((Image) o, curX, curY, false);
			} else if (o instanceof String) {
				g.drawString(((String) o), curX, curY);
			} else if (o instanceof Item) {
				((Item) o).paint(g, curX, curY, curWidth, curHeight, (i == curItemIdx));
				
				if (selectedItem && (i == curItemIdx) && (o instanceof ChoiceGroup)
						&& (((ChoiceGroup) o).choiceType == Choice.POPUP)) {
					// Draw popup window again after all items drawn
					activePopup = (ChoiceGroup) o;
					popupX = curX;
					popupY = curY;
				}
			}
			
			if ((curX + curWidth) < Display.SCREEN_WIDTH) {
				// Draw next item on current line
				curX += curWidth;
				if (curHeight > Display.CHAR_HEIGHT) {
					curY += (curHeight - Display.CHAR_HEIGHT);
				}
			} else {
				// Start new line and draw item
				curX = 0;
				curY += curHeight;
			}
		}
		
		// Draw popup menu above currently drawn items
		if (activePopup != null) {
			int popupHeight = ((activePopup.label != null) ? Display.CHAR_HEIGHT : 0)
				+ (activePopup.size() * Display.CHAR_HEIGHT);
			if ((popupY + popupHeight) > Display.SCREEN_HEIGHT) {
				popupHeight = Display.SCREEN_HEIGHT - popupY;
			}
			activePopup.paint(g, popupX, popupY, activePopup.getMinimumWidth(), 
					popupHeight, true);
		}
	}
	
	private int getItemWidth(int itemIdx) {
		if (itemIdx >= items.size()) {
			return 0;
		}
		
		Object o = items.get(itemIdx);
		if (o instanceof Image) {
			return ((Image) o).getWidth();
		} else if (o instanceof String) {
			return ((String) o).length() * Display.CHAR_WIDTH;
		} else if (o instanceof Item) {
			return Math.max(((Item) o).getMinimumWidth(), ((Item) o).getPreferredWidth());
		}

		return 0;
	}

	private int getItemHeight(int itemIdx) {
		if (itemIdx >= items.size()) {
			return 0;
		}
		
		Object o = items.get(itemIdx);
		int height = 0;
		if (o instanceof Image) {
			height = ((Image) o).getHeight();
		} else if (o instanceof String) {
			// Always single line string
			height = Display.CHAR_HEIGHT;
		} else if (o instanceof Item) {
			height = Math.max(((Item) o).getMinimumHeight(), ((Item) o).getPreferredHeight());
		}

		// Round to multiple of line height
		height = (((height + Display.CHAR_HEIGHT - 1) / Display.CHAR_HEIGHT)) * Display.CHAR_HEIGHT;
		return height;
	}
}
