package filemanager;

import com.one.AbstractFont;
import com.one.ExtGraphics;
import com.one.vector.AuxMath;
import javax.microedition.lcdui.*;
import java.util.Vector;
import java.util.Enumeration;
import com.vmx.*;

public class ListMenu extends gkcCanvas implements Runnable, HotKeyListener
{
	public static final int CURSOR_MOVED = 0;
	public static final int CURSOR_CHANGED = 1;
	public static final int ELEMENT_ADDED = 2;
	public static final int ELEMENT_DELETED = 3;
	public static final int ELEMENT_CHANGED = 4;
	
	protected AbstractFont ttlfont, subttlfont, font;
	protected int width, height;
	
	protected String title, subtitle;
	protected int[] ttlpos = new int[8];
	protected int[] sttlpos = new int[8];
	protected int[] sbpos = new int[7];
	protected int header, footer, listpos;
	
	protected Vector elements;
	protected IntVector elempos;
	protected int scrstart, scrsel, scrlen;
	protected int count;
	protected int lineh;
	
	protected CommandMenu cmenu;
	
	protected ListStateListener statelistener;
	
	protected LineEffect lfx;
	protected Thread t;
	protected boolean isshown;

	protected int ofnw, ofnx, ofnstep, ofndelay;
	protected int OFN_DEF_DELAY;

	protected Image background, cursor;
	
	public ListMenu()
	{
		setFullScreenMode(true);
		
		width = getWidth();
		height = getHeight();
		
		elements = new Vector();
		elempos = new IntVector();
		count = 0;
		
		lfx = new LineEffect(1, 1, width - 2, height - 2);
		
		ttlfont = AbstractFont.getFont(AbstractFont.FACE_SYSTEM, AbstractFont.STYLE_BOLD, AbstractFont.SIZE_MEDIUM);
		subttlfont = AbstractFont.getFont(AbstractFont.FACE_SYSTEM, AbstractFont.STYLE_PLAIN, AbstractFont.SIZE_MEDIUM);
		font = AbstractFont.getFont(AbstractFont.FACE_SYSTEM, AbstractFont.STYLE_PLAIN, options.largeFontInList ? AbstractFont.SIZE_MEDIUM : AbstractFont.SIZE_SMALL);
		
		ttlpos[0] = 0;
		ttlpos[1] = 0;
		ttlpos[2] = width;
		ttlpos[3] = ttlfont.getHeight() + ttlfont.getHeight() / 4;
		ttlpos[4] = subttlfont.getHeight() / 2;
		ttlpos[5] = ttlpos[1] + (ttlpos[3] - ttlfont.getHeight()) / 2;
		ttlpos[6] = ttlpos[2] - ttlfont.getHeight();
		ttlpos[7] = ttlfont.getHeight();
		
		sttlpos[0] = 0;
		sttlpos[1] = ttlpos[1] + ttlpos[3] - 1;
		sttlpos[2] = width;
		sttlpos[3] = subttlfont.getHeight() + subttlfont.getHeight() / 4;
		sttlpos[4] = subttlfont.getHeight() / 2;
		sttlpos[5] = sttlpos[1] + (sttlpos[3] - subttlfont.getHeight()) / 2;
		sttlpos[6] = sttlpos[2] - subttlfont.getHeight();
		sttlpos[7] = subttlfont.getHeight();
		
		cmenu = new CommandMenu(getRenderer(), this, width, height, options.largeFontInMenu ? AbstractFont.SIZE_MEDIUM : AbstractFont.SIZE_SMALL, false, true, true, false, false);
		
		sbpos[2] = width;
		sbpos[3] = 7;
		sbpos[0] = 0;
		sbpos[1] = height - cmenu.getSoftHeight() - sbpos[3] + 1;
		sbpos[4] = sbpos[1] + 3;
		sbpos[5] = 0;
		sbpos[6] = width;
		
		header = sttlpos[1] + sttlpos[3];
		footer = cmenu.getSoftHeight() + sbpos[3] - 1;
		
		lineh = font.getHeight() + 2;
		scrlen = (height - header - footer) / lineh;
		listpos = header + (height - header - footer - lineh * scrlen) / 2;
		
		scrstart = 0;
		scrsel = 0;
		
		setTitle(null);
		setSubTitle(null, false);
	}

//	public void updateGradients()
//	{
//		Graphics g;
//
//		if(options.ditherGradients)
//		{
//			if(!options.alphaGradients || (images.isColorOpaque(ColorScheme.colors[ColorScheme.back1]) && images.isColorOpaque(ColorScheme.colors[ColorScheme.back2])))
//			{
//				background = Image.createImage(width, height);
//				g = background.getGraphics();
//				images.drawVGradient(g, ColorScheme.colors[ColorScheme.back1], ColorScheme.colors[ColorScheme.back2], 0, 0, width, height, true, false);
//			}
//			else
//			{
//				background = null;
//			}
//
//			if(!options.alphaGradients || (images.isColorOpaque(ColorScheme.colors[ColorScheme.selback1]) && images.isColorOpaque(ColorScheme.colors[ColorScheme.selback2])))
//			{
//				cursor = Image.createImage(width, lineh);
//				g = cursor.getGraphics();
//				images.drawHGradient(g, ColorScheme.colors[ColorScheme.selback1], ColorScheme.colors[ColorScheme.selback2], 0, 0, width, lineh, true, false);
//			}
//			else
//			{
//				cursor = null;
//			}
//		}
//		else
//		{
//			background = null;
//			cursor = null;
//		}
//	}
	
