/*
  ==============================================================================

  This is an automatically generated GUI class created by the Introjucer!

  Be careful when adding custom code to these files, as only the code within
  the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
  and re-saved.

  Created with Introjucer version: 3.1.0

  ------------------------------------------------------------------------------

  The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
  Copyright 2004-13 by Raw Material Software Ltd.

  ==============================================================================
*/

//[Headers] You can add your own extra header files here...
//[/Headers]

#include "GraphComponent.h"


//[MiscUserDefs] You can add your own user definitions and misc code here...
//[/MiscUserDefs]

//==============================================================================
GraphComponent::GraphComponent ()
    : m_maxNumPoints(1024)
	, m_maxNumCurves(2)
	, m_graphMinX(-1.0)
	, m_graphMinY(-1.0)
	, m_graphMaxX(1.0)
	, m_graphMaxY(1.0)
	, m_tickStepX(0.1)
	, m_tickStepY(0.1)
{

    //[UserPreSize]
	m_pCurves = new Curve[m_maxNumCurves];
	for (int i=0; i < m_maxNumCurves; i++)
	{
		m_pCurves[i].alloc(m_maxNumPoints);
	}
    //[/UserPreSize]

    setSize (400, 400);


    //[Constructor] You can add your own custom stuff here..
    //[/Constructor]
}

GraphComponent::~GraphComponent()
{
    //[Destructor_pre]. You can add your own custom destruction code here..
    //[/Destructor_pre]



    //[Destructor]. You can add your own custom destruction code here..
	delete [] m_pCurves;
    //[/Destructor]
}

//==============================================================================
void GraphComponent::paint (Graphics& g)
{
    //[UserPrePaint] Add your own custom painting code here..
    juce::Point<float> p;
	juce::Colour c;
    //[/UserPrePaint]

    g.fillAll (Colours::black);

    //[UserPaint] Add your own custom painting code here..
	g.setColour (juce::Colours::white);
	g.setOpacity(0.25);
	drawMainTicks(g);

	g.setOpacity(1.00);
	g.setColour (juce::Colours::limegreen);
	drawCurve(g, 0);

	g.setOpacity(1.00);
	g.setColour (juce::Colours::red);
	drawCurve(g, 1);

	g.setColour (juce::Colours::white);
	g.setOpacity(1.0);
	drawBorder(g);

//	g.setColour (juce::Colours::limegreen);
//	g.setOpacity(0.5);
//	drawReference(g, 4, 1.0, 11);

    //[/UserPaint]
}

void GraphComponent::resized()
{
    //[UserResized] Add your own custom resize handling here..
	m_screenWidth = 3*getWidth()/4;
	m_screenHeight = 3*getHeight()/4;
	m_screenOffsetX = (getWidth()-m_screenWidth)/2;
	m_screenOffsetY = (getHeight()-m_screenHeight)/2;
    //[/UserResized]
}

void GraphComponent::mouseDown (const MouseEvent& e)
{
    //[UserCode_mouseDown] -- Add your code here...
	(void)e;
    //[/UserCode_mouseDown]
}



//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
void GraphComponent::receiverStatusChanged(ReceiverInterface *pReceiver)
{
	uint32_t i, j;

	float minX = 1000;
	float maxX = -1000;
	float minY = 1000;
	float maxY = -1000;

	j = 0;
	{
		const CVec &w = pReceiver->getWeightsCMA();
		for (i=0; i < w.size(); i++)
		{
			m_pCurves[j].m_pPointArray[i].x = (float)i;
			m_pCurves[j].m_pPointArray[i].y = abs(w(i));

			if (m_pCurves[j].m_pPointArray[i].y < minY)
			{
				minY = m_pCurves[j].m_pPointArray[i].y;
			}
			if (m_pCurves[j].m_pPointArray[i].y > maxY)
			{
				maxY = m_pCurves[j].m_pPointArray[i].y;
			}
		}
		m_pCurves[j].m_numPoints = w.size();
		maxX = (float)(w.size()-1);
	}

	j = 1;
	{
		const CVec &w = pReceiver->getWeightsDFE();
		for (i=0; i < w.size(); i++)
		{
			m_pCurves[j].m_pPointArray[i].x = (float)i;
			m_pCurves[j].m_pPointArray[i].y = abs(w(i));

			if (m_pCurves[j].m_pPointArray[i].y < minY)
			{
				minY = m_pCurves[j].m_pPointArray[i].y;
			}
			if (m_pCurves[j].m_pPointArray[i].y > maxY)
			{
				maxY = m_pCurves[j].m_pPointArray[i].y;
			}
		}
		m_pCurves[j].m_numPoints = w.size();

		minY = 0;
		minX = 0;
		maxY = ceil(maxY);
		if (w.size() > maxX)
		{
			maxX = (float)(w.size()-1);
		}
	}

	setGraphBounds(minX, maxX, minY, maxY);
	repaint();
}

