package chromaticTuner.tuner;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public final class TunerDisplay extends SurfaceView implements Runnable {
	private static final int HIGH_COLOR = Color.rgb(150,0,0);
	private static final int OK_COLOR = Color.rgb(0,150,0);
	private static final int BELOW_MIN_COLOR = Color.rgb(0,0,150);
	private static final int AUDIO_LEVEL_OK = -10;
	private static final int NUM_LEDS_PER_SIDE = 20;
	private static final int NUM_LED_MARGIN = 2;
	private Paint m_backgroundPaint = null;
	private Paint m_textPaint = null;
	private Paint m_gridPaint = null;
	private Paint m_indicatorPaint = null;
	private Bitmap m_background = null;
	private int m_backgroundColor=Color.rgb(0, 0, 0);
	private int m_coverColor=Color.rgb(240, 240, 240);
	private int m_ledOffColor=m_backgroundColor;
	private int m_ledOnColor=Color.rgb(170, 40, 40);
	private int m_note=-1;
	private Paint m_gridSquarePaint = null;
	private int m_gridSquareColor = Color.rgb(15,15,15);
	
	private final float m_drawSteps = 4f;
	private boolean m_dirty = true;
	private long m_redrawSleep = 35;

	private int m_inTuneColor=Color.rgb(40, 170, 40);
	private int m_outOfTuneColor=Color.rgb(170, 40, 40);
	private int m_cents=0;
	private int m_drawingCents=0;
	private float m_centsDrawStepSize=0f;

	private float m_minimumLevel=-96.0f;
	
	private float m_borderWidth = 0.025f;
	
	private float m_displayWidth = 0.0f;
	private float m_displayHeight = 0.0f;
	
	private final float m_backroundRoundingDivisor = 6000f;
	private final float m_ledWidth = (0.70f/NUM_LEDS_PER_SIDE);
	private final float m_ledMargin = ((1f-m_ledWidth*NUM_LEDS_PER_SIDE)/(NUM_LEDS_PER_SIDE+1));
	
	private final float m_indicatorWidth = 0.85f;
	private final float m_indicatorLeft = (1f-m_indicatorWidth)/2f;
	private final float m_indicatorHeight = 0.17f;
	private final float m_indicatorTop = m_borderWidth*2;
	
	private final float m_levelHeight = m_indicatorHeight;

	private final float m_gridWidth = 0.85f;
	private final float m_gridLeft = (1f-m_gridWidth)/2f;
	private final float m_gridTop = m_indicatorTop+m_indicatorHeight+m_borderWidth;
	private final float m_gridHeight=1f-m_indicatorTop-m_indicatorHeight-4*m_borderWidth-m_levelHeight;
	private float m_verticalMarg=0.0f;
	private float m_horizontalMarg=0.0f;
	private float m_backgroundHeight = (1-2*m_verticalMarg);
	private float m_backgroundWidth = (1-2*m_horizontalMarg);
	private float m_gridRectHeight = m_backgroundHeight * m_ledWidth;
	private float m_gridRectWidth = m_backgroundWidth * m_ledWidth;
	private float m_gridMarginV = m_backgroundHeight * m_ledMargin;
	private float m_gridMarginH = m_backgroundWidth * m_ledMargin;
	RectF m_gridBackgroundRect = new RectF(m_horizontalMarg,m_verticalMarg,1f-m_horizontalMarg,1f-m_verticalMarg);
	private boolean m_gridSizeCalculated = false;
	float m_maxGreaterGridDimension = 1.5f;

	private final float m_levelLeft = m_indicatorLeft;
	private final float m_levelTop = m_gridTop+m_gridHeight+m_borderWidth;
	private final float m_levelWidth = m_indicatorWidth;
	private final float m_textSize = 0.35f;
	private final float m_textX = 0.5f;
	private final float m_textY = 0.62f;
	private final float m_textWidthScale = 1.2f;

	private final float m_indicatorOutOfTuneVerticalMargin = 0.025f;
	private final float m_outOfTuneRectXRad = 0.025f;
	private final float m_outOfTuneRectYRad = 0.1f;

	private RectF m_outerBackgroundRect = new RectF(0.0f,0.0f,1.0f,1.0f);
	private RectF m_innerBackgroundRect = new RectF(m_borderWidth,m_borderWidth,1f-m_borderWidth,1f-m_borderWidth);
	private RectF m_gridRect = new RectF();
	private RectF m_fullRect = new RectF(0f,0f,1f,1f);
	private RectF m_inTuneRect = new RectF();
	private RectF m_outOfTuneRect = new RectF();

	private boolean m_running = false;
	private SurfaceHolder m_surfaceHolder = null;
	private Thread m_thread = null;

	private Path m_clipPath = null;

	private int m_regularHorizontalOffset = 4;
	private int m_sharpHorizontalOffset = 0;
	private int m_drawnBackgrounds = 0;
	private int m_previousRenderedCanvasWidth = -1;
	private int m_previousRenderedCanvasHeight = -1;
	enum AudioLevel {
		BELOW_MIN,
		OK,
		HIGH
	}
	private AudioLevel m_currentLevel = AudioLevel.BELOW_MIN;

	public TunerDisplay(Context context) {
		super(context);
		m_surfaceHolder = getHolder();
		initDrawingTools();
	}
	
	public TunerDisplay(Context context, AttributeSet attrs) {
		super(context, attrs);
		m_surfaceHolder = getHolder();
		initDrawingTools();
	}

	public TunerDisplay(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		m_surfaceHolder = getHolder();
		initDrawingTools();
	}
	
	@Override
	public void run() {
		while(m_running){
		    if(m_surfaceHolder.getSurface().isValid()){
		     updateDrawingValues();
		     if (m_dirty)
		     {
			     Canvas canvas = m_surfaceHolder.lockCanvas();
			     drawDisplay(canvas);
			     m_surfaceHolder.unlockCanvasAndPost(canvas);
			     m_dirty=false;
		     }
		     try {
				Thread.sleep(m_redrawSleep);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		    }
		}
	}

	public void onResumeDisplay(){
		if (m_running)
		{
			m_drawnBackgrounds=0;
			return;
		}
		m_running = true;
		m_drawnBackgrounds=0;
		m_thread = new Thread(this);
		m_thread.start();
	}

	public void onPauseDisplay(){
		if (!m_running)
		{
			return;
		}
		boolean retry = true;
		m_running = false;
		while(retry)
		{
			try
			{
				m_thread.join();
				retry = false;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public void setNote(int note)
	{
		if (note<0)
		{
			m_note=note;
		}
		else
		{
			m_note=note % 12;	
		}
		m_dirty=true;
	}
	
	public void erase()
	{
		setNote(-1);
		setCents(Integer.MIN_VALUE);
	}
	
	public void setCents(int cents)
	{
		m_cents=cents;
		if (m_cents>Integer.MIN_VALUE)
		{
			if (m_drawingCents==Integer.MIN_VALUE)
			{
				m_drawingCents=m_cents;
				m_centsDrawStepSize=0;
			}
			else
			{
				m_centsDrawStepSize=(m_cents-m_drawingCents)/m_drawSteps;
			}
		}
		else
		{
			m_centsDrawStepSize=0.0f;
			m_drawingCents=Integer.MIN_VALUE;
		}
		m_dirty=true;
	}
	
	public void setMinimumInputLevel(float minimumLevel)
	{
		if (minimumLevel<0.0)
		{
			m_minimumLevel=minimumLevel;
		}
		else
		{
			m_minimumLevel=-0.0001f;
		}
		m_dirty=true;
	}
	
	public void setAudioLevel(float audioLevel)
	{
		if (audioLevel<m_minimumLevel)
		{
			m_currentLevel = AudioLevel.BELOW_MIN;
		}
		else if (audioLevel < AUDIO_LEVEL_OK)
		{
			m_currentLevel = AudioLevel.OK;
		}
		else
		{
			m_currentLevel = AudioLevel.HIGH;
		}
		m_dirty=true;
	}

	private void initDrawingTools() {
		initMatrix();
		m_backgroundPaint = new Paint();
		m_backgroundPaint.setFilterBitmap(true);
		m_backgroundPaint.setAntiAlias(false);
		m_backgroundPaint.setAlpha(255);

		m_textPaint = new Paint();
		m_textPaint.setTextAlign(Align.CENTER);
		m_textPaint.setTextScaleX(m_levelHeight/m_levelWidth*m_textWidthScale);
		m_textPaint.setTextSize(m_textSize);
		m_textPaint.setAntiAlias(false);

		m_gridPaint = new Paint();
		m_gridPaint.setAlpha(255);
		m_gridPaint.setAntiAlias(false);
		m_gridPaint.setColor(m_ledOffColor);

		m_indicatorPaint = new Paint();
		m_indicatorPaint.setAlpha(255);
		m_indicatorPaint.setAntiAlias(false);

		m_clipPath = new Path();

		m_gridSquarePaint = new Paint();
		m_gridSquarePaint.setColor(m_gridSquareColor);
		m_gridSizeCalculated=false;
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		
		setMeasuredDimension(widthSize, heightSize);
	}
	
	private void drawBackground(Canvas canvas) {
		if (m_background == null) {
			//System.out.println("Background not created");
		} else {
			m_backgroundPaint.setColor(m_coverColor);
			canvas.drawRoundRect(m_outerBackgroundRect, m_displayHeight/m_backroundRoundingDivisor, m_displayWidth/m_backroundRoundingDivisor, m_backgroundPaint);
			m_backgroundPaint.setColor(m_backgroundColor);
			canvas.drawRoundRect(m_innerBackgroundRect,
					(m_displayHeight/m_backroundRoundingDivisor)*(1-m_borderWidth), (m_displayWidth/m_backroundRoundingDivisor)*(1-m_borderWidth),
					m_backgroundPaint);
		}
	}

	private void drawDisplay(Canvas canvas) {
		if (canvas.getHeight() != m_previousRenderedCanvasHeight ||
				canvas.getWidth() != m_previousRenderedCanvasWidth)
		{
			m_drawnBackgrounds = 0;
		}
		canvas.save(Canvas.MATRIX_SAVE_FLAG);
		canvas.scale(m_displayWidth, m_displayHeight);
		if (m_drawnBackgrounds<2)
		{
			drawBackground(canvas);
		}
		
		canvas.save();
		canvas.translate(m_indicatorLeft, m_indicatorTop);
		canvas.scale(m_indicatorWidth, m_indicatorHeight);
		drawIndicator(canvas);
		canvas.restore();
		
		canvas.save();
		canvas.translate(m_gridLeft, m_gridTop);
		canvas.scale(m_gridWidth, m_gridHeight);
		canvas.translate(0.05f, 0.05f);
		canvas.scale(0.90f, 0.90f);
		drawGrid(canvas);
		canvas.restore();
		
		canvas.save();
		canvas.translate(m_levelLeft, m_levelTop);
		canvas.scale(m_levelWidth, m_levelHeight);
		drawInputLevel(canvas);
		canvas.restore();
		
		canvas.restore();

		if (m_drawnBackgrounds<2)
		{
			m_drawnBackgrounds++;
		}
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		m_displayWidth=getWidth();
		m_displayHeight=getHeight();
		regenerateBackground();
		m_drawnBackgrounds=0;
	}
	
	private void regenerateBackground() {
		// free the old bitmap
		if (m_background != null) {
			m_background.recycle();
		}
		
		m_background = Bitmap.createBitmap((int)m_displayWidth, (int)m_displayHeight, Bitmap.Config.ARGB_8888);
		Canvas backgroundCanvas = new Canvas(m_background);
		backgroundCanvas.drawColor(m_backgroundColor);
		backgroundCanvas.scale(m_displayWidth, m_displayHeight);
		m_gridSizeCalculated=false;
	}

	private void updateDrawingValues()
	{
		if(m_drawingCents>Integer.MIN_VALUE && m_cents>Integer.MIN_VALUE && Math.abs(m_drawingCents-m_cents)>1)
		{
			m_drawingCents+=m_centsDrawStepSize;
			m_dirty=true;
		}
		else
		{
			m_centsDrawStepSize=0.0f;
			m_drawingCents=m_cents;
		}
	}
	
	private void calculateGridSizes(Canvas canvas)
	{
		float height = canvas.getHeight()*m_gridHeight;
		float width = canvas.getWidth()*m_gridWidth;
		m_verticalMarg=0.0f;
		m_horizontalMarg=0.0f;
		if (height==width)
		{
			;
		}
		else if (height>width)
		{
			m_verticalMarg= (height-width)/ (2.0f*m_maxGreaterGridDimension) / height;
		}
		else
		{
			m_horizontalMarg= (width-height) / (2.0f*m_maxGreaterGridDimension) / width;
		}
		m_backgroundHeight = (1-2*m_verticalMarg);
		m_backgroundWidth = (1-2*m_horizontalMarg);
		m_gridRectHeight = m_backgroundHeight * m_ledWidth;
		m_gridRectWidth = m_backgroundWidth * m_ledWidth;
		m_gridMarginV = m_backgroundHeight * m_ledMargin;
		m_gridMarginH = m_backgroundWidth * m_ledMargin;
		m_gridRect.set(0, 0, m_gridRectWidth, m_gridRectHeight);
		m_gridBackgroundRect = new RectF(m_horizontalMarg,m_verticalMarg,1f-m_horizontalMarg,1f-m_verticalMarg);
	}

	private void drawGrid(Canvas canvas)
	{
		if (!m_gridSizeCalculated)
		{
			calculateGridSizes(canvas);
			m_gridSizeCalculated=true;
		}
		canvas.save(Canvas.MATRIX_SAVE_FLAG);
		float gridInnerLeft;
		float gridInnerTop = m_verticalMarg + m_gridMarginV;
		canvas.drawRect(m_fullRect, m_backgroundPaint);
		for (int y=0;y<NUM_LEDS_PER_SIDE;y++)
		{
			gridInnerLeft=m_horizontalMarg + m_gridMarginH;
			if (y>=NUM_LED_MARGIN && y<NUM_LEDS_PER_SIDE-NUM_LED_MARGIN)
			{
				for (int x=0;x<NUM_LEDS_PER_SIDE;x++)
				{
					if (x>=NUM_LED_MARGIN && x<NUM_LEDS_PER_SIDE-NUM_LED_MARGIN)
					{
						m_gridPaint.setColor(getLedColor(y,x));
						m_gridRect.set(gridInnerLeft, gridInnerTop, gridInnerLeft+m_gridRectWidth, gridInnerTop+m_gridRectHeight);
						if (m_gridPaint.getColor()==m_ledOnColor) {
							canvas.drawRect(m_gridRect, m_gridPaint);
						}
					}
					gridInnerLeft+=m_gridRectWidth+m_gridMarginH;
				}
			}
			gridInnerTop+=m_gridRectHeight+m_gridMarginV;
		}
		canvas.restore();
	}
	
	private void drawIndicator(Canvas canvas)
	{
		canvas.drawRect(m_fullRect, m_backgroundPaint);
		float rectSide = 0.01f;
		float outOfTuneCenter = (float)(0.5f+m_drawingCents*0.01f);
		float top = 0.01f;
		float bottom = 0.99f;
		m_inTuneRect.set((0.5f-rectSide*0.5f), top, (0.5f+rectSide*0.5f), bottom);
		canvas.save(Canvas.MATRIX_SAVE_FLAG);
		if (Math.abs(m_drawingCents)<=3 && m_drawingCents>-100)
		{
			// draw in tune
			m_indicatorPaint.setColor(m_inTuneColor);
		}
		else
		{
			m_indicatorPaint.setColor(Color.WHITE);
		}
		canvas.drawRect(m_inTuneRect, m_indicatorPaint);
		canvas.restore();

		if (m_drawingCents < -99 || m_drawingCents > 99)
		{
			return;
		}

		if (Math.abs(m_drawingCents)>2)
		{
			// draw out of tune
			canvas.save(Canvas.MATRIX_SAVE_FLAG);
			m_indicatorPaint.setColor(m_outOfTuneColor);
			RectF m_outOfTuneClipRect = new RectF();
			if (m_drawingCents<0)
			{
				m_outOfTuneRect.set(outOfTuneCenter-rectSide*0.5f, top+m_indicatorOutOfTuneVerticalMargin,
						1.0f, bottom-m_indicatorOutOfTuneVerticalMargin);
				m_outOfTuneClipRect.set(0.0f, top, m_inTuneRect.left, bottom);
			}
			else
			{
				m_outOfTuneRect.set(0.0f, top+m_indicatorOutOfTuneVerticalMargin,
						outOfTuneCenter+rectSide*0.5f, bottom-m_indicatorOutOfTuneVerticalMargin);
				m_outOfTuneClipRect.set(m_inTuneRect.right, top, 1.0f, bottom);
			}

			m_clipPath.reset();
			m_clipPath.addRect(m_outOfTuneClipRect, Path.Direction.CCW);
			canvas.clipPath(m_clipPath);
			canvas.drawRoundRect(m_outOfTuneRect, m_outOfTuneRectXRad, m_outOfTuneRectYRad, m_indicatorPaint);
			canvas.restore();
		}	
	}
	
	private void drawInputLevel(Canvas canvas)
	{
		canvas.save(Canvas.MATRIX_SAVE_FLAG);
		int levelColor;

		if (m_currentLevel==AudioLevel.BELOW_MIN)
		{
			levelColor=BELOW_MIN_COLOR;
		}
		else if (m_currentLevel==AudioLevel.OK)
		{
			levelColor=OK_COLOR;
		}
		else
		{
			levelColor=HIGH_COLOR;
		}

		m_textPaint.setColor(levelColor);
		canvas.drawText("Input Level", m_textX, m_textY, m_textPaint);
		canvas.restore();
	}
	
	private int getLedColor(int y, int x)
	{
		switch (m_note)
		{
			case NoteCalculator.Gs:
				if (m_noteGsMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.A:
				if (m_noteAMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.As:
				if (m_noteAsMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.B:
				if (m_noteBMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.C:
				if (m_noteCMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.Cs:
				if (m_noteCsMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.D:
				if (m_noteDMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.Ds:
				if (m_noteDsMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.E:
				if (m_noteEMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.F:
				if (m_noteFMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.Fs:
				if (m_noteFsMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			case NoteCalculator.G:
				if (m_noteGMatrix[y][x]) {return m_ledOnColor;} return m_ledOffColor;
			
			default:
				return m_ledOffColor;
		}
	}
	
	private void initMatrix()
	{
		// A
		clearMatrix(m_noteAMatrix);
		a(m_noteAMatrix, m_regularHorizontalOffset);
		// A#
		clearMatrix(m_noteAsMatrix);
		a(m_noteAsMatrix, m_sharpHorizontalOffset);
		sharp(m_noteAsMatrix);
		// B
		clearMatrix(m_noteBMatrix);
		b(m_noteBMatrix, m_regularHorizontalOffset);
		// C
		clearMatrix(m_noteCMatrix);
		c(m_noteCMatrix, m_regularHorizontalOffset);
		// C#
		clearMatrix(m_noteCsMatrix);
		c(m_noteCsMatrix, m_sharpHorizontalOffset);
		sharp(m_noteCsMatrix);
		// D
		clearMatrix(m_noteDMatrix);
		d(m_noteDMatrix, m_regularHorizontalOffset);
		// D#
		clearMatrix(m_noteDsMatrix);
		d(m_noteDsMatrix, m_sharpHorizontalOffset);
		sharp(m_noteDsMatrix);
		// E
		clearMatrix(m_noteEMatrix);
		topHorizontal(m_noteEMatrix, m_regularHorizontalOffset);
		centerHorizontalShort(m_noteEMatrix, m_regularHorizontalOffset);
		bottomHorizontal(m_noteEMatrix, m_regularHorizontalOffset);
		leftVertical(m_noteEMatrix, m_regularHorizontalOffset);
		// F
		clearMatrix(m_noteFMatrix);
		topHorizontal(m_noteFMatrix, m_regularHorizontalOffset);
		centerHorizontalShort(m_noteFMatrix, m_regularHorizontalOffset);
		leftVertical(m_noteFMatrix, m_regularHorizontalOffset);
		// F#
		clearMatrix(m_noteFsMatrix);
		topHorizontal(m_noteFsMatrix, m_sharpHorizontalOffset);
		centerHorizontalShort(m_noteFsMatrix, m_sharpHorizontalOffset);
		leftVertical(m_noteFsMatrix, m_sharpHorizontalOffset);
		sharp(m_noteFsMatrix);
		// G
		clearMatrix(m_noteGMatrix);
		g(m_noteGMatrix, m_regularHorizontalOffset);
		// G#
		clearMatrix(m_noteGsMatrix);
		g(m_noteGsMatrix, m_sharpHorizontalOffset);
		sharp(m_noteGsMatrix);
	}
	
	private void clearMatrix(boolean[][] matrix)
	{
		for (int i=0;i<20;i++)
		{
			for (int j=0;j<20;j++)
			{
				matrix[i][j]=false;
			}
		}
	}
	
	private void leftVertical(boolean[][] matrix, int horizontalOffset)
	{
		for (int i=2;i<18;i++)
		{
			matrix[i][2+horizontalOffset]=true;
		}
	}
	
	private void topHorizontal(boolean[][] matrix, int horizontalOffset)
	{
		for (int i=2+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[2][i]=true;
		}
	}
	
	private void bottomHorizontal(boolean[][] matrix, int horizontalOffset)
	{
		for (int i=2+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[17][i]=true;
		}
	}
	
	private void centerHorizontalShort(boolean[][] matrix, int horizontalOffset)
	{
		for (int i=2+horizontalOffset;i<7+horizontalOffset;i++)
		{
			matrix[9][i]=true;
		}	
	}
	
	private void a(boolean[][] matrix, int horizontalOffset)
	{
		int i;
		int j=5+horizontalOffset;
		for (i=2;i<6;i++)
		{
			matrix[i][j]=true;
			j--;
		}
		j=6+horizontalOffset;
		for (i=3;i<6;i++)
		{
			matrix[i][j]=true;
			j++;
		}
		
		for (i=6;i<18;i++)
		{
			matrix[i][2+horizontalOffset]=true;
		}
		
		for (i=6;i<18;i++)
		{
			matrix[i][8+horizontalOffset]=true;
		}
		
		for (j=3+horizontalOffset;j<8+horizontalOffset;j++)
		{
			matrix[10][j]=true;
		}
	}
	
	private void b(boolean[][] matrix, int horizontalOffset)
	{
		leftVertical(matrix, horizontalOffset);
		int i, j;
		// top
		for (i=3+horizontalOffset;i<6+horizontalOffset;i++)
		{
			matrix[2][i]=true;
		}
		j=3;
		for (i=6+horizontalOffset;i<8+horizontalOffset;i++)
		{
			matrix[j][i]=true;
			j++;
		}
		for (i=3+horizontalOffset;i<6+horizontalOffset;i++)
		{
			matrix[10][i]=true;
		}
		j=9;
		for (i=6+horizontalOffset;i<8+horizontalOffset;i++)
		{
			matrix[j][i]=true;
			j--;
		}
		for (i=5;i<8;i++)
		{
			matrix[i][8+horizontalOffset]=true;
		}
		
		// bottom
		j=11;
		for (i=6+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[j][i]=true;
			j++;
		}
		
		for (i=3+horizontalOffset;i<6+horizontalOffset;i++)
		{
			matrix[17][i]=true;
		}
		j=17;
		for (i=6+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[j][i]=true;
			j--;
		}
		
		for (i=13;i<16;i++)
		{
			matrix[i][8+horizontalOffset]=true;
		}
		
	}
	
	private void c(boolean[][] matrix, int horizontalOffset)
	{
		int i=2, j=5+horizontalOffset;
		for (;j<9+horizontalOffset;j++)
		{
			matrix[i][j]=true;
		}
		i=2;j=4+horizontalOffset;
		for (;j>1+horizontalOffset;j--)
		{
			matrix[i][j]=true;
			i++;
		}
		i=5;
		for(;i<15;i++)
		{
			matrix[i][2+horizontalOffset]=true;
		}
		
		j=4+horizontalOffset;
		i=17;
		for (;j>1+horizontalOffset;j--)
		{
			matrix[i][j]=true;
			i--;
		}
		
		i=17;j=5+horizontalOffset;
		for (;j<9+horizontalOffset;j++)
		{
			matrix[i][j]=true;
		}
	}
	
	private void d(boolean[][] matrix, int horizontalOffset)
	{
		leftVertical(matrix, horizontalOffset);
		int i,j;
		for (i=3+horizontalOffset;i<6+horizontalOffset;i++)
		{
			matrix[2][i]=true;
		}
		j=3;
		for (i=6+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[j][i]=true;
			j++;
		}
		
		for (i=6;i<14;i++)
		{
			matrix[i][8+horizontalOffset]=true;
		}
		
		for (i=3+horizontalOffset;i<6+horizontalOffset;i++)
		{
			matrix[17][i]=true;
		}
		j=16;
		for (i=6+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[j][i]=true;
			j--;
		}
	}
	
	private void g(boolean[][] matrix, int horizontalOffset)
	{
		matrix[3][8+horizontalOffset]=true;
		int i=2, j=5+horizontalOffset;
		for (;j<8+horizontalOffset;j++)
		{
			matrix[i][j]=true;
		}
		i=2;j=4+horizontalOffset;
		for (;j>1+horizontalOffset;j--)
		{
			matrix[i][j]=true;
			i++;
		}
		i=5;
		for(;i<15;i++)
		{
			matrix[i][2+horizontalOffset]=true;
		}
		
		j=4+horizontalOffset;
		i=17;
		for (;j>1+horizontalOffset;j--)
		{
			matrix[i][j]=true;
			i--;
		}
		
		matrix[17][5+horizontalOffset]=true;
		matrix[17][6+horizontalOffset]=true;
		j=7+horizontalOffset;
		i=16;
		for (;j<9+horizontalOffset;j++)
		{
			matrix[i][j]=true;
			i--;
		}
		
		for (i=14;i>10;i--)
		{
			matrix[i][8+horizontalOffset]=true;
		}
		
		for (i=6+horizontalOffset;i<9+horizontalOffset;i++)
		{
			matrix[10][i]=true;
		}
	}
	
	private void sharp(boolean[][] matrix)
	{
		//left vertical		
		for (int i=6;i<14;i++)
		{
			matrix[i][13]=true;
		}

		//right vertical
		for (int i=6;i<14;i++)
		{
			matrix[i][16]=true;
		}
		
		//top horizontal
		for (int i=12;i<18;i++)
		{
			matrix[8][i]=true;
		}
		
		// bottom horizontal
		for (int i=12;i<18;i++)
		{
			matrix[11][i]=true;
		}
	}
	
	private boolean[][] m_noteAMatrix = new boolean[20][20];
	private boolean[][] m_noteAsMatrix = new boolean[20][20];
	private boolean[][] m_noteBMatrix = new boolean[20][20];
	private boolean[][] m_noteCMatrix = new boolean[20][20];
	private boolean[][] m_noteCsMatrix = new boolean[20][20];
	private boolean[][] m_noteDMatrix = new boolean[20][20];
	private boolean[][] m_noteDsMatrix = new boolean[20][20];
	private boolean[][] m_noteEMatrix = new boolean[20][20];
	private boolean[][] m_noteFMatrix = new boolean[20][20];
	private boolean[][] m_noteFsMatrix = new boolean[20][20];
	private boolean[][] m_noteGMatrix = new boolean[20][20];
	private boolean[][] m_noteGsMatrix = new boolean[20][20];

}
