package signals;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Float;
import java.io.Serializable;

import chrono.ChronoPanel;

public abstract class Signals  implements Serializable
{
	private static final long serialVersionUID = 1L;
	public static int leftSpace = 80;
	public static int Yspace = 20;
	public static int heightUnit = 25;
	private static int idBank = 0;
	public int id = -1;
	public int widthUnit = 20;
	public String name;
	public String type;
	public int setUpTime = 0;
	public boolean bus = false;
	public boolean clock = false;
	public boolean dependsOnClk = false;
	public float ratio = 1.0F;
	public int clockDomainId;
	public boolean selected_for_edit = false;
	public boolean clkToQdelay = false;
	public boolean displayRisingEdges = false;
	public boolean bold = false;
	public Color color = Color.black;
	public transient boolean selected = false;
	//public int[] wave = new int['?'];
	
	public int[] wave = new int[100];
	
	protected String[] busNames = new String['?'];
	protected boolean[] busFill = new boolean['?'];
  
	public abstract void generate(boolean paramBoolean);
	
	private int nb_points = 100;
  
	public static void resetIdBank()
	{
		idBank = 0;
	}
  
	public static void setIdBank(int id)
	{
		idBank = id;
	}
  
	protected void setId()
	{
		this.id = idBank;
		idBank += 1;
	}
  
	/**********************/
	
	public void setWave(int x, boolean toggle)
	{
		
		System.out.println("TRACE");
		
		int newValue = 0;
		if (this.clkToQdelay) 
		{
			x += this.widthUnit / 2;
		}
		
		if (!this.clock)
		{
			int value = this.wave[((x - leftSpace) / this.widthUnit)];
			if (value == 0) 
			{
				newValue = 1;
			}
			if (value == 1) 
			{
				newValue = 0;
			}
      
			int i = 0;
      
			do
			{
				this.wave[((x - leftSpace) / this.widthUnit + i)] = newValue;
				i++;
				System.out.println("A");
			} while (((x - leftSpace) / this.widthUnit + i < nb_points) && (this.wave[((x - leftSpace) / this.widthUnit + i)] == value) && (toggle));
			// 500
			
			if (this.bus)
			{
				if (this.busNames[((x - leftSpace) / this.widthUnit)] != null) 
				{
					setBusName(x, this.busNames[((x - leftSpace) / this.widthUnit)]);
				}
                       
				//if (this.busFill[((x - leftSpace) / this.widthUnit)] != 0) 
				if (this.busFill[((x - leftSpace) / this.widthUnit)] != false) 					
				{
			          setBusFill(x, false);
			    }
				
				//int aze = (x - leftSpace) / (this.widthUnit);
				//if (this.busFill[((x - leftSpace) / this.widthUnit)] != 0)
		        //if ( aze != 0)
		        //{
		        //  setBusFill(x, false);
		        //}
        
        
		        while ((x - leftSpace) / this.widthUnit + i < nb_points) // 500
		        {
					System.out.println("B");

		        	if (this.wave[((x - leftSpace) / this.widthUnit + i)] == 0) 
		        	{
		        		this.wave[((x - leftSpace) / this.widthUnit + i)] = 1;
		        	} 
		        	else if (this.wave[((x - leftSpace) / this.widthUnit + i)] == 1) 
		        	{
		        		this.wave[((x - leftSpace) / this.widthUnit + i)] = 0;
		        	}
		        	i++;
		        }
			}
		}
	}
  
	/**********************/
		
	public void setBusName(int x, String s)
	{
		int indice = (x - leftSpace) / this.widthUnit;
		
		for (int i = getFirstChange(x); i < getNextChange(indice); i++) 
		{
			this.busNames[i] = null;
		}
		this.busNames[getFirstChange(x)] = s;
	}
  
	/**********************/
		
	public void setBusFill(int x, boolean clean)
	{
		int indice = (x - leftSpace) / this.widthUnit;
		if (!clean)
		{
			boolean mem = this.busFill[getFirstChange(x)];
			for (int i = getFirstChange(x); i < getNextChange(indice); i++) 
			{
				this.busFill[i] = false;
			}
			this.busFill[getFirstChange(x)] = (boolean) (mem ? 0 : true);
		}
		else
		{
			for (int i = getFirstChange(x); i < getNextChange(indice); i++) 
			{
				this.busFill[i] = false;
			}
		}
	}
  
	/**********************/
		
	public int getFirstChange(int x)
	{
		int indice = (x - leftSpace) / this.widthUnit;
		while ((indice > 0) && (this.wave[(indice - 1)] == this.wave[indice])) 
		{
			indice--;
		}
		return indice;
	}
  
	/**********************/
		
	public int getIndex(int x)
	{
		return (x - leftSpace) / this.widthUnit;
	}
  
	/**********************/
		
	public int getNextChange(int offset)
	{
		int indice = offset;
//		while ((indice < 199) && (this.wave[(indice + 1)] == this.wave[indice])) 
		while ((indice < nb_points) && (this.wave[(indice + 1)] == this.wave[indice])) 
		{
			System.out.println("C");
			indice++;
		}
		return indice;
	}
  