void GraphComponent::setGraphBounds(float minX, float maxX, float minY, float maxY)
{
	m_graphMinX = minX;
	m_graphMaxX = maxX;
	m_graphMinY = minY;
	m_graphMaxY = maxY;
	m_tickStepX = (maxX - minX)/10;
	m_tickStepY = (maxY - minY)/10;
}

float GraphComponent::getScreenCoordX(float x)
{
	if (isnan(x) || isinf(x))
		return 0;

	if ((x > m_graphMaxX) || (x < m_graphMinX))
		return 0;

	return m_screenOffsetX + (float)(m_screenWidth*(x-m_graphMinX)/(m_graphMaxX-m_graphMinX));
}

float GraphComponent::getScreenCoordY(float y)
{
	if (isnan(y) || isinf(y))
		return 0;

	if ((y > m_graphMaxY) || (y < m_graphMinY))
		return 0;

	return m_screenOffsetY + (float)(m_screenHeight*(1-(y-m_graphMinY)/(m_graphMaxY-m_graphMinY)));
}

void GraphComponent::drawCurve(Graphics& g, uint32_t index)
{
	uint32_t i;
	float x0, y0;
	float x1, y1;

	for (i=1; i < m_pCurves[index].m_numPoints; i++)
	{
		x0 = getScreenCoordX(m_pCurves[index].m_pPointArray[i-1].x);
		y0 = getScreenCoordY(m_pCurves[index].m_pPointArray[i-1].y);
		x1 = getScreenCoordX(m_pCurves[index].m_pPointArray[i].x);
		y1 = getScreenCoordY(m_pCurves[index].m_pPointArray[i].y);

		g.drawLine(x0, y0, x1, y1, 1.f);
	}
}

void GraphComponent::drawBorder(Graphics& g)
{
	g.drawVerticalLine((int)getScreenCoordX(m_graphMinX), getScreenCoordY(m_graphMaxY), getScreenCoordY(m_graphMinY));
	g.drawVerticalLine((int)getScreenCoordX(m_graphMaxX), getScreenCoordY(m_graphMaxY), getScreenCoordY(m_graphMinY));
	g.drawHorizontalLine((int)getScreenCoordY(m_graphMinY), getScreenCoordX(m_graphMinX), getScreenCoordX(m_graphMaxX));
	g.drawHorizontalLine((int)getScreenCoordY(m_graphMaxY), getScreenCoordX(m_graphMinX), getScreenCoordX(m_graphMaxX));
}

void GraphComponent::drawMainTicks(Graphics& g)
{
	float pos;

	pos = m_graphMinX;
	while(pos < m_graphMaxX)
	{
		pos += m_tickStepX;
		g.drawVerticalLine((int)getScreenCoordX(pos), getScreenCoordY(m_graphMaxY), getScreenCoordY(m_graphMinY));
	}

	pos = m_graphMinY;
	while(pos < m_graphMaxY)
	{
		pos += m_tickStepY;
		g.drawHorizontalLine((int)getScreenCoordY(pos), getScreenCoordX(m_graphMinX), getScreenCoordX(m_graphMaxX));
	}
}

void GraphComponent::drawReference(Graphics& g, uint32_t numConst, float radius, float size)
{
	float step;
	float s;
	float i, q;
	float x, y;
	uint32_t xx, yy;
	uint32_t N;

	N = (uint32_t)(sqrt((float)numConst));
	s = (float)(radius/sqrt(2));

	step = 2*s/(N-1);

	i = -s;
	xx = N;
	while(xx--)
	{
		q = -s;
		yy = N;
		while(yy--)
		{
			x = getScreenCoordX(i);
			y = getScreenCoordY(q);
			g.drawEllipse(x-size/2, y-size/2, size, size, 1.5);
			q += step;
		}
		i += step;
	}
}

void GraphComponent::drawCross(Graphics& g, juce::Point<float> point, float size)
{
	float x, y;

	x = getScreenCoordX(point.getX());
	y = getScreenCoordY(point.getY());

	g.drawLine(x-size/2, y+size/2, x+size/2, y-size/2, 1.f);
	g.drawLine(x+size/2, y+size/2, x-size/2, y-size/2, 1.f);
}

void GraphComponent::drawCircle(Graphics& g, juce::Point<float> point, float size)
{
	float x, y;

	x = getScreenCoordX(point.getX());
	y = getScreenCoordY(point.getY());

	g.drawEllipse(x-size/2, y-size/2, size, size, 1.5);
}

//[/MiscUserCode]


//==============================================================================
//[EndFile] You can add extra defines here...
//[/EndFile]
