/*
 * Created on Mar 26, 2004
 *
 */
package LumiSoft.UI.Controls;

import java.awt.*;
import javax.swing.*;

/**
 * BarCode control.
 */
public class WBarCode extends JPanel
{
	private String m_BarCode = ""; 
	
	/**
	 * Default constructor. 
	 *
	 */
	public WBarCode()
	{		
	}
	
	
	/**
	 * Is called by system when control need to repaint.
	 */
	public void paintComponent(Graphics g)
	{
		PaintBarCode(m_BarCode,g,new Rectangle(0,0,this.getWidth(),this.getHeight()));
	}
		
	
	/**
	 * Detects barcode type and paints barcode to specified graphics into specified draw rectangle.
	 * @param barCode BarCode value.
	 * @param g Graphics where to draw.
	 * @param drawBounds Draw bounds.
	 */
	public static void PaintBarCode(String barCode,Graphics g,Rectangle drawBounds)
	{
		if(barCode.length() == 8){
			Paint_EAN8(barCode,g,drawBounds);
		}
		else if(barCode.length() == 13){
			Paint_EAN13(barCode,g,drawBounds);
		}
	}
	
	/**
	 * Paint EAN8 barcode to specified graphics into specified draw rectangle.
	 * @param barCode BarCode value.
	 * @param g Graphics where to draw.
	 * @param drawBounds Draw bounds.
	 */
	public static void Paint_EAN8(String barCode,Graphics g,Rectangle drawBounds)
	{
		char[] symbols = barCode.toCharArray();

		//--- Validate barCode --------------//
		if(barCode.length() != 8){
			return;
		}
		for(int i=0;i<symbols.length;i++){
			if(!Character.isDigit(symbols[i])){
				return;
			}
		}
		if(!Check_EAN_CheckSum(barCode)){
			return;
		}
		//----------------------------------//

		g.setFont(new Font("SansSerif",Font.PLAIN,12));
		
		// Fill backround with white color
		g.setColor(Color.WHITE);
		g.fillRect(drawBounds.x,drawBounds.y,drawBounds.width,drawBounds.height);

		int lineWidth = 1;
		int x = drawBounds.x;

		// Paint left 'guard bars', always same '101'
		x += PaintStripes("101",g,lineWidth,x,drawBounds.y,drawBounds.height);
		
		// 4 symbol code
		String lines = "";
		for(int i=0;i<4;i++){
			lines += EAN_NumberToStripes("odd",symbols[i]);
		}

		// Paint human readable left-side 4 symbol code
		g.setColor(Color.BLACK);
		g.drawString(barCode.substring(0,4),x,drawBounds.y + drawBounds.height - 1);

		// Paint left-side 4 symbol stripes
		x += PaintStripes(lines,g,lineWidth,x,drawBounds.y,drawBounds.height - 12);

		// Paint center 'guard bars', always same '01010'
		x += PaintStripes("01010",g,lineWidth,x,drawBounds.y,drawBounds.height);

		// 5 symbol product code + 1 symbol parity
		lines = "";
		for(int i=4;i<8;i++){
			lines += EAN_NumberToStripes("character",symbols[i]);
		}

		// Paint human readable left-side 4 symbol code
		g.setColor(Color.BLACK);
		g.drawString(barCode.substring(4,8),x,drawBounds.y + drawBounds.height - 1);

		// Paint right-side 4 symbol stripes
		x += PaintStripes(lines,g,lineWidth,x,drawBounds.y,drawBounds.height - 12);
		
		// Paint right 'guard bars', always same '101'
		x += PaintStripes("101",g,lineWidth,x,drawBounds.y,drawBounds.height);
	}
	
