//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"

#include "gui/editor/guiGraphCtrl.h"

IMPLEMENT_CONOBJECT(GuiGraphCtrl);

GuiGraphCtrl::GuiGraphCtrl()
{

   for(int i = 0; i < MaxPlots; i++)
   {
	   mPlots[i].mAutoPlot = NULL;
	   mPlots[i].mAutoPlotDelay = 0;
	   mPlots[i].mGraphColor = ColorF(1.0, 1.0, 1.0);
	   VECTOR_SET_ASSOCIATION(mPlots[i].mGraphData);
	   mPlots[i].mGraphMax = 1;
	   mPlots[i].mGraphType = Polyline;
	   for(int j = 0; j < MaxDataPoints; j++)
		   mPlots[i].mGraphData.push_front(0.0);
   }

   AssertWarn(MaxPlots == 6, "Only 6 plot colors initialized.  Update following code if you change MaxPlots.");

   mPlots[0].mGraphColor = ColorF(1.0, 1.0, 1.0);
   mPlots[1].mGraphColor = ColorF(1.0, 0.0, 0.0);
   mPlots[2].mGraphColor = ColorF(0.0, 1.0, 0.0);
   mPlots[3].mGraphColor = ColorF(0.0, 0.0, 1.0);
   mPlots[4].mGraphColor = ColorF(0.0, 1.0, 1.0);
   mPlots[5].mGraphColor = ColorF(0.0, 0.0, 0.0);
}

static EnumTable::Enums enumGraphTypes[] = {
   { GuiGraphCtrl::Bar,        "bar"      },
   { GuiGraphCtrl::Filled,     "filled"   },
   { GuiGraphCtrl::Point,      "point"    },
   { GuiGraphCtrl::Polyline ,  "polyline" },
};

static EnumTable gGraphTypeTable( 4, &enumGraphTypes[0] );

bool GuiGraphCtrl::onWake()
{
   if (! Parent::onWake())
      return false;
   setActive(true);
   return true;
}

void GuiGraphCtrl::onRender(Point2I offset, const RectI &updateRect)
{
	if (mProfile->mBorder)
	{
		RectI rect(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
		dglDrawRect(rect, mProfile->mBorderColor);
	}

	glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
	glEnable(GL_BLEND);
	ColorF color(1.0, 1.0, 1.0, 0.5);
	dglDrawRectFill(updateRect, color);
	glDisable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ZERO);

	for (int k = 0; k < MaxPlots; k++)
	{
		

		// Check if there is an autoplot and the proper amount of time has passed, if so add datum.
		if((mPlots[k].mAutoPlot!=NULL) &&
			(mPlots[k].mAutoPlotDelay < (Sim::getCurrentTime() - mPlots[k].mAutoPlotLastDisplay)))
		{
				mPlots[k].mAutoPlotLastDisplay = Sim::getCurrentTime();
				addDatum(k, Con::getFloatVariable(mPlots[k].mAutoPlot));
				Con::setIntVariable(mPlots[k].mAutoPlot, 0);
		}

		// Adjust scale to max value + 5% so we can see high values
		F32 Scale = (F32(getExtent().y) / (F32(mPlots[k].mGraphMax*1.05)));

		// Nothing to graph
		if (mPlots[k].mGraphData.size() == 0)
			continue;

		// Bar graph
		if(mPlots[k].mGraphType == Bar)
		{

			glBegin(GL_QUADS);

			glColor3fv(mPlots[k].mGraphColor);

			S32 temp1,temp2;
			temp1 = 0;

			for (S32 sample = 0; sample < getExtent().x; sample++)
			{
				if(mPlots[k].mGraphData.size() >= getExtent().x)
					temp2 = sample;
				else
					temp2 = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);

				glVertex2i(getPosition().x + temp1,
					(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));

				glVertex2i(getPosition().x + temp2,
					(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));

				glVertex2i(getPosition().x + temp2,
					getPosition().y + getExtent().y);

				glVertex2i(getPosition().x + temp1,
					getPosition().y + getExtent().y);

				temp1 = temp2;
			}

			glEnd();
		}

		// Filled graph
		else if(mPlots[k].mGraphType == Filled)
		{
			glBegin(GL_QUADS);

			glColor3fv(mPlots[k].mGraphColor);

			S32 temp1,temp2;
			temp1 = 0;

			for (S32 sample = 0; sample < (getExtent().x-1); sample++)
			{
				if(mPlots[k].mGraphData.size() >= getExtent().x)
					temp2 = sample;
				else
					temp2 = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);

				glVertex2i(getPosition().x + temp1,
					(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));

				glVertex2i(getPosition().x + temp2,
					(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample+1) * Scale));

				glVertex2i(getPosition().x + temp2,
					getPosition().y + getExtent().y);

				glVertex2i(getPosition().x + temp1,
					getPosition().y + getExtent().y);

				temp1 = temp2;
			}

			
			// last point
			S32 sample = getExtent().x;

			if(mPlots[k].mGraphData.size() >= getExtent().x)
				temp2 = sample;
			else
				temp2 = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);

			glVertex2i(getPosition().x + temp1,
				(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));

			glVertex2i(getPosition().x + temp2,
				(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));

			glVertex2i(getPosition().x + temp2,
				getPosition().y + getExtent().y);

			glVertex2i(getPosition().x + temp1,
				getPosition().y + getExtent().y);

			glEnd();
		}

		
		// Point or Polyline graph
		else if((mPlots[k].mGraphType == Point) || (mPlots[k].mGraphType == Polyline))
		{
			if(mPlots[k].mGraphType == Point)
				glBegin(GL_POINTS);
			else
				glBegin(GL_LINE_STRIP);

			glColor3fv(mPlots[k].mGraphColor);

			for (S32 sample = 0; sample < getExtent().x; sample++)
			{
				S32 temp;
				if(mPlots[k].mGraphData.size() >= getExtent().x)
					temp = sample;
				else
					temp = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);

				glVertex2i(getPosition().x + temp,
					(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
			}

			glEnd();
		}

	}

}

