package com.one.vector;

import javax.microedition.lcdui.Graphics;
import java.util.Vector;
import java.util.Enumeration;
import java.lang.Math;
import java.io.*;

public class Group extends VectorElement
{
	public VectorImage image;
	
	protected Vector effects;
	
	protected Vector elements;
	protected int count;
	
	protected double[] data = new double[4];
	
	public double[] param = new double[7];
	public int angle;
	
	protected int[] clip = new int[4];
	
	public Group(VectorImage vi, DataInputStream dis) throws IOException
	{
		setImage(vi);
		read(dis);
	}
	
	public void read(DataInputStream dis) throws IOException
	{
		name = dis.readUTF();
		color = dis.readInt();
		parseFlag(dis.readUnsignedByte());
		
		data[0] = dis.readFloat();
		data[1] = dis.readFloat();
		data[2] = dis.readFloat();
		data[3] = dis.readFloat();
		
		param[0] = dis.readFloat();
		param[1] = dis.readFloat();
		param[4] = dis.readFloat();
		param[5] = dis.readFloat();

		if(image.getVersion() >= 0x0401)
		{
			angle = dis.readShort();
		}
		else
		{
			angle = 0;
		}
		
		count = dis.readUnsignedShort();
		elements = new Vector(count);
		
		VectorElement ve;
		
		for(int i = 0; i < count; i++)
		{
			ve = VectorElement.getInstance(dis.readUnsignedByte());
			ve.read(dis);
			
			ve.create();
			ve.setGroup(this);
			elements.addElement(ve);
		}
		
		int fxcount = dis.readUnsignedShort();
		effects = new Vector(fxcount);
		
		Effect effect;
		
		for(int i = 0; i < fxcount; i++)
		{
			effect = Effect.getInstance(dis.readUnsignedByte());
			effect.read(dis);
			
			effects.addElement(effect);
		}
		
		readTransformList(dis);
	}
	
	public void write(DataOutputStream dos) throws IOException
	{
		dos.writeUTF(name);
		dos.writeInt(color);
		dos.writeByte(getFlag());
		
		dos.writeFloat((float)data[0]);
		dos.writeFloat((float)data[1]);
		dos.writeFloat((float)data[2]);
		dos.writeFloat((float)data[3]);
		
		dos.writeFloat((float)param[0]);
		dos.writeFloat((float)param[1]);
		dos.writeFloat((float)param[4]);
		dos.writeFloat((float)param[5]);
		dos.writeShort(angle);
		
		dos.writeShort(count);
		
		VectorElement ve;
		
		for(int i = 0; i < count; i++)
		{
			ve = (VectorElement)elements.elementAt(i);
			
			dos.writeByte(VectorElement.elementType(ve));
			ve.write(dos);
		}
		
		dos.writeShort(effects.size());
		
		Effect effect;
		
		for(int i = 0; i < effects.size(); i++)
		{
			effect = (Effect)effects.elementAt(i);
			
			dos.writeByte(Effect.effectType(effect));
			effect.write(dos);
		}
		
		writeTransformList(dos);
	}
	
	public Group()
	{
		image = DEFAULT_IMAGE;
		
		color = 0xFF000000;
		
		data[0] = 0;
		data[1] = 0;
		data[2] = 1;
		data[3] = 1;
		
		param[0] = 0;
		param[1] = 0;
		param[2] = 1;
		param[3] = 1;
		param[4] = 1;
		param[5] = 1;
		param[6] = 1;
		angle = 0;
		
		elements = new Vector();
		count = 0;
		
		effects = new Vector();
	}
	
	public void addElement(VectorElement ve)
	{
		ve.setName(image.nextElementName(elementType(ve)));
		ve.setGroup(this);
		ve.rescale();
		
		elements.addElement(ve);
		count = elements.size();
	}
	
	public void create()
	{
		for(int i = 0; i < count; i++)
		{
			((VectorElement)elements.elementAt(i)).create();
		}
	}
	
	public void rescale()
	{
		rescale(true);
	}
	
