package modules.image; // переведен

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.util.Timer;
import java.io.*;
import com.vmx.*;
import com.one.*;
import com.one.vector.*;

import com.one.file.*;
import filemanager.ColorScheme;
import filemanager.MenuListener;
import filemanager.cvsContextMenu;
import filemanager.images;
import filemanager.main;
import filemanager.options;
import java.util.Enumeration;
import java.util.TimerTask;

/**
 * Класс - просмотрщик картинок
 */
public class cvsImageView extends gkcCanvas implements Runnable, MenuListener
{
	public static final int MODE_NULL = 0;
	public static final int MODE_NORMAL = 1;
	public static final int MODE_VECTOR = 2;
	
	private ImageContainer currentImage = null;
	private int mode;

	private boolean rotate;
	private int trans;
	private boolean scaled;

	private boolean enableUI = true;
	private int pictureWidth, pictureHeight;
	private int currWidth, currHeight;
	private double pictureAspect;
	private int w, h;
	private double aspect;
	private int curposx, curposy;
	private String OnlyFileName = "";
	private String errorMessage = null;
	private AbstractFont nameFont;
	private int hstep, vstep;
	private int navx, navy, navw, navh;     // большой прямоугольник
	private int navcx, navcy, navcw, navch; // малый примоугольник
	private Timer timer = null;
	private boolean isshown;
	private int runpanel;
	private PlayList playlist;
	private boolean keyReleaseAllowed = true;
	private int keyHeld;

	private cvsContextMenu mn;

	protected static class AnimationTask extends TimerTask
	{
		protected ImageContainer image;
		protected PaintableObject canvas;

		public AnimationTask(ImageContainer image, PaintableObject canvas)
		{
			this.image = image;
			this.canvas = canvas;
		}

		public void run()
		{
			synchronized(image)
			{
				image.nextFrame();
			}

			canvas.repaint();
			canvas.serviceRepaints();
		}
	}

	private static cvsImageView instance;

	public static void loadInstance()
	{
		if(instance == null)
		{
			instance = new cvsImageView();
		}
	}

	public static cvsImageView getInstance()
	{
		loadInstance();
		return instance;
	}

	/**
	 * Конструктор
	 */
	public cvsImageView()
	{
		setFullScreenMode(true);

		w = getWidth();
		h = getHeight();

		aspect = (double)w / (double)h;

		nameFont = AbstractFont.getFont(AbstractFont.FACE_SYSTEM, AbstractFont.STYLE_PLAIN, AbstractFont.SIZE_SMALL);

		//fgimage = images.createUI(w, h);

		mn = new cvsContextMenu(menuData, menuKeyConfig, menuKeyAllowed);
		mn.setListener(this);
		
		runpanel = -1;
	}

	public void setPlayList(PlayList newlist)
	{
		playlist = newlist;
	}

	public void setPlayList(Enumeration newlist)
	{
		playlist = new PlayList(newlist);
	}

	protected void initPanels()
	{
		if(timer != null)
		{
			timer.cancel();
			timer = null;
		}

		mode = MODE_NULL;
		currentImage = null;

		if(runpanel < 0)
		{
			runpanel = main.manager.currentPanel();
		}
		
		main.manager.setCurrent(this, runpanel);
		main.manager.updateAssociation(runpanel, playlist.getCurrentElement());
		main.manager.changePanel(runpanel);
	}

	protected void initPlayList(String file)
	{
		OnlyFileName = file.substring(file.lastIndexOf('/') + 1);

		if(playlist == null)
		{
			playlist = new PlayList(file);
		}

		playlist.selectElement(file);
	}

