 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 main.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 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[200];
   protected String[] busNames = new String[200];
   protected boolean[] busFill = new boolean[200];
   
   public abstract void generate(boolean paramBoolean);
   
   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) {
     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++;
       } while (((x - leftSpace) / this.widthUnit + i < 200) && (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)] != false) 
        	 setBusFill(x, false);
         
         while ((x - leftSpace) / this.widthUnit + i < 200) // 500
         {
           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)] = (mem ? 0 : true);
     }
     else {
       for (int i = getFirstChange(x); i < getNextChange(indice); i++) this.busFill[i] = false;
     }
   }
*/   
   public void setBusFill(int x, boolean clean){
		int indice = (x-leftSpace)/widthUnit;
		if (!clean){
			boolean mem = busFill[getFirstChange(x)];
			for (int i=getFirstChange(x); i<getNextChange(indice); i++) busFill[i]=false;
			busFill[getFirstChange(x)]=!mem;
		}
		else{
			for (int i=getFirstChange(x); i<getNextChange(indice); i++) 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])) {
       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)
   {
     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]);
       drawSignalsInInterval(g, chrono.timeLapse[(chrono.nbrTimeLapse - 1)][3], maxWidth - 15 - leftSpace);
     }
     else {
       drawSignalsInInterval(g, 0, maxWidth - 15 - leftSpace);
     }
     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)]);
             g2.setStroke(new BasicStroke(thickness));
           }
         }
         if (i < chrono.nbrTimeLapse - 1) { drawSignalsInInterval(g, chrono.timeLapse[i][3], chrono.timeLapse[(i + 1)][0]);
         }
       }
     }
     g.translate(-leftSpace, 0);
     g2.setStroke(new BasicStroke());
     g.setFont(ChronoPanel.myFont);
     g.translate(0, -(nbr * (heightUnit + Yspace) + Yspace));
   }
   
 
   public void drawSignalsInInterval(Graphics g, int min, int max)
   {
     Rectangle2D drawArea = new Rectangle2D.Float(min, -1.0F, max - min, heightUnit + 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);
     }
     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);
         if ((this.wave[i] == 1) || (this.bus)) { g.drawLine(i * this.widthUnit, heightUnit, i * this.widthUnit + this.setUpTime, 0);
         }
         
       }
       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);
         if ((this.wave[i] == 1) || (this.bus)) { g.drawLine(i * this.widthUnit, 0, i * this.widthUnit + this.setUpTime - 1, 0);
         }
       }
       
       if ((this.wave[i] == 0) || (this.bus)) g.drawLine(i * this.widthUnit + this.setUpTime, heightUnit, (i + 1) * this.widthUnit - 1, heightUnit);
       if ((this.wave[i] == 1) || (this.bus)) { g.drawLine(i * this.widthUnit + this.setUpTime, 0, (i + 1) * this.widthUnit - 1, 0);
       }
     }
     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 < 200; i++) {
       int left = i * this.widthUnit + this.setUpTime;
       if (i == 0) left = 0;
       int endData = (getNextChange(i) + 1) * this.widthUnit;
       
       if (this.busFill[i] != false)
       {
         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);
   }
 }





