Please analyze this code for possible issues and fix them as well as optimize it. package views.components;

import broadcasting.properties.Broadcast;
import broadcasting.properties.Listeners;
import display.buttons.Button;
import display.buttons.ButtonText;
import display.buttons.StyleButton;
import display.checkboxes.Checkbox;
import display.dialogs.KstyleDialog;
import display.dropdowns.StyleDropdown;
import display.scrollbars.Scrollbar;
import display.scrolling.ScrollerDob;
import display.SpriteX;
import display.text_entry.StyleTextinput;
import display.text_entry.Textinput;
import localization.F;
import localization.FString;
import model_view.SpriteView;
import object_cleanup.Autocleaner;
import openfl.events.MouseEvent;
import openfl.Vector;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.Quad;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
import starling.utils.Max;
import texture_assets.Txman;

// This class acts as a dropdown that can expand to include other dropdowns
class MenuBar extends SpriteView implements Broadcast
{
	//region Broadcast Implementation
	public var listeners(get, never):Listeners;
	private var _listeners:Listeners;

	private function get_listeners():Listeners
	{
		if(_listeners == null)
		{
			_listeners = new Listeners(this);
		}
		return _listeners;
	}

	private var propNext(get, never):Int;

	private function get_propNext():Int { return listeners.propNext; }
	//endregion

	static public inline var EXPAND_DOWN = "down";
	static public inline var EXPAND_RIGHT = "right";

	private var _menuBtn:ButtonText;
	private var _spDropdownContainer:SpriteX;
	private var _spDropdown:SpriteX;
	private var _rgItems:Vector<DisplayObject>; // A list of all items added in the dropdown
	private var _stExpandType:String;
	private var _spBG:SpriteX;
	private var _quadMask:Quad;
	private var _colorDropdown:Int;
	private var _mapValues:Map<DisplayObject, Dynamic>;
	private var _mapClickFunctions:Map<DisplayObject, Dynamic -> Void>;
	private var _mapOverFunctions:Map<DisplayObject, Dynamic -> Void>;
	public var onClose:Void -> Void;
	private var _onExpand:Void->Void;
	private var _closeDependency:MenuBar;
	private var _style:StyleDropdown;
	private var _maxHeight:Float;
	private var _contentHeight:Float = 0.0;
	private var _fRoot:Bool;
	private var _fDropdown:Bool; // Allow the menu items clicked to be selected at top
	private var _rgDobToTop:Vector<DisplayObject>; // All of the dependents that need to be brought to the top of their display lists when this menu is expanded
	public var _scrollbar:Scrollbar;
	private var _btnScrollUp:Quad;
	private var _btnScrollDown:Quad;
	private var _fSearchable:Bool;
	private var _touchingItem:DisplayObject;

	public var onToggled:Bool -> Void; // called when the menu is opened or closed

	public var textinputSearch(default, null):Textinput;

	public var menuBtn(get, never):ButtonText;

	private function get_menuBtn():ButtonText { return _menuBtn; }

	public var fDropdownShown(get, never):Bool;

	private function get_fDropdownShown():Bool { return _spDropdownContainer.parent != null; }

	public var menu_width(get, never):Float;

	private function get_menu_width():Float { return _menuBtn.width; }

	public var menu_height(get, never):Float;

	private function get_menu_height():Float { return _menuBtn.height; }

	public var dropdown_height(get, never):Float;

	private function get_dropdown_height():Float
	{
		if(_spDropdown.numChildren <= 0)
			return 0.0;
		var dob = _spDropdown.getChildAt(_spDropdown.numChildren - 1);
		var dobHeight = dob.height;
		if(Std.is(dob, MenuBar))
			dobHeight = cast(dob, MenuBar).menu_height;

		return dob.y + dobHeight;
	}

	public var title(get, set):String;

	private function get_title():String { return _menuBtn.st.toString(); }

	private function set_title(st:String):String
	{
		_menuBtn.st = F.S(st);
		return st;
	}

	public var valueSelected(get, set):Dynamic;