	protected void initDisplay()
	{
		// Прячем картинку за пределы экрана на время
		curposx = w;
		curposy = h;

		pictureWidth = currentImage.getWidth();
		pictureHeight = currentImage.getHeight();

		switch(ImageOptions.rotateMode)
		{
			default:
			case 0:
				rotate = false;
				trans = Sprite.TRANS_NONE;
				break;

			case 1:
				rotate = true;
				trans = Sprite.TRANS_ROT90;
				break;

			case 2:
				rotate = false;
				trans = Sprite.TRANS_ROT180;
				break;

			case 3:
				rotate = true;
				trans = Sprite.TRANS_ROT270;
				break;

			case 4:
				if(pictureWidth > pictureHeight)
				{
					rotate = true;
					trans = Sprite.TRANS_ROT90;
				}
				break;

			case 5:
				if(pictureWidth > pictureHeight)
				{
					rotate = true;
					trans = Sprite.TRANS_ROT270;
				}
				break;
		}

		if(ImageOptions.scaleImages)
		{
			pictureAspect = (double)pictureWidth / (double)pictureHeight;

			if(rotate) //если поворачиваем
			{
				/* экран повернут, так что ширина это h и высота это w */

				if(pictureAspect > (1 / aspect)) //вписываем по ширине
				{
					currentImage.scale(h, (int)(h / pictureAspect));
				}
				else //вписываем по высоте
				{
					currentImage.scale((int)(w * pictureAspect), w);
				}
			}
			else //если не поворачиваем
			{
				if(pictureAspect > aspect) //вписываем по ширине
				{
					currentImage.scale(w, (int)(w / pictureAspect));
				}
				else //вписываем по высоте
				{
					currentImage.scale((int)(h * pictureAspect), h);
				}
			}

			// SHIT! Что-то мне подсказывает, что это можно было сделать проще...
		}

		currWidth = currentImage.getWidth();
		currHeight = currentImage.getHeight();

		if(currWidth != pictureWidth || currHeight != pictureHeight)
		{
			scaled = true;
			hstep = vstep = 0;
		}
		else
		{
			scaled = false;
			hstep = vstep = (w + h) / 6;
		}

		if(rotate)
		{
			currWidth ^= currHeight;
			currHeight ^= currWidth;
			currWidth ^= currHeight;
		}

		if(currWidth > currHeight) // изображение горизонтальное
		{
			navw = w / 4;
			navh = navw * currHeight / currWidth;
		}
		else
		{
			navh = w / 4;
			navw = navh * currWidth / currHeight;
		}

		navx = w - navw - 4;
		navy = 3;

		curposx = (w - currWidth) / 2;
		curposy = (h - currHeight) / 2;
	}
	
	public void displayImageFromStream(InputStream is, String imgName)
	{
		initPanels();
		
		mode = MODE_NORMAL;
		
		initPlayList(imgName);
		
		repaint();
		serviceRepaints();

		errorMessage = Locale.getString(this, Locale.NO_IMAGE);

		try
		{
			currentImage = new GenericImageContainer(Image.createImage(is));
			is.close();

			errorMessage = null;
		}
		catch(Throwable x)
		{
			//ErrScreen.showErrMsg(123, x);

			x.printStackTrace();
			errorMessage = AuxClass.formatException(x);
		}

		if(currentImage == null || currentImage.isNull())
		{
			currentImage = new TestImageContainer();
		}

		initDisplay();

		repaint();
	}

	public void displayImage(String imgName)
	{
		try
		{	
			displayImageFromStream(Connector.openInputStream("file:///" + imgName), imgName);
		}
		catch(Exception e)
		{
		}
	}
	
	public void displayVectorImageFromStream(InputStream is, String imgName)
	{
		initPanels();

		mode = MODE_VECTOR;
		currentImage = null;

		initPlayList(imgName);
		repaint();

		errorMessage = Locale.getString(this, Locale.NO_IMAGE);

		try
		{
			is = new DataInputStream(is);
			currentImage = new VectorImageContainer(new VectorImage((DataInputStream)is));
			is.close();

			errorMessage = null;
		}
		catch(Throwable x)
		{
			//ErrScreen.showErrMsg(124, x);

			x.printStackTrace();
			errorMessage = AuxClass.formatException(x);
		}

		if(currentImage == null || currentImage.isNull())
		{
			currentImage = new TestImageContainer();
		}

		initDisplay();

		repaint();
	}
	