	public void addCommand(Command c)
	{
		cmenu.addCommand(c);
	}
	
	public void removeCommand(Command c)
	{
		cmenu.removeCommand(c);
	}
	
	public void removeAllCommands()
	{
		cmenu.removeAllCommands();
	}
	
	public void setCommandListener(CommandListener cl)
	{
		cmenu.setCommandListener(cl);
	}
	
	public void setStateListener(ListStateListener lsl)
	{
		statelistener = lsl;
	}
	
	public void setTitle(String ttl)
	{
		if(ttl == null)
		{
			ttl = "";
		}
		
		int ttlw = ttlfont.stringWidth(ttl);
		
		if(ttlw <= ttlpos[6])
		{
			title = ttl;
		}
		else
		{
			int i = ttl.length();
			
			do
			{
				title = ttl.substring(0, --i).trim() + "...";
				ttlw = ttlfont.stringWidth(title);
			}
			while(ttlw > ttlpos[6]);
		}

		repaintAll();
	}
	
	public void setSubTitle(String sttl, boolean mode)
	{
		if(sttl == null)
		{
			sttl = "";
		}
		
		int sttlw = subttlfont.stringWidth(sttl);
		
		if(sttlw <= sttlpos[6])
		{
			subtitle = sttl;
		}
		else
		{
			if(mode)
			{
				int i = 0;
				
				do
				{
					subtitle = "..." + sttl.substring(++i).trim();
					sttlw = subttlfont.stringWidth(subtitle);
				}
				while(sttlw > sttlpos[6]);
			}
			else
			{
				int i = sttl.length();
				
				do
				{
					subtitle = sttl.substring(0, --i).trim() + "...";
					sttlw = subttlfont.stringWidth(subtitle);
				}
				while(sttlw > sttlpos[6]);
			}
		}

		repaintAll();
	}

	protected int getElementPosition(String text)
	{
		int pos = (width - font.stringWidth(text)) / 2;

		if(pos >= 1)
		{
			return pos;
		}
		else
		{
			return 1;
		}
	}
	
	public void setString(String text, int index)
	{
		elements.setElementAt(text, index);
		elempos.set(index, getElementPosition(text));

		if(index == scrsel)
		{
			updateScroll();
		}
		
		if(statelistener != null)
		{
			statelistener.listStateChanged(this, ELEMENT_CHANGED);
		}

		repaintAll();
	}
	
	public void append(String element)
	{
		elements.addElement(element);
		elempos.add(getElementPosition(element));
		count = elements.size();
		
		sbpos[5] = width * scrstart / count;
		sbpos[6] = width * scrlen / count;

		if(count == 1)
		{
			updateScroll();
		}
		
		if(statelistener != null)
		{
			statelistener.listStateChanged(this, ELEMENT_ADDED);
		}

		repaintAll();
	}
	
	public void append(Enumeration enumeration)
	{
		if(enumeration == null)
		{
			return;
		}
		
		String s;
		
		while(enumeration.hasMoreElements())
		{
			s = (String)enumeration.nextElement();
			
			elements.addElement(s);
			elempos.add(getElementPosition(s));
		}
		
		count = elements.size();
		
		if(count > 0)
		{
			sbpos[5] = width * scrstart / count;
			sbpos[6] = width * scrlen / count;
		}
		else
		{
			sbpos[5] = 0;
			sbpos[6] = width;
		}

		if(count == 1)
		{
			updateScroll();
		}
		
		if(statelistener != null)
		{
			statelistener.listStateChanged(this, ELEMENT_ADDED);
		}

		repaintAll();
	}
	