	public void rescale(boolean flag)
	{
		param[2] = (1.0f / param[4]) * image.width;
		param[3] = (1.0f / param[5]) * image.height;
		
		param[6] = (double)Math.sqrt((double)((param[2] * param[2] + param[3] * param[3]) / 2.0f));
		
		if(flag)
		{
			for(int i = 0; i < count; i++)
			{
				((VectorElement)elements.elementAt(i)).rescale();
			}
		}
	}
	
	public void update(boolean flag, boolean changed)
	{
		Transform transform;
		int transparam;
		
		for(int i = 0; i < transcount; i++)
		{
			transform = (Transform)transforms.elementAt(i);
			transform.setFrame(image.frame);
			
			if(transform.isApplied())
			{
				transparam = transform.getParam();
				
				switch(transparam)
				{
					case SHOW:
						setHidden(AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition()));
						break;
					
					case COLOR:
						color = AuxMath.linearFunctionRGB(transform.getData(), transform.getPosition());
						break;
					
					case X:
						data[0] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						break;
					
					case Y:
						data[1] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						break;
					
					case WIDTH:
						data[2] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						break;
					
					case HEIGHT:
						data[3] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						break;
					
					case ANGLE:
						angle = AuxMath.specificFunctionInt(transform.getData(), transform.getPosition());
						break;
					
					case OFFSET_X:
						param[0] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						changed = true;
						break;
					
					case OFFSET_Y:
						param[1] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						changed = true;
						break;
					
					case SCALE_X:
						param[4] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						changed = true;
						break;
					
					case SCALE_Y:
						param[5] = AuxMath.specificFunctionFloat(transform.getData(), transform.getPosition());
						changed = true;
						break;
				}
			}
		}
		
		if(flag && changed)
		{
			rescale(false);
		}
		