	/**********************/
		
  	public int getLastDisplayed(int maxWidth)
  	{
  		int res = 0;
  		for (int i = 0; i < 200; i++) 
  		{
  			if (leftSpace + (i + 1) * this.widthUnit < maxWidth - 10) 
  			{
  				res++;
  			}
  		}
  		return leftSpace + res * this.widthUnit;
  	}
  
  	/**********************/
	  	
  	public void draw(Graphics g, ChronoPanel chrono, int nbr, int maxWidth, String type)
  	{
  		Graphics2D g2 = (Graphics2D)g;
  		float[] dash1 = { 5.0F };
  		float thickness = 1.0F;
  		g.setFont(ChronoPanel.myFont);
  		
  		if (this.bold)
  		{
  			g.setFont(ChronoPanel.myBoldFont);
  			thickness = 3.0F;
  		}
  		Stroke dashed = new BasicStroke(thickness, 0, 0, 10.0F, dash1, 0.0F);
  		
  		if (this.selected)
  		{
  			g.setColor(new Color(215, 215, 235));
  			g.fillRect(0, nbr * (heightUnit + Yspace) + Yspace / 2, maxWidth - 15, heightUnit + Yspace);
  		}
  		
  		g.setColor(this.color);
  		g.translate(0, nbr * (heightUnit + Yspace) + Yspace);
  		g.drawString(this.name, leftSpace - (ChronoPanel.metrics.stringWidth(this.name) + 8), heightUnit - 1);
    
  		g.translate(leftSpace, 0);
    
  		g2.setStroke(new BasicStroke(thickness));
    
  		if (this.bus) 
  		{
  			drawBusFillOptions(g, 0, maxWidth - 15 - leftSpace);
  		}
    
  		if ((chrono != null) && (chrono.nbrTimeLapse > 0))
  		{
  			drawSignalsInInterval(g, 0, chrono.timeLapse[0][0], type);
  			drawSignalsInInterval(g, chrono.timeLapse[(chrono.nbrTimeLapse - 1)][3], maxWidth - 15 - leftSpace,type);
  		}
  		else
  		{
  			drawSignalsInInterval(g, 0, maxWidth - 15 - leftSpace,type);
  		}
  		
  		if (chrono != null) 
  		{
  			for (int i = 0; i < chrono.nbrTimeLapse; i++)
  			{
  				for (int j = 0; j < 3; j++) 
  				{
  					if ((j == 0) || (j == 2))
  					{
  						g2.setStroke(dashed);
  						drawSignalsInInterval(g, chrono.timeLapse[i][j], chrono.timeLapse[i][(j + 1)],type);
  						g2.setStroke(new BasicStroke(thickness));
  					}
  				}
  				
  				if (i < chrono.nbrTimeLapse - 1) 
  				{
  					drawSignalsInInterval(g, chrono.timeLapse[i][3], chrono.timeLapse[(i + 1)][0],type);
  				}
  			}
  		}
    
  		g.translate(-leftSpace, 0);
  		g2.setStroke(new BasicStroke());
  		g.setFont(ChronoPanel.myFont);
  		g.translate(0, -(nbr * (heightUnit + Yspace) + Yspace));
  	}
  
  	//cinq   
  	//deux   xx   
  	//six
  	//un
  	//cinq
  	
  	/**********************/
	  	