	/**
	 * Paint EAN13 barcode to specified graphics into specified draw rectangle.
	 * @param barCode BarCode value.
	 * @param g Graphics where to draw.
	 * @param drawBounds Draw bounds.
	 */
	public static void Paint_EAN13(String barCode,Graphics g,Rectangle drawBounds)
	{            
		char[] symbols = barCode.toCharArray();

		//--- Validate barCode --------------//
		if(barCode.length() != 13){
			return;
		}
		for(int i=0;i<symbols.length;i++){
			if(!Character.isDigit(symbols[i])){
				return;
			}
		}
		if(!Check_EAN_CheckSum(barCode)){
			return;
		}
		//----------------------------------//
	
		g.setFont(new Font("SansSerif",Font.PLAIN,12));
		
		// Fill backround with white color
		g.setColor(Color.WHITE);
		g.fillRect(drawBounds.x,drawBounds.y,drawBounds.width,drawBounds.height);

		int lineWidth = 1;
		int x = drawBounds.x;

		// Paint human readable 1 system symbol code
		g.setColor(Color.BLACK);
		g.drawString(String.valueOf(symbols[0]),x + 1,drawBounds.y + drawBounds.height - 4);
		x += 10;

		// Paint left 'guard bars', always same '101'
		x += PaintStripes("101",g,lineWidth,x,drawBounds.y,drawBounds.height);

		// First number of barcode specifies how to encode each character in the left-hand 
		// side of the barcode should be encoded.
		boolean[] leftSideParity = new boolean[6];
		switch(symbols[0])
		{
			case '0':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = true;  // Odd
				leftSideParity[2] = true;  // Odd
				leftSideParity[3] = true;  // Odd
				leftSideParity[4] = true;  // Odd
				leftSideParity[5] = true;  // Odd
				break;
			case '1':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = true;  // Odd
				leftSideParity[2] = false; // Even
				leftSideParity[3] = true;  // Odd
				leftSideParity[4] = false; // Even
				leftSideParity[5] = false; // Even
				break;
			case '2':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = true;  // Odd
				leftSideParity[2] = false; // Even
				leftSideParity[3] = false; // Even
				leftSideParity[4] = true;  // Odd
				leftSideParity[5] = false; // Even
				break;
			case '3':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = true;  // Odd
				leftSideParity[2] = false; // Even
				leftSideParity[3] = false; // Even
				leftSideParity[4] = false; // Even
				leftSideParity[5] = true;  // Odd
				break;
			case '4':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = false; // Even
				leftSideParity[2] = true;  // Odd
				leftSideParity[3] = true;  // Odd
				leftSideParity[4] = false; // Even
				leftSideParity[5] = false; // Even
				break;
			case '5':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = false; // Even
				leftSideParity[2] = false; // Even
				leftSideParity[3] = true;  // Odd
				leftSideParity[4] = true;  // Odd
				leftSideParity[5] = false; // Even
				break;
			case '6':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = false; // Even
				leftSideParity[2] = false; // Even
				leftSideParity[3] = false; // Even
				leftSideParity[4] = true;  // Odd
				leftSideParity[5] = true;  // Odd
				break;
			case '7':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = false; // Even
				leftSideParity[2] = true;  // Odd
				leftSideParity[3] = false; // Even
				leftSideParity[4] = true;  // Odd
				leftSideParity[5] = false; // Even
				break;
			case '8':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = false; // Even
				leftSideParity[2] = true;  // Odd
				leftSideParity[3] = false; // Even
				leftSideParity[4] = false; // Even
				leftSideParity[5] = true;  // Odd
				break;
			case '9':
				leftSideParity[0] = true;  // Odd
				leftSideParity[1] = false; // Even
				leftSideParity[2] = false; // Even
				leftSideParity[3] = true;  // Odd
				leftSideParity[4] = false; // Even
				leftSideParity[5] = true;  // Odd
				break;
		}

		
		// NOTE: first number isn't painted as stripes, parity will determine it !!!
		
		// second number system digit + 5 symbol manufacter code
		String lines = "";
		for(int i=0;i<6;i++){
			boolean oddParity = leftSideParity[i];
			if(oddParity){
				lines += EAN_NumberToStripes("odd",symbols[i + 1]);
			}
			// Even parity
			else{
				lines += EAN_NumberToStripes("even",symbols[i + 1]);
			}
		}

		// Paint human readable left-side 6 symbol code
		g.setColor(Color.BLACK);
		g.drawString(barCode.substring(1,7),x + 1,drawBounds.y + drawBounds.height - 1);

		// Paint left-side 6 symbol stripes
		x += PaintStripes(lines,g,lineWidth,x,drawBounds.y,drawBounds.height - 12);
		
		// Paint center 'guard bars', always same '01010'
		x += PaintStripes("01010",g,lineWidth,x,drawBounds.y,drawBounds.height);

		// 5 symbol product code + 1 symbol parity
		lines = "";
		for(int i=7;i<13;i++){
			lines += EAN_NumberToStripes("character",symbols[i]);
		}

		// Paint human readable left-side 6 symbol code
		g.setColor(Color.BLACK);
		g.drawString(barCode.substring(7,13),x,drawBounds.y + drawBounds.height - 1);

		// Paint right-side 6 symbol stripes
		x += PaintStripes(lines,g,lineWidth,x,drawBounds.y,drawBounds.height - 12);
		
		// Paint right 'guard bars', always same '101'
		x += PaintStripes("101",g,lineWidth,x,drawBounds.y,drawBounds.height);
	}
	
	
	private static String EAN_NumberToStripes(String parity,char number)
	{
		String lines = "";

		if(parity.toLowerCase().equals("odd")){
			switch(number)
			{
				case '0':
					lines += "0001101";
					break;
				case '1':
					lines += "0011001";
					break;
				case '2':
					lines += "0010011";
					break;
				case '3':
					lines += "0111101";
					break;
				case '4':
					lines += "0100011";
					break;
				case '5':
					lines += "0110001";
					break;
				case '6':
					lines += "0101111";
					break;
				case '7':
					lines += "0111011";
					break;
				case '8':
					lines += "0110111";
					break;
				case '9':
					lines += "0001011";
					break;
			}
		}
		// Even parity
		else if(parity.toLowerCase().equals("even")){
			switch(number)
			{
				case '0':
					lines += "0100111";
					break;
				case '1':
					lines += "0110011";
					break;
				case '2':
					lines += "0011011";
					break;
				case '3':
					lines += "0100001";
					break;
				case '4':
					lines += "0011101";
					break;
				case '5':
					lines += "0111001";
					break;
				case '6':
					lines += "0000101";
					break;
				case '7':
					lines += "0010001";
					break;
				case '8':
					lines += "0001001";
					break;
				case '9':
					lines += "0010111";
					break;
			}
		}
		else if(parity.toLowerCase().equals("character")){
			switch(number)
			{
				case '0':
					lines += "1110010";
					break;
				case '1':
					lines += "1100110";
					break;
				case '2':
					lines += "1101100";
					break;
				case '3':
					lines += "1000010";
					break;
				case '4':
					lines += "1011100";
					break;
				case '5':
					lines += "1001110";
					break;
				case '6':
					lines += "1010000";
					break;
				case '7':
					lines += "1000100";
					break;
				case '8':
					lines += "1001000";
					break;
				case '9':
					lines += "1110100";
					break;
			}
		}

		return lines;
	}
	