	public void displayVectorImage(String imgName)
	{
		try
		{
			displayVectorImageFromStream(Connector.openInputStream("file:///" + imgName), imgName);
		}
		catch(Exception e)
		{
		}
	}

	/**
	 * Функция отрисовки
	 */
	public void paint(ExtGraphics g)
	{
		g.setColor(images.cImgBack); // 0x000000);
		g.fillRect(0, 0, w, h);

		// фон
		if(currentImage != null)
		{
			currentImage.paint(g, curposx, curposy, trans);
		}

		if(enableUI)
		{
			if(currentImage != null && (currWidth > w || currHeight > h))
			{
				// ----- прямоугольники в правом верхнем углу -----
				
				g.setColor(ColorScheme.colors[ColorScheme.back1]);
				g.fillRect(navx, navy, navw, navh);
				
				if(currWidth > w)
				{
					navcw = navw * w / currWidth;
					navcx = navx - curposx * navw / currWidth;
				}
				else
				{
					navcw = navw;
					navcx = navx;
				}
				
				if(currHeight > h)
				{
					navch = navh * h / currHeight;
					navcy = navy - curposy * navh / currHeight;
				}
				else
				{
					navch = navh;
					navcy = navy;
				}
				
				/* - такого не должно быть - см. условия выше
				if(navcw > navw)
				{
					navcw = navw;
				}
				
				if(navch > navh)
				{
					navch = navh;
				}
				
				if(navcx < navx)
				{
					navcx = navx;
				}
				else if(navcx + navcw > navx + navw)
				{
					navcx = navx + navw - navcw;
				}
				
				if(navcy < navy)
				{
					navcy = navy;
				}
				else if(navcy + navch > navy + navh)
				{
					navcy = navy + navh - navch;
				}
				*/
				
				g.setColor(ColorScheme.colors[ColorScheme.fore]);
				g.fillRect(navcx + 1, navcy + 1, navcw, navch);
				
				g.setColor(ColorScheme.colors[ColorScheme.dkborder]);
				g.drawRect(navx, navy, navw, navh);
				
				// ----- закончились прямоугольники -----
			}
			
			if(images.minUI != null)
			{
				g.drawImage(images.minUI, 0, h, Graphics.LEFT | Graphics.BOTTOM);
			}

			g.setFont(nameFont);

			g.setColor(images.cPlayFore1); //0x800000);
			g.drawString(OnlyFileName, w / 2, h - images.uiBottomHeight + images.uiBottomVSpace, Graphics.TOP | Graphics.HCENTER);

			if(currentImage != null)
			{
				String tmp;

				if(errorMessage != null)
				{
					tmp = errorMessage;
				}
				else
				{
					tmp = pictureWidth + " x " + pictureHeight + " (";

					if(scaled)
					{
						tmp += Locale.getString(this, Locale.IMAGEVIEW_SCALED) + ", ";
					}

					if(mode == MODE_VECTOR && currentImage != null)
					{
						tmp += Integer.toString(currentImage.currentFrame());
					}
					else
					{
						switch(trans)
						{
							case Sprite.TRANS_NONE:
								tmp += "0°";
								break;

							case Sprite.TRANS_ROT90:
								tmp += "90°";
								break;

							case Sprite.TRANS_ROT180:
								tmp += "180°";
								break;

							case Sprite.TRANS_ROT270:
								tmp += "270°";
								break;
						}
					}

					tmp += ")";
				}

				g.setColor(images.cPlayFore2); //0x000080);
				g.drawString(tmp, w / 2, h - images.uiBottomVSpace, Graphics.BOTTOM | Graphics.HCENTER);
			}
		}
	}

	public void keyPressed(int keyCode)
	{
		keyCode = remapKey(keyCode);

		keyReleaseAllowed = true;

		if(keyCode == KEY_LSK)
		{
			showMenu();
		}
	}

	public void keyRepeated(int keyCode)
	{
		keyCode = remapKey(keyCode);

		if(mn.isKeyAllowed(keyCode))
		{
			if(++keyHeld == 3)
			{
				keyHeld = 0;
				keyReleaseAllowed = false;

				mn.keyAction(keyCode | KEY_HELD);
			}
		}
		else
		{
			keyReleaseAllowed = false;
			handleKeyAction(keyCode);
		}
	}

