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

  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.

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


#include "RbmComponent.h"



//==============================================================================
RbmComponent::RbmComponent (Weights &_weights, RbmComponent *pRbmUpper, RbmComponentListener &listener)
	: Rbm(_weights, pRbmUpper->getHiddenBatch())
	, m_weights(_weights)
	, m_listener(listener)
	, m_currWeightIndexToDraw(0)
	, m_currTrainingIndexToDraw(0)
	, DrawTraining(nullptr)
	, DrawReconstruction(nullptr)
	, DrawWeights(nullptr)
	, DrawHidden(nullptr)
{
	pRbmUpper->registerRbm(this);
	size_t vNumX = m_weights.getNumVisibleX();
	size_t vNumY = m_weights.getNumVisibleY();
	size_t hNum = m_weights.getNumHidden();

	addAndMakeVisible (DrawTraining = new DrawComponent (vNumX, vNumY));
	DrawTraining->setListener(this);

    addAndMakeVisible (DrawReconstruction = new DrawComponent (vNumX, vNumY));

	
    addAndMakeVisible (DrawWeights = new DrawComponent (getTopWeights().getNumVisibleX(), getTopWeights().getNumVisibleY(), 0.5, 0.5));
    addAndMakeVisible (DrawHidden = new DrawComponent (hNum, 1));
	DrawHidden->setListener(this);
	redrawWeights();
	redrawReconstruction();

    setSize (430, 130);
	resized();
}

RbmComponent::RbmComponent (Weights &_weights, MatrixXd const &batch, RbmComponentListener &listener)
	: Rbm(_weights, batch)
	, m_weights(_weights)
	, m_listener(listener)
	, m_currWeightIndexToDraw(0)
	, m_currTrainingIndexToDraw(0)
	, DrawTraining(nullptr)
	, DrawReconstruction(nullptr)
	, DrawWeights(nullptr)
	, DrawHidden(nullptr)
{

	size_t vNumX = m_weights.getNumVisibleX();
	size_t vNumY = m_weights.getNumVisibleY();
	size_t hNum = m_weights.getNumHidden();

	addAndMakeVisible (DrawTraining = new DrawComponent (vNumX, vNumY));
	DrawTraining->setListener(this);

    addAndMakeVisible (DrawReconstruction = new DrawComponent (vNumX, vNumY));

	
    addAndMakeVisible (DrawWeights = new DrawComponent (getTopWeights().getNumVisibleX(), getTopWeights().getNumVisibleY(), 0.5, 0.5));
    addAndMakeVisible (DrawHidden = new DrawComponent (hNum, 1));
	DrawHidden->setListener(this);
	redrawWeights();
	redrawReconstruction();

    setSize (430, 130);
	resized();

	MatrixXd m = MatrixXd::Random(4,3);
	MatrixXd m2 = MatrixXd::Zero(4,3);
	cout << "Rows = " << m.rows() << ", cols = " << m.cols() << endl;
	cout << "m = " << endl << m << endl;
	cout << "max = " << endl << m.rowwise().maxCoeff() << endl;
	cout << "m.colwise().sum() = " << endl << m.colwise().sum() << endl;

	for (MatrixXf::Index i=0; i < m.rows(); i++)
	{
		MatrixXf::Index j = 8;
		cout << "max = " << endl << m.row(i).maxCoeff(&j);
		cout << "at row = " << i << ", col = " << j << endl;
		m2(i, j) = m(i, j);
	}
	cout << "m2 = " << endl << m2 << endl;
}

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

    //[Destructor]. You can add your own custom destruction code here..
    DrawTraining = nullptr;
    DrawReconstruction = nullptr;
    DrawWeights = nullptr;
    DrawHidden = nullptr;
    //[/Destructor]
}

//==============================================================================
void RbmComponent::paint (Graphics& g)
{
    //[UserPrePaint] Add your own custom painting code here..
    //[/UserPrePaint]

    g.fillAll (Colours::white);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Sigma"),
                32, 306, 80, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Lambda"),
                120, 306, 80, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Num. epochs"),
                32, 354, 80, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Alpha"),
                120, 354, 80, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Sparsity"),
                208, 306, 80, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Sigma decay"),
                296, 306, 96, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Weight decay"),
                296, 354, 96, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Momentum"),
                208, 354, 80, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Sparsity Alpha"),
                408, 306, 96, 14,
                Justification::centred, true);

    g.setColour (Colours::black);
    g.setFont (Font (15.00f, Font::plain));
    g.drawText (TRANS("Weight init"),
                408, 354, 96, 14,
                Justification::centred, true);

    //[UserPaint] Add your own custom painting code here..
    //[/UserPaint]
}