	public void delete(int index)
	{
		elements.removeElementAt(index);
		elempos.remove(index, 1);
		
		count = elements.size();
		
		if(scrstart > count - 1)
		{
			scrstart = count - 1;
			
			if(scrstart < 0)
			{
				scrstart = 0;
			}
		}
		
		if(scrsel >= count)
		{
			scrsel = count - 1;
		}
		
		if(count > 0)
		{
			sbpos[5] = width * scrstart / count;
			sbpos[6] = width * scrlen / count;
		}
		else
		{
			sbpos[5] = 0;
			sbpos[6] = width;
		}

		updateScroll();
		
		if(statelistener != null)
		{
			statelistener.listStateChanged(this, ELEMENT_DELETED);
		}

		repaintAll();
	}
	
	public void deleteAll()
	{
		elements.removeAllElements();
		elempos.remove(0, elempos.size());
		
		count = 0;
		scrstart = 0;
		scrsel = 0;
		
		sbpos[5] = 0;
		sbpos[6] = width;

		updateScroll();
		
		if(statelistener != null)
		{
			statelistener.listStateChanged(this, ELEMENT_DELETED);
		}

		repaintAll();
	}
	
	public String getString(int index)
	{
		return (String)elements.elementAt(index);
	}
	
	public int getSelectedIndex()
	{
		return scrsel;
	}
	
	public void setSelectedIndex(int index)
	{
		if(index >= 0 && index < count)
		{
			scrsel = index;
			
			if(scrsel >= scrstart + scrlen)
			{
				do
				{
					scrstart++;
				}
				while(scrsel >= scrstart + scrlen);
			}
			else if(scrsel < scrstart)
			{
				do
				{
					scrstart--;
				}
				while(scrsel < scrstart);
			}
			
			if(count > 0)
			{
				sbpos[5] = width * scrstart / count;
				sbpos[6] = width * scrlen / count;
			}
			else
			{
				sbpos[5] = 0;
				sbpos[6] = width;
			}

			updateScroll();
			
			if(statelistener != null)
			{
				statelistener.listStateChanged(this, CURSOR_CHANGED);
			}

			repaintAll();
		}
	}
	
	public void select(String text)
	{
		int index = -1;
		
		for(int i = 0; i < count; i++)
		{
			if(((String)elements.elementAt(i)).equals(text))
			{
				index = i;
				break;
			}
		}
		
		if(index >= 0)
		{
			setSelectedIndex(index);
		}
	}

	protected void updateScroll()
	{
		ofnx = 1;
		ofndelay = OFN_DEF_DELAY;

		if(elements.size() > 0)
		{
			ofnw = font.stringWidth((String)elements.elementAt(scrsel));
		}
		else
		{
			ofnw = 0;
		}

		if(ofnw > width - 2)
		{
			ofnstep = -1;
		}
		else
		{
			ofnstep = 0;
		}
	}
	
	public int size()
	{
		return count;
	}
	
	public void showNotify()
	{
		if(t != null && t.isAlive())
		{
			try
			{
				t.join();
			}
			catch(Exception e)
			{
			}
		}
		
		isshown = true;

		//updateGradients();
		
		if(options.longScrollSpeed > 0)
		{
			(t = new Thread(this, "ListMenu/Animation")).start();
		}
	}
	
	public void hideNotify()
	{
		isshown = false;
	}
	
	public void directKeyPressed(int keyCode)
	{
	}
	
	public void hotKeyAction(int keyCode)
	{
	}
	