	public void keyReleased(int keyCode)
	{
		keyCode = remapKey(keyCode);

		if(!keyReleaseAllowed)
		{
			return;
		}

		if(mn.isKeyAllowed(keyCode))
		{
			if(keyHeld < 3 && isKeyNum(keyCode))
			{
				mn.keyAction(keyCode);
			}
		}
		else
		{
			handleKeyAction(keyCode);
		}

		keyHeld = 0;
	}

	/**
	 * Обработчик нажатий клавиш
	 */
	public void handleKeyAction(int keyCode)
	{
		keyCode = rotateKey(keyCode, trans);

		if(keyCode == KEY_DOWN || keyCode == KEY_RIGHT) // Следующая картинка
		{
			nextPicture();
		}
		else if(keyCode == KEY_UP || keyCode == KEY_LEFT) // Предыдущ картинка
		{
			prevPicture();
		}
		else if(keyCode == KEY_FIRE)
		{
			if(mode == MODE_VECTOR)
			{
				if(timer != null)
				{
					timer.cancel();
					timer = null;
				}
				else
				{
					timer = new Timer();
					timer.scheduleAtFixedRate(new AnimationTask(currentImage, this), 0, currentImage.getFrameDelay());
				}
			}
		}
		else if(keyCode == KEY_RSK) // Выход
		{
			if(timer != null)
			{
				timer.cancel();
				timer = null;
			}
			
			mode = MODE_NULL;

			playlist.cancelSearch();
			playlist = null;
			
			main.manager.ret();
			runpanel = -1;

			if(options.unloadModules)
			{
				instance = null;
			}
		}
		else if(keyCode == KEY_CANCEL) // сворачиваемся
		{
			main.manager.minimizePanel();
		}
		else if(keyCode == KEY_NUM4)
		{
			curposx += hstep;
			
			if(curposx > 0)
			{
				curposx = 0;
			}
			
			repaint();
		}
		else if(keyCode == KEY_NUM6)
		{
			curposx -= hstep;
			
			if(curposx < w - currWidth)
			{
				curposx = w - currWidth;
			}
			
			repaint();
		}
		else if(keyCode == KEY_NUM2)
		{
			curposy += vstep;
			
			if(curposy > 0)
			{
				curposy = 0;
			}
			
			repaint();
		}
		else if(keyCode == KEY_NUM8)
		{
			curposy -= vstep;
			
			if(curposy < h - currHeight)
			{
				curposy = h - currHeight;
			}
			
			repaint();
		}
		else if(keyCode == KEY_NUM5)
		{
			curposx = (w - currWidth) / 2;
			curposy = (h - currHeight) / 2;
			
			repaint();
		}
		else if(keyCode == KEY_NUM1)
		{
			if(timer == null)
			{
				if(mode == MODE_VECTOR)
				{
					currentImage.prevFrame();
					repaint();
				}
			}
		}
		else if(keyCode == KEY_NUM3)
		{
			if(timer == null)
			{
				if(mode == MODE_VECTOR)
				{
					currentImage.nextFrame();
					repaint();
				}
			}
		}
		else if(keyCode == KEY_NUM7)
		{
			if(timer == null)
			{
				if(mode == MODE_VECTOR)
				{
					currentImage.gotoFrame(currentImage.currentFrame() - 10);
					repaint();
				}
			}
		}
		else if(keyCode == KEY_NUM9)
		{
			if(timer == null)
			{
				if(mode == MODE_VECTOR)
				{
					currentImage.gotoFrame(currentImage.currentFrame() + 10);
					repaint();
				}
			}
		}
	}
	
	/**
	 * следующая картинка
	 */
	public void nextPicture()
	{
		playlist.nextElement(false);
		main.FileSelect.executeFile(playlist, null, runpanel);
	}
	