	private function set_valueSelected(value:Dynamic)
	{
		if(value == null)
		{
			title = "";
			_valueSelected = null;
			return _valueSelected;
		}

		// Look for items with matching value and set to matching title
		for(item in _rgItems)
		{
			if(Std.is(item, ButtonText) && _mapValues.get(item) == value)
			{
				title = cast(item, ButtonText).st.toString();
				_valueSelected = value;
				return _valueSelected;
			}
		}

		return _valueSelected;
	}

	private var valueSelected_(never, set):Dynamic;
	public var prop_valueSelected(get, never):Int;
	private var _valueSelected:Dynamic;

	private function get_valueSelected():Dynamic
	{ return _valueSelected; }

	private var _prop_valueSelected:Int = 0;

	private function get_prop_valueSelected():Int { if(_prop_valueSelected == 0) { _prop_valueSelected = propNext; } return _prop_valueSelected; }
	private function set_valueSelected_(value:Dynamic):Dynamic
	{
		// Look for items with matching value and set to matching title
		for(item in _rgItems)
		{
			if(Std.is(item, ButtonText) && _mapValues.get(item) == value)
			{
				title = cast(item, ButtonText).st.toString();
				break;
			}
		}

		var valueOld:Dynamic = valueSelected;
		_valueSelected = value;
		listeners.fireChange(prop_valueSelected, valueOld, value);

		return _valueSelected;
	}

	public var values(get, null):Map<DisplayObject, Dynamic>;
	private function get_values():Map<DisplayObject, Dynamic> { return _mapValues; }

	override public function get_height():Float { return menu_height; }

	public function new(autocleaner:Autocleaner,
						txman:Txman,
						stText:FString,
						styleDropdown:StyleDropdown,
						stExpandType:String = EXPAND_DOWN,
						menuWidth:Float = -1,
						maxHeight:Float = 1000,
						colorDropdown:Int = 0x0/*TODO: remove this unused param*/,
						fRootMenu:Bool = true,
						fSearchable:Bool = false,
						onExpand:Void->Void = null)
	{
		super(autocleaner);

		_stExpandType = stExpandType;
		_style = styleDropdown;
		_colorDropdown = _style.colorListBackground;
		_maxHeight = maxHeight;
		_fRoot = fRootMenu;
		_fSearchable = fSearchable;
		_onExpand = onExpand;

		_rgItems = new Vector<DisplayObject>();

		_mapValues = new Map<DisplayObject, Dynamic>();
		_mapClickFunctions = new Map<DisplayObject, Dynamic -> Void>();
		_mapOverFunctions = new Map<DisplayObject, Dynamic -> Void>();

		var fExpandRight = (_stExpandType == EXPAND_RIGHT);

		_menuBtn = mkButtonText(_style.styleButton, stText, menuWidth != -1 ? menuWidth : _style.styleButton.widthDefault, 75 - 1, fExpandRight ? null : onTouchMenu);
		var btnW = _menuBtn.height;
		addChild(_menuBtn);

		_spDropdownContainer = new SpriteX(autocleaner, txman);
		addChild(_spDropdownContainer);

		_spBG = new SpriteX(autocleaner, txman);
		_spDropdownContainer.addChild(_spBG);
		//_spBG.addEventListener(TouchEvent.TOUCH, onTouchBG);

		_quadMask = mkQuad(1, 1, 0x0);
		_spDropdownContainer.addChild(_quadMask);

		_spDropdown = new SpriteX(autocleaner, txman);
		if(fExpandRight)
			_spDropdownContainer.x = _menuBtn.width;
		else
			_spDropdownContainer.y = _menuBtn.height;
		_spDropdownContainer.addChild(_spDropdown);

		_scrollbar = new Scrollbar(this, txman, KstyleDialog.Standard.styleScrollbar, true, _maxHeight);
		var scroller = new ScrollerDob(this, true, 300, _maxHeight);
		scroller.setScrollbar(_scrollbar);
		_spDropdownContainer.addChild(_scrollbar);
		subscribe(_scrollbar, _scrollbar.prop_uScroll, onScroll);

		name = "menuBar" + stText.toString() + Std.int(Math.random() * Max.INT_MAX_VALUE);

		showDropdown(false);

		addEventListener(Event.ADDED_TO_STAGE, onAdded);
	}