	public void keyPressed(int keyCode)
	{
		keyCode = remapKey(keyCode);

		if(cmenu.keyPressed(keyCode))
		{
			repaintAll();
			return;
		}
		
		if(count == 0)
		{
			return;
		}
		
		keyCode = translateKey(keyCode);
		
		if(keyCode == KEY_DOWN)
		{
			scrsel++;
			
			if(scrsel >= count)
			{
				scrsel = 0;
				scrstart = 0;
			}
			else if(scrsel >= scrstart + scrlen)
			{
				scrstart++;
			}
		}
		else if(keyCode == KEY_UP)
		{
			scrsel--;
			
			if(scrsel < 0)
			{
				scrsel = count - 1;
				scrstart = count - scrlen;
				
				if(scrstart < 0)
				{
					scrstart = 0;
				}
			}
			else if(scrsel < scrstart)
			{
				scrstart--;
			}
		}
		else if(keyCode == KEY_RIGHT)
		{
			if(scrsel == count - 1)
			{
				scrsel = 0;
				scrstart = 0;
			}
			else
			{
				scrsel += scrlen;
				
				if(scrsel > count - 1)
				{
					scrsel = count - 1;
				}
				
				while(scrsel >= scrstart + scrlen)
				{
					scrstart++;
				}
			}
		}
		else if(keyCode == KEY_LEFT)
		{
			if(scrsel == 0)
			{
				scrsel = count - 1;
				scrstart = count - scrlen;
				
				if(scrstart < 0)
				{
					scrstart = 0;
				}
			}
			else
			{
				scrsel -= scrlen;
				
				if(scrsel < 0)
				{
					scrsel = 0;
				}
				
				while(scrsel < scrstart)
				{
					scrstart--;
				}
			}
		}
		else
		{
			return;
		}
		
		if(count > 0)
		{
			sbpos[5] = width * scrstart / count;
			sbpos[6] = width * scrlen / count;
		}
		else
		{
			sbpos[5] = 0;
			sbpos[6] = width;
		}

		updateScroll();
		
		if(statelistener != null)
		{
			statelistener.listStateChanged(this, CURSOR_MOVED);
		}
		
		repaintAll();
	}
	
	public void keyReleased(int keyCode)
	{
		keyCode = remapKey(keyCode);

		if(cmenu.keyReleased(keyCode))
		{
			repaintAll();
		}
	}

	public void repaintAll()
	{
		if(isshown && options.longScrollSpeed <= 0)
		{
			repaint();
		}
	}
	
	public void paint(ExtGraphics g)
	{
		g.setClip(0, 0, width, height);
		
		//g.setColor(ColorScheme.colors[ColorScheme.back1]);
		//g.fillRect(0, 0, width, height);

		if(background != null)
		{
			g.drawImage(background, 0, 0, Graphics.LEFT | Graphics.TOP);
		}
		else
		{
			images.drawVGradient(g, ColorScheme.colors[ColorScheme.back1], ColorScheme.colors[ColorScheme.back2], 0, 0, width, height, options.ditherGradients);
		}

		if(!options.noEffects && options.longScrollSpeed > 0)
		{
			g.setClip(0, header, width, height - header - footer);

			g.setColor(ColorScheme.colors[ColorScheme.ltborder]);
			lfx.drawLines(g);

			g.setClip(0, 0, width, height);
		}

		//-----------------------------------------------------------------------------------------
		
		//g.setColor(ColorScheme.colors[ColorScheme.back1]);
		//g.fillRect(ttlpos[0], ttlpos[1], ttlpos[2], ttlpos[3]);
		
		g.setFont(ttlfont);
		
		g.setColor(ColorScheme.colors[ColorScheme.fore]);
		g.drawString(title, ttlpos[4], ttlpos[5], Graphics.LEFT | Graphics.TOP);
		
		g.setColor(ColorScheme.colors[ColorScheme.ltborder]);
		g.drawRect(ttlpos[0], ttlpos[1], ttlpos[2] - 1, ttlpos[3] - 1);

		//-----------------------------------------------------------------------------------------
		
		//g.setColor(ColorScheme.colors[ColorScheme.back1]);
		//g.fillRect(sttlpos[0], sttlpos[1], sttlpos[2], sttlpos[3]);
		
		g.setFont(subttlfont);
		
		g.setColor(ColorScheme.colors[ColorScheme.fore]);
		g.drawString(subtitle, sttlpos[4], sttlpos[5], Graphics.LEFT | Graphics.TOP);
		
		g.setColor(ColorScheme.colors[ColorScheme.ltborder]);
		g.drawRect(sttlpos[0], sttlpos[1], sttlpos[2] - 1, sttlpos[3] - 1);

		//-----------------------------------------------------------------------------------------
		
		//g.setColor(ColorScheme.colors[ColorScheme.back1]);
		//g.fillRect(sbpos[0], sbpos[1], sbpos[2], sbpos[3]);
		
		g.setColor(ColorScheme.colors[ColorScheme.ltborder]);
		g.drawLine(sbpos[0], sbpos[4], sbpos[0] + sbpos[2], sbpos[4]);
		
		g.setColor(ColorScheme.colors[ColorScheme.fore]);
		g.drawLine(sbpos[5], sbpos[4], sbpos[5] + sbpos[6], sbpos[4]);
		g.drawLine(sbpos[5] + 1, sbpos[4] - 1, sbpos[5] + sbpos[6] - 1, sbpos[4] - 1);
		g.drawLine(sbpos[5] + 1, sbpos[4] + 1, sbpos[5] + sbpos[6] - 1, sbpos[4] + 1);
		
		g.setColor(ColorScheme.colors[ColorScheme.ltborder]);
		g.drawRect(sbpos[0], sbpos[1], sbpos[2] - 1, sbpos[3] - 1);

		//-----------------------------------------------------------------------------------------
		
		g.setFont(font);
		
		String s;
		int x, y;
		int i, p;

		g.setClip(1, 0, width - 1, height);

		if(!options.noEffects && (ColorScheme.colors[ColorScheme.back1] & 0xFFFFFF) == (ColorScheme.colors[ColorScheme.back2] & 0xFFFFFF))
		{
			g.setColor(ColorScheme.colors[ColorScheme.back1]);

			for(i = scrstart, p = 0; i < count && i < scrstart + scrlen; i++, p++)
			{
				if(i != scrsel)
				{
					s = (String)elements.elementAt(i);
					x = elempos.get(i);
					y = listpos + p * lineh;

					LineEffect.drawTextOutline(g, s, x, y);
				}
			}
		}
		
		for(i = scrstart, p = 0; i < count && i < scrstart + scrlen; i++, p++)
		{
			s = (String)elements.elementAt(i);
			x = elempos.get(i);
			y = listpos + p * lineh;
			
			if(i == scrsel)
			{
				if(ofnstep != 0)
				{
					x = ofnx;
				}
				
				g.setClip(0, 0, width, height);

				//g.setColor(ColorScheme.colors[ColorScheme.selback1]);
				//g.fillRect(0, y - 1, width, lineh);

				if(cursor != null)
				{
					g.drawImage(cursor, 0, y - 1, Graphics.LEFT | Graphics.TOP);
				}
				else
				{
					images.drawHGradient(g, ColorScheme.colors[ColorScheme.selback1], ColorScheme.colors[ColorScheme.selback2], 0, y - 1, width, lineh, options.ditherGradients);
				}
				
				g.setColor(ColorScheme.colors[ColorScheme.selfore]);
				g.drawString(s, x, y, Graphics.LEFT | Graphics.TOP);

				g.setColor(ColorScheme.colors[ColorScheme.ltborder]);
				g.drawRect(0, y - 1, width - 1, lineh - 1);

				g.setClip(1, 0, width - 1, height);
			}
			else
			{
				g.setColor(ColorScheme.colors[ColorScheme.fore]);
				g.drawString(s, x, y, Graphics.LEFT | Graphics.TOP);
			}
		}

		//-----------------------------------------------------------------------------------------
		
		cmenu.paint(g);
	}
	