  	public void drawSignalsInInterval(Graphics g, int min, int max, String type)
  	{
  		Rectangle2D drawArea = new Rectangle2D.Float(min, -1.0F, max - min, heightUnit + 3);
  		
  		//Rectangle2D drawArea = new Rectangle2D.Float(min, -1.0F, max - min, 50 + 3);
  		
  		
  		int limit = max / this.widthUnit + 2;
  		
  		//g.setClip(drawArea);
  		
  		if (this.bus) 
  		{
  			g.translate(-this.setUpTime / 2, 0);
  		}
  		if (this.clkToQdelay) 
  		{
  			g.translate(-this.widthUnit / 2, 0);
  		}
  		
  		int cpt = 0;
  		
  		for (int i = 0; i < limit; i++)
  		{
  			if ((i > 0) && (this.wave[(i - 1)] != this.wave[i]))
  			{
  				if ((this.wave[i] == 0) || (this.bus)) 
  				{
  					g.drawLine(i * this.widthUnit, 0, i * this.widthUnit + this.setUpTime, heightUnit);
  					//System.out.println("un");
  				}
  				if ((this.wave[i] == 1) || (this.bus)) 
  				{
  					g.drawLine(i * this.widthUnit, heightUnit, i * this.widthUnit + this.setUpTime, 0);
  					//System.out.println("deux");
  					
  					if(type.equals("clock"))
  					{
	  					cpt = cpt + 1;
	  					String clk_nb = Integer.toString(cpt);
	  					
	  					g.drawString( clk_nb ,  i * this.widthUnit, heightUnit - 30 );
	  					//System.out.println("deux");
	  					
	  					int r = 10;
	  					int x = i * this.widthUnit;
	  					int y = heightUnit - 38; 
	  					x = x-(r/2);
	  					y = y-(r/2);
	  					// g.setColor(Color.red);
	  					//g.fillOval(x,y,r,r);
	  					
	  					// pour la fléche qui monte 
	  					// point la gauche    
	  					int pgx =  i * this.widthUnit - 5;
	  					int pgy =  this.setUpTime + 10 ;
	  					
	  					g.drawLine(pgx, pgy, i * this.widthUnit, this.setUpTime);
	  					
	  					// point la droite gauche    
	  					int pdx =  i * this.widthUnit + 5;
	  					int pdy =  pgy ;
	  					
	  					g.drawLine(i * this.widthUnit, this.setUpTime, pdx, pdy);
  					}
	
  				}
  			}
  			else if ((this.setUpTime > 0) || (this.bus))
  			{
  				if ((this.wave[i] == 0) || (this.bus)) 
  				{
  					g.drawLine(i * this.widthUnit, heightUnit, i * this.widthUnit + this.setUpTime - 1, heightUnit);
  					//System.out.println("trois");
  				}
  				if ((this.wave[i] == 1) || (this.bus))
  				{
  					g.drawLine(i * this.widthUnit, 0, i * this.widthUnit + this.setUpTime - 1, 0);
  					//System.out.println("quatre");
  				}
  			}
  			
  			if ((this.wave[i] == 0) || (this.bus))
  			{
  				//g.drawString("x", i * this.widthUnit + this.setUpTime, heightUnit + 10 );
  				g.drawLine(i * this.widthUnit + this.setUpTime, heightUnit, (i + 1) * this.widthUnit - 1, heightUnit);
  				//System.out.println("cinq");

  			}
  			if ((this.wave[i] == 1) || (this.bus)) 
  			{
  			    g.drawLine(i * this.widthUnit + this.setUpTime, 0, (i + 1) * this.widthUnit - 1, 0);
				//System.out.println("six");
  			}
  		}
    
  		if (this.bus) 
  		{
  			g.translate(this.setUpTime / 2, 0);
  		}
    
  		if (this.clkToQdelay) 
  		{
  			g.translate(this.widthUnit / 2, 0);
  		}
  		g.setClip(null);
  	}
  
  	/**********************/
	  	
  	public void drawBusFillOptions(Graphics g, int min, int max)
  	{
  		Rectangle2D drawArea = new Rectangle2D.Float(min, 0.0F, max - min, heightUnit + 1);
  		g.setClip(drawArea);
    
  		if (this.bus) 
  		{
  			g.translate(-this.setUpTime / 2, 0);
  		}
    
  		if (this.clkToQdelay) 
  		{
  			g.translate(-this.widthUnit / 2, 0);
  		}
    
  		for (int i = 0; i < nb_points; i++) // 200
  		{
			System.out.println("D");

  			int left = i * this.widthUnit + this.setUpTime;
      
  			if (i == 0) 
  			{
  				left = 0;
  			}
  			int endData = (getNextChange(i) + 1) * this.widthUnit;
  			
  			
  			//if (this.busFill[i] != 0) // 0
  			if (this.busFill[i] != false) // 0	
  			{
  				g.setColor(new Color(Math.min(this.color.getRed() + 150, 255), Math.min(this.color.getGreen() + 150, 255), Math.min(this.color.getBlue() + 150, 255)));
  				g.fillRect(left, 0, endData - left, heightUnit);
        
  				int start = this.setUpTime / 2;
        
  				if ((left != 0) && (this.setUpTime != 0)) 
  				{
  					for (int j = 0; j < start; j++) 
  					{
  						g.drawLine(left - start + j, heightUnit / 2 - j * heightUnit / (2 * start), left - start + j, heightUnit / 2 + j * heightUnit / (2 * start));
  					}
  				}
        
  				if ((endData != getLastDisplayed(max)) && (this.setUpTime != 0)) 
  				{
  					for (int j = start; j >= 0; j--) 
  					{
  						g.drawLine(endData + start - j, heightUnit / 2 - j * heightUnit / (2 * start), endData + start - j, heightUnit / 2 + j * heightUnit / (2 * start));
  					}
  				}
  				g.setColor(this.color);
  			}
      
  			if (this.busNames[i] != null)
  			{
  				endData = Math.min((getNextChange(i) + 1) * this.widthUnit, getLastDisplayed(max));
  				g.drawString(this.busNames[i], (left + endData) / 2 - ChronoPanel.metrics.stringWidth(this.busNames[i]) / 2, heightUnit - 8);
  			}
  		}
  		
  		if (this.bus) 
  		{
  			g.translate(this.setUpTime / 2, 0);
  		}
  		if (this.clkToQdelay) 
  		{
  			g.translate(this.widthUnit / 2, 0);
  		}
  		g.setClip(null);
  	}
}