	private function onAdded(e:Event):Void
	{
		removeEventListener(Event.ADDED_TO_STAGE, onAdded);
		Starling.current.nativeStage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
		if(_fRoot)
			stage.addEventListener(TouchEvent.TOUCH, onTouchStage);

		addEventListener(Event.ADDED_TO_STAGE, onReAdded); // Always have the dropdown closed on addition to stage
	}

	private function onReAdded(e:Event):Void
	{
		showDropdown(false, false);
	}

	private function onMouseWheel(e:MouseEvent):Void
	{
		if(_scrollbar.visible)
			_scrollbar.set_uScroll(_scrollbar.uScroll - (e.delta * 50), true);
	}

	private function onScroll():Void
	{
		_spDropdown.y = -(_spDropdown.height - _maxHeight) * _scrollbar.pctScroll;
	}

	private function onTouchMenu(btn:Button):Void
	{
		showDropdown(_spDropdownContainer.parent == null);
	}

	public function allowSelectionAsDropdown(fDropdown:Bool = true):Void
	{
		_fDropdown = fDropdown;
	}

	private function shouldShowDropdown():Bool
	{
		return (_contentHeight > _maxHeight);
	}

	public function showDropdown(fShow:Bool = true, doDependency:Bool = true):Void
	{
		if(textinputSearch != null)
			textinputSearch.fFocused = false;

		if(fShow)
		{
			// close sibling menu bars
			var currParent:DisplayObject = this.parent;
			while(currParent != null)
			{
				if(Std.is(currParent, MenuBar))
				{
					cast(currParent, MenuBar).closeAll();
					break;
				}
				currParent = currParent.parent;
			}
		}

		if(fShow && _spDropdownContainer.parent == null)
		{
			if(onToggled != null)
				onToggled(fShow);
			addChild(_spDropdownContainer);
		}
		else if(!fShow && _spDropdownContainer.parent != null)
		{
			if(onToggled != null)
				onToggled(fShow);
			_spDropdownContainer.removeFromParent();
		}
		else
			return; // no state change

		if(!fShow)
			_scrollbar.visible = false;
		else
		{
			if(shouldShowDropdown())
				_scrollbar.visible = true;
			bringDependentsToTop();
		}

		if(doDependency && fShow == false && _closeDependency != null)
			_closeDependency.showDropdown(fShow);

		if(!fShow && onClose != null)
			onClose();

		if(_onExpand != null)
			_onExpand();
	}

	public function closeAll():Void
	{
		for(iChild in 0..._spDropdown.numChildren)
		{
			var dob = _spDropdown.getChildAt(iChild);
			if(Std.is(dob, MenuBar))
				cast(dob, MenuBar).showDropdown(false, false);
		}
	}

	public function createItem(stText:FString, value:Dynamic = null, onClick:Dynamic -> Void = null, onOver:Dynamic -> Void = null, fExpander:Bool = false, widthButton:Int = -1, fRedraw:Bool = true, fSearchable:Bool = false):DisplayObject
	{
		var dob:DisplayObject;

		if(fSearchable) // Overwrite expand value if this is searchable
			fExpander = true;

		if(fExpander)
		{
			dob = new MenuBar(autocleaner, txman, stText, _style, MenuBar.EXPAND_RIGHT, widthButton != -1 ? widthButton : _style.styleValue.widthDefault, _maxHeight, _colorDropdown, false, fSearchable);
			dob.addEventListener(TouchEvent.TOUCH, onTouchExpander);
			var menuBar = cast(dob, MenuBar);
			menuBar.setCloseDependency(this);
			menuBar.menuBtn.mask = _quadMask;
			_contentHeight += cast(dob, MenuBar).menu_height;
		}
		else
		{
			if(_fSearchable && textinputSearch == null)
				createSearchBar(KstyleDialog.Standard.styleTextinput.clone().styleText(KstyleDialog.Standard.styleTextinput.styleText.clone().size(40).finish()).finish());

			dob = mkButtonText(_style.styleValue, stText, widthButton != -1 ? widthButton : _style.styleValue.widthDefault, -1, this.onClick);
			dob.addEventListener(TouchEvent.TOUCH, onTouchButton);
			dob.name = stText.toString();
			dob.mask = _quadMask;
			_contentHeight += dob.height;
		}

		var lastItemAdded:DisplayObject = _spDropdown.numChildren > 0 ? _spDropdown.getChildAt(_spDropdown.numChildren - 1) : null;
		if(lastItemAdded != null)
		{
			dob.x = lastItemAdded.x;
			var lastItemAddedHeight = Std.is(lastItemAdded, MenuBar) ? cast(lastItemAdded, MenuBar).menu_height : lastItemAdded.height;
			dob.y = lastItemAdded.y + lastItemAddedHeight;
		}

		if(onClick != null)
			_mapClickFunctions.set(dob, onClick);
		if(onOver != null)
		{
			_mapOverFunctions.set(dob, onOver);
			dob.addEventListener(TouchEvent.TOUCH, onTouchItem);
		}

		_mapValues.set(dob, value);
		_spDropdown.addChild(dob);
		_rgItems.push(dob);

		if(fRedraw)
			redraw();

		return dob;
	}