	/**
	 * предыдущая картинка
	 */
	public void prevPicture()
	{
		playlist.prevElement(false);
		main.FileSelect.executeFile(playlist, null, runpanel);
	}
	
	private void redisplay()
	{
		if(mode == MODE_VECTOR)
		{
			currentImage.scale(-1, -1);

			initDisplay();

			repaint();
		}
		else
		{
			main.FileSelect.executeFile(playlist, null, runpanel);
		}
	}
	
	public void showNotify()
	{
		isshown = true;
		keyReleaseAllowed = false;
		
		if(ImageOptions.useAccelerometer)
		{
			(new Thread(this, "ImageView/AccelEvent")).start();
		}
	}
	
	public void hideNotify()
	{
		isshown = false;
	}
	
	public void run()
	{
		while(isshown)
		{
			int delta = main.accelerometer.getDelta(0, 1200);
			
			if(currentImage != null || currentImage != null)
			{
				if(delta != 0)
				{
					if(delta > 0)
					{
						nextPicture();
					}
					else
					{
						prevPicture();
					}
				}
			}
			
			try
			{
				Thread.sleep(300);
			}
			catch(InterruptedException ie)
			{
			}
		}
	}

	public void menuAction(int action)
	{
		mn.ret();

		switch(action)
		{
			case Locale.MINIMIZE_CMD:
				try
				{
					main.dsp.setCurrent(null);
				}
				catch(Exception e)
				{
				}
				break;

			case Locale.ROTATE_CMD:
				if(++ImageOptions.rotateMode > 3)
				{
					ImageOptions.rotateMode = 0;
				}

				redisplay();
				break;

			case Locale.SCALE_CMD:
				// если не вышло масштабировать,
				// то нечего и переключать

				boolean wasrunning = timer != null;
				int prevframe = currentImage.currentFrame();

				if(wasrunning)
				{
					timer.cancel();
					timer = null;
				}

				ImageOptions.scaleImages = !scaled;
				redisplay();

				currentImage.gotoFrame(prevframe);

				if(wasrunning)
				{
					timer = new Timer();
					timer.scheduleAtFixedRate(new AnimationTask(currentImage, this), 0, currentImage.getFrameDelay());
				}
				break;

			case Locale.ENABLE_UI_CMD:
				enableUI = !enableUI;
				repaint();
				break;

			default:
				main.showMessage("Error", "Menu command #" + action + " is not implemented yet.", AlertType.WARNING, 3000, this);
		}
	}

	public void showMenu()
	{
		mn.show(this);
	}

	/**
	 * Данные меню.
	 */
	public static final int[][] menuData =
	{
		{
			Locale.MENU_FILE,
			Locale.SAVE_CMD,
			Locale.MINIMIZE_CMD
		},
		{
			Locale.MENU_EDIT,
			Locale.EFFECTS_CMD,
			Locale.QUANTIZATION,
			Locale.TRANSPARENCY
		},
		{
			Locale.MENU_VIEW,
			Locale.ROTATE_CMD,
			Locale.SCALE_CMD,
			Locale.ENABLE_UI_CMD
		}
	};

	/**
	 * Данные enabled режимов меню.
	 */
	public static final boolean[][] menuEnabled =
	{
		{ false, true, true },
		{ false, true, true, true },
		{ false, true, true, true }
	};

	/**
	 * Кнопки, ассоциированные с пунктами меню.
	 * Может быть цифровая кнопка, *, # или KEY_INVALID.
	 */
	public static int[][] menuKeyConfig =
	{
		{
			KEY_STAR | KEY_HELD,
			KEY_NUM0 | KEY_HELD
		},
		{
			KEY_INVALID,
			KEY_INVALID,
			KEY_INVALID
		},
		{
			KEY_STAR,
			KEY_NUM0,
			KEY_POUND
		}
	};

	/**
	 * Какие горячие клавиши разрешено назначать для данного типа меню.
	 * Идет *, #, потом цифровые.
	 */
	public static final boolean[] menuKeyAllowed =
	{
		true, true, true, false, false, false, false, false, false, false, false, false
	};
}