void GuiGraphCtrl::addDatum(S32 plotID, F32 v)
{
   AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!");

   // Add the data and trim the vector...
   mPlots[plotID].mGraphData.push_front( v );

   if(mPlots[plotID].mGraphData.size() > MaxDataPoints)
      mPlots[plotID].mGraphData.pop_back();

   // Keep record of maximum data value for scaling purposes.
   if(v > mPlots[plotID].mGraphMax)
	   mPlots[plotID].mGraphMax = v;
}

float GuiGraphCtrl::getDatum( int plotID, int sample)
{
   AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!");
   AssertFatal(sample > -1 && sample < MaxDataPoints, "Invalid sample specified!");
   return mPlots[plotID].mGraphData[sample];
}

void GuiGraphCtrl::addAutoPlot(S32 plotID, const char *variable, S32 update)
{
   mPlots[plotID].mAutoPlot = StringTable->insert(variable);
   mPlots[plotID].mAutoPlotDelay = update;
   Con::setIntVariable(mPlots[plotID].mAutoPlot, 0);
}

void GuiGraphCtrl::removeAutoPlot(S32 plotID)
{
   mPlots[plotID].mAutoPlot = NULL;
}

void GuiGraphCtrl::setGraphType(S32 plotID, const char *graphType)
{
	AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!");
	if(!dStricmp(graphType,"Bar"))
		mPlots[plotID].mGraphType = Bar;
	else if(!dStricmp(graphType,"Filled"))
		mPlots[plotID].mGraphType = Filled;
	else if(!dStricmp(graphType,"Point"))
		mPlots[plotID].mGraphType = Point;
	else if(!dStricmp(graphType,"Polyline"))
		mPlots[plotID].mGraphType = Polyline;
	else AssertWarn(true, "Invalid graph type!");
}

ConsoleMethod(GuiGraphCtrl, addDatum, void, 4, 4, "(int plotID, float v)"
              "Add a data point to the given plot.")
{
   S32 plotID = dAtoi(argv[2]);
   if(plotID > object->MaxPlots)
   {
	   Con::errorf("Invalid plotID.");
	   return;
   }
   object->addDatum( plotID, dAtof(argv[3]));
}

ConsoleMethod(GuiGraphCtrl, getDatum, F32, 4, 4, "(int plotID, int samples)"
              "Get a data point from the plot specified, samples from the start of the graph.")
{
   S32 plotID = dAtoi(argv[2]);
   S32 samples = dAtoi(argv[3]);

   if(plotID > object->MaxPlots)
   {
	   Con::errorf("Invalid plotID.");
	   return -1;
   }
   if(samples > object->MaxDataPoints)
   {
	   Con::errorf("Invalid sample.");
	   return -1;
   }

   return object->getDatum(plotID, samples);
}

ConsoleMethod(GuiGraphCtrl, addAutoPlot, void, 5, 5, "(int plotID, string variable, int update)"
			  "Adds a data point with value variable, every update ms.")
{
   S32 plotID = dAtoi(argv[2]);

   if(plotID > object->MaxPlots)
   {
	   Con::errorf("Invalid plotID.");
	   return;
   }

   object->addAutoPlot(plotID,argv[3],dAtoi(argv[4]));
}

ConsoleMethod(GuiGraphCtrl, removeAutoPlot, void, 3, 3, "(int plotID)"
			  "Stops automatic pointing over set interval.")
{
   S32 plotID = dAtoi(argv[2]);

   if(plotID > object->MaxPlots)
   {
	   Con::errorf("Invalid plotID.");
	   return;
   }

   object->removeAutoPlot(plotID);
}

ConsoleMethod(GuiGraphCtrl, setGraphType, void, 4, 4, "(int plotID, string graphType)"
			  "Change GraphType of plot plotID.")
{
	S32 plotID = dAtoi(argv[2]);

	if(plotID > object->MaxPlots)
	{
		Con::errorf("Invalid plotID.");
		return;
	}

	object->setGraphType(dAtoi(argv[2]), argv[3]);
}

ConsoleMethod(GuiGraphCtrl, matchScale, void, 3, GuiGraphCtrl::MaxPlots+2, "(int plotID, int plotID, ...)"
			  "Sets the scale of all specified plots to the maximum scale among them.")
{
    F32 Max = 0;
	for(int i=0; i < (argc-2); i++)
	{
		if(dAtoi(argv[2+i]) > object->MaxPlots)
		{
			Con::errorf("Invalid plotID.");
			return;
		}
		if (object->mPlots[dAtoi(argv[2+i])].mGraphMax > Max)
			Max = object->mPlots[dAtoi(argv[2+i])].mGraphMax;
	}
	for(int i=0; i < (argc-2); i++)
		object->mPlots[dAtoi(argv[2+i])].mGraphMax = Max;
}