	public function addItem(dob:DisplayObject, value:Dynamic, onClick:Dynamic -> Void = null, onOver:Dynamic -> Void = null):Void
	{
		if(_fSearchable && textinputSearch == null)
			createSearchBar(KstyleDialog.Standard.styleTextinput.clone().styleText(KstyleDialog.Standard.styleTextinput.styleText.clone().size(40).finish()).finish());

		var lastItemAdded:DisplayObject = _spDropdown.numChildren > 0 ? _spDropdown.getChildAt(_spDropdown.numChildren - 1) : null;
		if(lastItemAdded != null)
		{
			dob.x = lastItemAdded.x;
			var lastItemAddedHeight = Std.is(lastItemAdded, MenuBar) ? cast(lastItemAdded, MenuBar).menu_height : lastItemAdded.height;
			dob.y = lastItemAdded.y + lastItemAddedHeight;
		}

		_contentHeight += dob.height;

		if(onClick != null)
			_mapClickFunctions.set(dob, onClick);
		if(onOver != null)
			_mapOverFunctions.set(dob, onOver);

		if(onClick != null || onOver != null)
			dob.addEventListener(TouchEvent.TOUCH, onTouchItem);

		_mapValues.set(dob, value);
		_spDropdown.addChild(dob);
		_rgItems.push(dob);

		redraw();
	}

	public function createSearchBar(styleInput:StyleTextinput):Void
	{
		if(textinputSearch != null)
			return;

		textinputSearch = new Textinput(autocleaner, txman, styleInput, FString.Empty, false, _style.styleValue.widthDefault);
		textinputSearch.mask = _quadMask;
		_contentHeight += textinputSearch.height;

		// move all existing content below
		for(iChild in 0..._spDropdown.numChildren)
			_spDropdown.getChildAt(iChild).y += textinputSearch.height;

		_spDropdown.addChildAt(textinputSearch, 0);
		redraw();
		subscribe(textinputSearch, textinputSearch.prop_stInput, onSearchInput);
	}

	private function onSearchInput():Void
	{
		if(textinputSearch == null)
			return;

		alphabetize(textinputSearch.stInput);
	}

	public function redraw():Void
	{
		drawBG();
		onScroll();
		_scrollbar.duContent = _contentHeight;

		if(shouldShowDropdown())
		{
			_scrollbar.visible = true;
			_scrollbar.setRequiresRedraw();
		}
		else
			_scrollbar.visible = false;
	}

	public function removeItem(value:Dynamic, fDispose:Bool = false):Bool
	{
		var dob:DisplayObject = null;
		var fFound = false;
		var removedContentHeight = 0.0;
		// Remove from view
		for(iChild in 0..._spDropdown.numChildren)
		{
			if(fFound) // Item has been found already
			{
				dob.y -= removedContentHeight; // shift item up to fill removed item
				continue;
			}

			dob = _spDropdown.getChildAt(iChild);
			if(_mapValues.get(dob) == value)
			{
				fFound = true;
				dob.removeFromParent(fDispose);

				if(Std.is(dob, MenuBar))
					removedContentHeight = cast(dob, MenuBar).menu_height
				else
					removedContentHeight = dob.height;
				_contentHeight -= removedContentHeight;
			}
		}

		// Remove from our internal list of items (not all items may be displayed)
		var dob:DisplayObject = null;
		for(iItem in 0..._rgItems.length)
		{
			dob = _rgItems[iItem];
			if(_mapValues.get(dob) == value)
			{
				_rgItems.removeAt(iItem);
				_mapValues.remove(dob);
				redraw();
				return true;
			}
		}

		return false;
	}