	public void run()
	{
		try
		{
			long lfxsleep = lfx.getDelay();

			long ofnsleep = (int)(8000f / options.longScrollSpeed / Math.min(width, height));
			OFN_DEF_DELAY = (int)(600 / ofnsleep);

			if(options.noEffects)
			{
				lfxsleep = ofnsleep;
			}

			long target = AuxMath.gcd((int)lfxsleep, (int)ofnsleep);
			long sleep;

			if(target < AuxClass.MIN_THREAD_SLEEP)
			{
				target = AuxClass.MIN_THREAD_SLEEP;
			}

			long current;
			long ofnprev = System.currentTimeMillis();
			long lfxprev = System.currentTimeMillis();

			boolean flag = false;

			while(isshown)
			{
				current = System.currentTimeMillis();

				while(current - ofnprev >= ofnsleep)
				{
					ofnprev += ofnsleep;

					if(ofnstep != 0)
					{
						if(ofndelay > 0)
						{
							ofndelay--;
						}
						else
						{
							ofnx += ofnstep;
						}

						if(ofnstep > 0)
						{
							if(ofnx > 1)
							{
								ofnx = 1;
								ofnstep = -ofnstep;
								ofndelay = OFN_DEF_DELAY;
							}
						}
						else
						{
							if(ofnx < width - 1 - ofnw)
							{
								ofnx = width - 1 - ofnw;
								ofnstep = -ofnstep;
								ofndelay = OFN_DEF_DELAY;
							}
						}

						flag = true;
					}
				}

				while(current - lfxprev >= lfxsleep)
				{
					lfxprev += lfxsleep;
					lfx.moveLines();

					flag = true;
				}

				if(flag)
				{
					repaint();
					//serviceRepaints();
					
					flag = false;
				}

				sleep = target - (current - Math.min(ofnprev, lfxprev));

				if(sleep > 0)
				{
					Thread.sleep(sleep);
				}
			}
		}
		catch(InterruptedException ie)
		{
		}
		
		t = null;
	}
}