void RbmComponent::resized()
{
	DrawTraining->setBoundsRelative(0, 0, 100./430, 100./130);
	DrawReconstruction->setBoundsRelative(110./430, 0, 100./430, 100./130);
    DrawWeights->setBoundsRelative (220./430, 0, 100./430, 100./130);
    DrawHidden->setBoundsRelative (0, 110./130, 430./430, 20./130);
}

void RbmComponent::mouseMove (const MouseEvent& e)
{
    //[UserCode_mouseMove] -- Add your code here...
    //[/UserCode_mouseMove]
}

void RbmComponent::mouseEnter (const MouseEvent& e)
{
    //[UserCode_mouseEnter] -- Add your code here...
    //[/UserCode_mouseEnter]
}

void RbmComponent::mouseExit (const MouseEvent& e)
{
    //[UserCode_mouseExit] -- Add your code here...
    //[/UserCode_mouseExit]
}

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

void RbmComponent::mouseDrag (const MouseEvent& e)
{
    //[UserCode_mouseDrag] -- Add your code here...
//	e.eventComponent->setCentrePosition(e.getPosition().x, e.getPosition().y);
	cout << "Mouse = " << e.x << "," << e.y << endl;
    //[/UserCode_mouseDrag]
}

void RbmComponent::mouseUp (const MouseEvent& e)
{
    //[UserCode_mouseUp] -- Add your code here...
    //[/UserCode_mouseUp]
}

void RbmComponent::mouseDoubleClick (const MouseEvent& e)
{
    //[UserCode_mouseDoubleClick] -- Add your code here...
    //[/UserCode_mouseDoubleClick]
}

void RbmComponent::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
{
    //[UserCode_mouseWheelMove] -- Add your code here...
    //[/UserCode_mouseWheelMove]
}

void RbmComponent::onProgressChanged()
{
//	mylog("Training %f %%\n", obj.getProgress()*100);
	upPass(DrawHidden->getData());
	redrawReconstruction();
	redrawWeights();
	m_listener.onProgressChanged((size_t)(100*getProgress() + 0.5));
}

void RbmComponent::onDraw(DrawComponent &obj)
{
	if (&obj == DrawHidden)
	{
		upPass(obj.getData());
	}
	if (&obj == DrawTraining)
	{
    	redrawReconstruction();
	}
}

void RbmComponent::redrawReconstruction()
{
	uint32_t i;

	downPass(DrawReconstruction->getData(), DrawTraining->getData());
	
	for (i=0; i < params().m_numGibbs-1; i++)
	{
		downPass(DrawReconstruction->getData(), DrawReconstruction->getData());
	}
	DrawReconstruction->DrawData();
}

MatrixXd RbmComponent::getConvolutedWeight(RowVectorXd const &w)
{
	if (upper)
	{
		MatrixXd wc = w * upper->getWeights().transpose() * 1.0/sqrt((double)w.cols());
		return upper->getConvolutedWeight(wc);
	}
	else
	{
		return w;
	}
	
}
void RbmComponent::redrawWeights()
{
	DrawWeights->getData() = getConvolutedWeight(m_weights.weights().col(m_currWeightIndexToDraw));
	DrawWeights->DrawData();
}

void RbmComponent::onParamsChanged()
{
	redrawWeights();
   	redrawReconstruction();
}

void RbmComponent::batchchanged()
{
	Rbm::updateHiddenBatch();
	if (lower)
	{
		lower->batchchanged();
	}
}

void RbmComponent::setWeightsIndex(size_t index)
{
	m_currWeightIndexToDraw = index;
	redrawWeights();
}

size_t RbmComponent::getWeightsIndex()
{
	return m_currWeightIndexToDraw;
}

void RbmComponent::setTrainingIndex(size_t index)
{
	if (getBatch().rows() > 0)
	{
		m_currTrainingIndexToDraw = index;
		DrawTraining->getData() = getBatch().row(index);
		DrawTraining->DrawData();
		
		redrawReconstruction();
		upPass(DrawHidden->getData());
	}
}

size_t RbmComponent::getTrainingIndex()
{
	return m_currTrainingIndexToDraw;
}

RowVectorXd const& RbmComponent::getTrainingData()
{
	return DrawTraining->getData();
}

Weights& RbmComponent::getTopWeights()
{
	if (upper)
	{
		return upper->getTopWeights();
	}
	else
	{
		return m_weights;
	}
}

MatrixXd const& RbmComponent::getWeights()
{
	return m_weights.weights();
}
//[/MiscUserCode]


//==============================================================================