	public function removeAllItems(fDispose:Bool = false):Void
	{
		_contentHeight = 0;
		_rgItems = new Vector<DisplayObject>();
		_spDropdown.removeChildren(0, -1, fDispose);
		textinputSearch = null;
		_mapValues = new Map<DisplayObject, Dynamic>();
		redraw();
	}

	public function alphabetize(stSearchInput:String = null/*Only list the menu items with text labels that match*/):Void
	{
		if(stSearchInput != null)
			stSearchInput = stSearchInput.toLowerCase(); // make search non-case sensitive

		_rgItems.sort(compareDobTitles); // alphabetize our internal list of items

		var iChild = _spDropdown.numChildren - 1;
		while(iChild >= 0)
		{
			if(!Std.is(_spDropdown.getChildAt(iChild), Textinput)) // Do not remove the textinput searchbar
				_spDropdown.removeChildAt(iChild);
			--iChild;
		}

		var lastItemAdded:DisplayObject = null;
		if(textinputSearch != null)
			lastItemAdded = textinputSearch; // Begin y at textinput search bar

		var maxY = 0.0;
		for(dob in _rgItems)
		{
			var dobTitle = getDobTitle(dob);
			if(stSearchInput != null && stSearchInput != "" && dobTitle != null && dobTitle.toLowerCase().indexOf(stSearchInput) == -1) // Search for input
				continue; // not found, do not list this item

			dob.y = 0;
			_spDropdown.addChild(dob);

			if(lastItemAdded != null)
			{
				var lastItemAddedHeight = Std.is(lastItemAdded, MenuBar) ? cast(lastItemAdded, MenuBar).menu_height : lastItemAdded.height;
				dob.y = maxY + lastItemAddedHeight;
				maxY = dob.y;
			}

			lastItemAdded = dob;
		}

		_contentHeight = _spDropdown.height;

		redraw();
	}

	private function drawBG():Void
	{
		_spBG.removeChildren(0, -1, true);
		if(_contentHeight > 0)
		{
			_spBG.addChild(mkQuad(_style.styleValue.widthDefault + (shouldShowDropdown() ? _scrollbar.width : 0), _contentHeight < _maxHeight ? _contentHeight : _maxHeight, _colorDropdown));
			_quadMask.width = _spBG.width;
			_quadMask.height = _spBG.height;
		}
		_scrollbar.x = _spBG.x + _style.styleValue.widthDefault;
	}

	public function getValueFrom(dob:DisplayObject):Dynamic
	{
		if(_mapValues.exists(dob))
			return _mapValues.get(dob);
		return null;
	}

	private function onClick(button:Button):Void
	{
		if(_fDropdown)
			valueSelected_ = getValueFrom(button);

		showDropdown(false);
		if(_mapClickFunctions.exists(button))
		{
			if(_mapValues.exists(button))
				_mapClickFunctions.get(button)(_mapValues.get(button));
			else
				_mapClickFunctions.get(button)(null);
		}
	}

	private function onTouchExpander(e:TouchEvent):Void
	{
		//if(!Std.is(e.currentTarget, MenuBar))
		//return;

		var menuBar = cast(e.currentTarget, MenuBar);
		var touch:Touch = e.getTouch(menuBar);

		if(touch != null)
		{
			if(touch.phase == TouchPhase.HOVER && !menuBar.fDropdownShown)
				menuBar.showDropdown(true);
		}
	}

	private function onTouchBG(e:TouchEvent):Void
	{
/*		var touch:Touch = e.getTouch(_spBG);

		if(touch != null)
			showDropdown();
		else
			showDropdown(false, false);*/
	}

