/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "hsTypes.h"
#include "plMaxFileData.h"
#include "hsUtils.h"

#include "max.h"
#include "notify.h"
#include "tvnode.h"

#define PLASMA_FILE_DATA_CID Class_ID(0x255a700a, 0x285279dc)

// MyControl is derived from StdControl, but has no controller functionality. It simply has some
// membervariables and saves these by Load and Save.
// EditTrackParams and TrackParamsType are responsible for displaying a user interface (RightClick->Properties)
// on the controler. With these functions you can avoid it, having an interface !
// As you can see, most of these methods are stubbed. Only Load and Save are implemented
// and of course the methods, to access the membervariables.
class plMaxFileDataControl : public StdControl
{
public:
	SYSTEMTIME fCodeBuildTime;
	char fBranch[128];

	plMaxFileDataControl()
	{
		memset(&fCodeBuildTime, 0, sizeof(SYSTEMTIME));
		memset(&fBranch, 0, sizeof(fBranch));
	}

	// Animatable
	virtual void EditTrackParams(TimeValue t, ParamDimensionBase *dim,TCHAR *pname,HWND hParent, IObjParam *ip, DWORD flags){};
	int TrackParamsType() { return TRACKPARAMS_WHOLE; }
	virtual void DeleteThis() { delete this; }

	// ReferenceMaker
	virtual RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID,RefMessage message)
	{return REF_DONTCARE;}

	Class_ID ClassID() { return PLASMA_FILE_DATA_CID; }
	SClass_ID SuperClassID() { return CTRL_FLOAT_CLASS_ID; }
	void GetClassName(TSTR& s) {s = "blah";}

	// Control methods
	RefTargetHandle Clone(RemapDir& remap) { return TRACKED_NEW plMaxFileDataControl(); }
	void Copy(Control *from) {}
	virtual BOOL IsReplaceable() { return FALSE; }

	// StdControl methods
	void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE){}
	void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}
	void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type){}
	void *CreateTempValue() {return NULL;}
	void DeleteTempValue(void *val) {}
	void ApplyValue(void *val, void *delta) {}
	void MultiplyValue(void *val, float m) {}

	// MyControl methods
	IOResult Load(ILoad *iload);
	IOResult Save(ISave *isave);
};

#define MAXFILE_DATA_CHUNK	1001
static const UInt8 kVersion = 1;

IOResult plMaxFileDataControl::Load(ILoad *iload)
{
	ULONG nb;
	IOResult res;
	while (IO_OK==(res=iload->OpenChunk()))
	{
		if (iload->CurChunkID() == MAXFILE_DATA_CHUNK)
		{
			UInt8 version = 0;
			res = iload->Read(&version, sizeof(UInt8), &nb);
			res = iload->Read(&fCodeBuildTime, sizeof(SYSTEMTIME), &nb);

			int branchLen = 0;
			iload->Read(&branchLen, sizeof(int), &nb);
			iload->Read(&fBranch, branchLen, &nb);
		}

		iload->CloseChunk();
		if (res != IO_OK)
			return res;
	}

	return IO_OK;
}

IOResult plMaxFileDataControl::Save(ISave *isave)
{
	ULONG nb;
	isave->BeginChunk(MAXFILE_DATA_CHUNK);

	isave->Write(&kVersion, sizeof(kVersion), &nb);
	isave->Write(&fCodeBuildTime, sizeof(SYSTEMTIME), &nb);

	int branchLen = strlen(fBranch)+1;
	isave->Write(&branchLen, sizeof(int), &nb);
	isave->Write(&fBranch, branchLen, &nb);

	isave->EndChunk();
	return IO_OK;
}

class MaxFileDataClassDesc : public ClassDesc
{
public:
	int 			IsPublic()				{ return FALSE; }
	void*			Create(BOOL loading)	{ return TRACKED_NEW plMaxFileDataControl; }
	const TCHAR*	ClassName()				{ return _T("MaxFileData"); }
	SClass_ID		SuperClassID()			{ return CTRL_FLOAT_CLASS_ID; }
	Class_ID 		ClassID()				{ return PLASMA_FILE_DATA_CID; }
	const TCHAR* 	Category()				{ return _T(""); }
};
MaxFileDataClassDesc gMaxFileDataClassDesc;
ClassDesc *GetMaxFileDataDesc() { return &gMaxFileDataClassDesc; }