	/**
	 * Checks EAN checksum.
	 * @param barCode Barcode value.
	 * @return Returns true if barcode is ok.
	 */
	private static boolean Check_EAN_CheckSum(String barCode)
	{						
		char[] symbols = barCode.toCharArray();

		//--- Check barcode checksum --------------------------------------------------------//
		int checkSum = Integer.parseInt(String.valueOf(symbols[symbols.length - 1]));
		int calcSum  = 0;
		boolean one_three = false;
		for(int i=(barCode.length()-2);i>-1;i--){
			if(one_three){
				calcSum += (Integer.parseInt(String.valueOf(symbols[i])) * 1);
				one_three = false;
			}
			else{
				calcSum += (Integer.parseInt(String.valueOf(symbols[i])) * 3);
				one_three = true;
			}				
		}
	
		char[] calcSumChar = String.valueOf(calcSum).toCharArray();
		int neededForDiv10 = 10 - Integer.parseInt(String.valueOf(calcSumChar[calcSumChar.length - 1]));
		
		// If the sum calculated is evenly disivisible by 10, the check digit is "0" (not 10). 
		if(neededForDiv10 != 10 && checkSum != neededForDiv10){
			return false;
	    }
		//------------------------------------------------------------------------------------//

		return true;
	}
	
	
	/**
	 * Paints stripes from string. String must contain only 0 or 1 values.
	 * 1 is black stripe, 0 is white stripe.
	 * @param stripes Srtipes string.
	 * @param g Graphics to paint.
	 * @param stripeWidth One stripe width.
	 * @param x X start position.
	 * @param y Y position.
	 * @param height Stripe height.
	 * @return
	 */
	private static int PaintStripes(String stripes,Graphics g,int stripeWidth,int x,int y,int height)
	{
		int paintedWidth = 0;
		char[] stripeArray = stripes.toCharArray();
		for(int i=0;i<stripeArray.length;i++){
			if(stripeArray[i] == '0'){
				g.setColor(Color.WHITE);
				g.drawLine(x,y,x,y + height);
			}
			else{
				g.setColor(Color.BLACK);
				g.drawLine(x,y,x,y + height);
			}
			x += stripeWidth;
			paintedWidth += stripeWidth;
		}

		return paintedWidth;
	}
	
	
	/**
	 * Gets barCode value.
	 * @return
	 */
	public String getBarCode()
	{
		return m_BarCode;
	}
	/**
	 * Sets barCode value.
	 * @param value
	 */
	public void setBarCode(String value)
	{
		if(!m_BarCode.equals(value)){
			m_BarCode = value;
			
			this.repaint();
		}
	}
}