	private function onTouchItem(e:TouchEvent):Void
	{
		var target = e.currentTarget;

		if(!Std.is(target, DisplayObject))
			return;

		var dob = cast(target, DisplayObject);
		var touch = e.getTouch(dob);
		if(touch != null)
		{
			var val:Dynamic;

			if(Std.is(target, Checkbox))
				val = cast(target, Checkbox).fChecked;
			else
				val = _mapValues.get(dob);

			if(touch.phase == TouchPhase.HOVER && _touchingItem != dob && _mapOverFunctions.exists(dob))
				_mapOverFunctions.get(dob)(val);

			_touchingItem = dob;
		}
		else
			_touchingItem = null;
	}

	// Touch handling for non-expander items
	private function onTouchButton(e:TouchEvent):Void
	{
		var target = e.currentTarget;

		if(!Std.is(target, DisplayObject))
			return;

		var dob = cast(target, DisplayObject);
		var touch = e.getTouch(dob);
		if(touch != null && touch.phase == TouchPhase.HOVER)
		{
			closeAll();
		}
	}

	private function onTouchStage(e:TouchEvent):Void
	{
		var touch = e.getTouch(stage);

		if(touch != null)
		{
			if(touch.phase == TouchPhase.ENDED)
			{
				if((Std.is(e.target, DisplayObject) && isInMenuBarDisplayList(cast(e.target, DisplayObject), this.name)) || (Std.is(e.target, Button) && Std.is(cast(e.target, Button).parent, Scrollbar)))
					return;
				showDropdown(false);
			}
		}
	}

	private function setCloseDependency(menuBar:MenuBar):Void
	{
		_closeDependency = menuBar;
	}

	private function isInMenuBarDisplayList(dob:DisplayObject, stMenuBarName:String):Bool
	{
		if(dob.parent != null)
		{
			if(dob.parent.name != null && dob.parent.name == this.name)
				return true;
			return isInMenuBarDisplayList(dob.parent, stMenuBarName);
		}
		return false;
	}

	public function setToTopOnOpen(dob:DisplayObject):Void
	{
		if(_rgDobToTop == null)
			_rgDobToTop = new Vector<DisplayObject>();
		_rgDobToTop.push(dob);
	}

	private function bringDependentsToTop():Void
	{
		if(_rgDobToTop == null)
			return;

		for(dob in _rgDobToTop)
			if(dob.parent != null)
				dob.parent.setChildIndex(dob, dob.parent.numChildren - 1);
	}

	public function isOpen():Bool
	{
		if(_spDropdownContainer.parent != null)
			return true;

		var dob:DisplayObject = null;
		for(iChild in 0..._spDropdown.numChildren)
		{
			dob = _spDropdown.getChildAt(iChild);
			if(Std.is(dob, MenuBar) && cast(dob, MenuBar).isOpen())
				return true;
		}

		return false;
	}

	private function getDobTitle(dob:DisplayObject):String
	{
		if(Std.is(dob, ButtonText))
		{
			var btn = cast(dob, ButtonText);
			try
			{
				return btn != null && btn.st != null ? btn.st.toString() : btn.name;
			} catch(e:Any)
			{
				return btn.name;
			}
		}
		else if(Std.is(dob, Textinput))
			return title;
		else if(Std.is(dob, MenuBar))
			return cast(dob, MenuBar).title;
		return null;
	}

	private function compareDobTitles(dobA:DisplayObject, dobB:DisplayObject):Int
	{
		return compareStrings(getDobTitle(dobA), getDobTitle(dobB));
	}

	private function compareStrings(a:String, b:String):Int
	{
		a = a.toLowerCase();
		b = b.toLowerCase();
		if(a < b) return -1;
		if(a > b) return 1;
		return 0;
	}

	public function fHasValue(value:Dynamic):Bool
	{
		var dob:DisplayObject = null;
		for(iChild in 0..._spDropdown.numChildren)
		{
			dob = _spDropdown.getChildAt(iChild);
			if(_mapValues.exists(dob) && _mapValues.get(dob) == value)
				return true;
		}
		return false; // no match found
	}

	override public function mkButtonText(style:StyleButton, st:FString, width:Float = -1.0, height:Float = -1.0, functionClick:Button -> Void = null):ButtonText
	{
		return new UIButton(autocleaner, style, st, width, height, functionClick);
	}

	override public function dispose()
	{
		onToggled = null;
		super.dispose();
	}
}