		for(int i = 0; i < count; i++)
		{
			((VectorElement)elements.elementAt(i)).update(flag, changed);
		}
	}
	
	public int getTranslateX()
	{
		return getScaled(data[0], 0);
	}
	
	public int getTranslateY()
	{
		return getScaled(data[1], 1);
	}
	
	public void paint(Graphics g)
	{
		if(hidden)
		{
			return;
		}
		
		clip[0] = g.getClipX();
		clip[1] = g.getClipY();
		clip[2] = g.getClipWidth();
		clip[3] = g.getClipHeight();
		
		g.clipRect(getScaled(data[0], 0), getScaled(data[1], 1), getScaled(data[2], 2), getScaled(data[3], 3));
		g.translate(getScaled(data[0], 0), getScaled(data[1], 1));
		
		for(int i = 0; i < count; i++)
		{
			((VectorElement)elements.elementAt(i)).paint(g);
		}
		
		g.translate(-getScaled(data[0], 0), -getScaled(data[1], 1));
		g.setClip(clip[0], clip[1], clip[2], clip[3]);
	}
	
	public int getScaled(double v, int mode)
	{
		switch(mode)
		{
			case 0:
				return (int)((v + image.param[0]) * image.param[2]);
			
			case 1:
				return (int)((v + image.param[1]) * image.param[3]);
			
			case 2:
				return (int)(v * image.param[2]);
			
			case 3:
				return (int)(v * image.param[3]);
		}
		
		return (int)v;
	}
	
	public double getInternal(int v, int mode)
	{
		switch(mode)
		{
			case 0:
				return (double)v / image.param[2] - image.param[0];
			
			case 1:
				return (double)v / image.param[3] - image.param[1];
			
			case 2:
				return (double)v / image.param[2];
			
			case 3:
				return (double)v / image.param[3];
		}
		
		return (double)v;
	}
	
	public void setImage(VectorImage img)
	{
		if(img != null)
		{
			image = img;
		}
		else
		{
			image = DEFAULT_IMAGE;
		}
	}
	
	public VectorImage getImage()
	{
		return image;
	}
	
	public Enumeration listElements()
	{
		return elements.elements();
	}
	
	public VectorElement getElement(String name)
	{
		VectorElement element;
		
		for(int i = 0; i < count; i++)
		{
			element = (VectorElement)elements.elementAt(i);
			
			if(element.getName().equals(name))
			{
				return element;
			}
		}
		
		return null;
	}
	
	public VectorElement getNextElement(VectorElement current)
	{
		for(int i = 0; i < count; i++)
		{
			if(elements.elementAt(i) == current)
			{
				if(i == count - 1)
				{
					return (VectorElement)elements.elementAt(0);
				}
				else
				{
					return (VectorElement)elements.elementAt(i + 1);
				}
			}
		}
		
		return current;
	}
	
	public VectorElement getPreviousElement(VectorElement current)
	{
		for(int i = 0; i < count; i++)
		{
			if(elements.elementAt(i) == current)
			{
				if(i == 0)
				{
					return (VectorElement)elements.elementAt(count - 1);
				}
				else
				{
					return (VectorElement)elements.elementAt(i - 1);
				}
			}
		}
		
		return current;
	}
	
	public void deleteElement(VectorElement ve)
	{
		elements.removeElement(ve);
		count = elements.size();
	}
	
	public void arrangeElements(int index, int newindex)
	{
		if(index < 0)
		{
			index = 0;
		}
		else if(index >= elements.size())
		{
			index = elements.size() - 1;
		}
		
		if(newindex < 0)
		{
			newindex = 0;
		}
		else if(newindex >= elements.size())
		{
			newindex = elements.size() - 1;
		}
		
		Object temp = elements.elementAt(newindex);
		elements.setElementAt(elements.elementAt(index), newindex);
		elements.setElementAt(temp, index);
	}
	
	public void addEffect(Effect effect)
	{
		effect.setName(image.nextEffectName(Effect.effectType(effect)));
		effects.addElement(effect);
	}
	
	public Enumeration listEffects()
	{
		return effects.elements();
	}
	
	public boolean hasEffects()
	{
		return effects.size() > 0;
	}
	
	public Effect getEffect(String name)
	{
		Effect effect;
		
		for(int i = 0; i < effects.size(); i++)
		{
			effect = (Effect)effects.elementAt(i);
			
			if(effect.getName().equals(name))
			{
				return effect;
			}
		}
		
		return null;
	}
	
	public void deleteEffect(Effect effect)
	{
		effects.removeElement(effect);
	}
	
	public void arrangeEffects(int index, int newindex)
	{
		if(index < 0)
		{
			index = 0;
		}
		else if(index >= effects.size())
		{
			index = effects.size() - 1;
		}
		
		if(newindex < 0)
		{
			newindex = 0;
		}
		else if(newindex >= effects.size())
		{
			newindex = effects.size() - 1;
		}
		
		Object temp = effects.elementAt(newindex);
		effects.setElementAt(effects.elementAt(index), newindex);
		effects.setElementAt(temp, index);
	}
	
	public Object getValue(int param, int subparam)
	{
		switch(param)
		{
			case X:
				return new Double(data[0]);
			
			case Y:
				return new Double(data[1]);
			
			case WIDTH:
				return new Double(data[2]);
			
			case HEIGHT:
				return new Double(data[3]);
			
			case ANGLE:
				return new Integer(angle);
		}
		
		return null;
	}
	
	public void setValue(Object value, int param, int subparam)
	{
		switch(param)
		{
			case X:
				data[0] = ((Double)value).doubleValue();
				break;
			
			case Y:
				data[1] = ((Double)value).doubleValue();
				break;
			
			case WIDTH:
				data[2] = ((Double)value).doubleValue();
				break;
			
			case HEIGHT:
				data[3] = ((Double)value).doubleValue();
				break;
			
			case ANGLE:
				angle = ((Integer)value).intValue();
				break;
		}
	}
	
	public void writeSVG(PrintStream ps)
	{
		ps.print("\t\t<g");
		ps.print(" transform=\"rotate(" + Integer.toString(angle) + ", " + Integer.toString(getScaled(data[2], 2) / 2) + ", " + Integer.toString(getScaled(data[3], 3) / 2) + ")");
		ps.print(" translate(" + Integer.toString(getScaled(data[0], 0)) + ", " + Integer.toString(getScaled(data[1], 1)) + ")\"");
		ps.println(">");
		
		for(int i = 0; i < count; i++)
		{
			((VectorElement)elements.elementAt(i)).writeSVG(ps);
		}
		
		ps.println("\t\t</g>");
	}
}