// This functions searches for Trackviewnode and the Controller and creates one, if none is present.
plMaxFileDataControl *GetMaxFileData(bool& created)
{
	plMaxFileDataControl *pCtrl = NULL;
	ITrackViewNode *tvNode = NULL;
	ITrackViewNode *tvRoot = GetCOREInterface()->GetTrackViewRootNode();

	int i = tvRoot->FindItem(PLASMA_FILE_DATA_CID);
	if (i < 0)
	{
		created = true;

		tvNode = CreateITrackViewNode();

		// This method adds the Node with the specific Title (e.g. "My Settings")
		tvRoot->AddNode(tvNode, "Plasma Globals", PLASMA_FILE_DATA_CID);
		pCtrl = (plMaxFileDataControl*)CreateInstance(CTRL_FLOAT_CLASS_ID, PLASMA_FILE_DATA_CID);

		TSTR s;
		pCtrl->GetClassName(s);


		// This adds the controller
		tvNode->AddController(pCtrl, s, PLASMA_FILE_DATA_CID);
		tvNode->HideChildren(TRUE);
	}
	else
	{
		created = false;

		tvNode = tvRoot->GetNode(i);
		pCtrl = (plMaxFileDataControl*)tvNode->GetController(PLASMA_FILE_DATA_CID);
	}

	return pCtrl;
}

static SYSTEMTIME gThisCodeBuildTime;
static char gThisBranch[128];

static void PrintTime(SYSTEMTIME& time, char* buf)
{
	sprintf(buf, "%d/%d/%d %d:%02d %s", time.wMonth, time.wDay, time.wYear,
			(time.wHour <= 12) ? time.wHour : time.wHour-12,
			time.wMinute,
			(time.wHour < 12 || time.wHour == 24) ? "AM" : "PM");
}

static void NotifyProc(void *param, NotifyInfo *info)
{
	if (info->intcode == NOTIFY_FILE_POST_OPEN)
	{
		bool created;
		plMaxFileDataControl* data = GetMaxFileData(created);

		if (!created)
		{
			FILETIME fileTime, pluginTime;
			SystemTimeToFileTime(&gThisCodeBuildTime, &pluginTime);
			SystemTimeToFileTime(&data->fCodeBuildTime, &fileTime);

			if (CompareFileTime(&fileTime, &pluginTime) > 0)
			{
				if (hsMessageBox_SuppressPrompts)
					return;

				char buf[1024];

				strcpy(buf, "This file was last saved with plugins stamped:\n\n");

				char timeBuf[128];
				PrintTime(data->fCodeBuildTime, timeBuf);
				strcat(buf, timeBuf);
				strcat(buf, "\n");
				strcat(buf, data->fBranch);

				strcat(buf, "\n\nThese plugins are stamped:\n\n");

				PrintTime(gThisCodeBuildTime, timeBuf);
				strcat(buf, timeBuf);
				strcat(buf, "\n");
				strcat(buf, gThisBranch);

				strcat(buf,
					"\n\nNew features may have been added to the newer plugins,\n"
					"so saving this file could cause data to be lost.");

				MessageBox(GetCOREInterface()->GetMAXHWnd(), buf, "Plugin Warning", MB_OK | MB_ICONEXCLAMATION);
			}
		}

		strcpy(data->fBranch, gThisBranch);
		memcpy(&data->fCodeBuildTime, &gThisCodeBuildTime, sizeof(SYSTEMTIME));
	}
}

static void IGetString(int resID, char *destBuffer, int size)
{
	HRSRC rsrc = ::FindResource(hInstance, MAKEINTRESOURCE(resID), RT_RCDATA);

	if (rsrc != NULL)
	{
		HGLOBAL handle = ::LoadResource(hInstance, rsrc);

		if (handle != NULL)
		{
			char* str = (char*)::LockResource(handle);
			strncpy(destBuffer, str, size);
			UnlockResource(handle);
		}
	}
}

void InitMaxFileData()
{
	memset(&gThisCodeBuildTime, 0, sizeof(SYSTEMTIME));

	// Date
	char buf[128];
	IGetString(1000, buf, sizeof(buf) - 1);
	sscanf(buf, "%hu/%hu/%hu", &gThisCodeBuildTime.wMonth, &gThisCodeBuildTime.wDay, &gThisCodeBuildTime.wYear);

	// Time
	IGetString(1001, buf, sizeof(buf) - 1);
	sscanf(buf, "%hu:%hu", &gThisCodeBuildTime.wHour, &gThisCodeBuildTime.wMinute);

	if (strstr(buf, "PM") != nil)
	{
		gThisCodeBuildTime.wHour += 12;
	}

	IGetString(1002, gThisBranch, sizeof(gThisBranch) - 1);

	RegisterNotification(NotifyProc, 0, NOTIFY_FILE_POST_OPEN);
}