//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
//
//                 Half-Life Model Viewer (c) 1999 by Mete Ciragan
//
// file:           pakviewer.cpp
// last modified:  May 04 1999, Mete Ciragan
// copyright:      The programs and associated files contained in this
//                 distribution were developed by Mete Ciragan. The programs
//                 are not in the public domain, but they are freely
//                 distributable without licensing fees. These programs are
//                 provided without guarantee or warrantee expressed or
//                 implied.
//
// version:        1.2
//
// email:          mete@swissquake.ch
// web:            http://www.swissquake.ch/chumbalum-soft/
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mxtk/mx.h>
#include "pakviewer.h"
#include "mdlviewer.h"
// #include "GlWindow.h"
#include "StudioModel.h"
#include "ControlPanel.h"
#include "FileAssociation.h"



int
pak_ExtractFile (const char *pakFile, const char *lumpName, char *outFile)
{
	FILE *file = fopen (pakFile, "rb");
	if (!file)
		return 0;

	int ident, dirofs, dirlen;

	fread (&ident, sizeof (int), 1, file);
	if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'))
	{
		fclose (file);
		return 0;
	}

	fread (&dirofs, sizeof (int), 1, file);
	fread (&dirlen, sizeof (int), 1, file);

	fseek (file, dirofs, SEEK_SET);
	int numLumps = dirlen / 64;

	for (int i = 0; i < numLumps; i++)
	{
		char name[56];
		int filepos, filelen;

		fread (name, 56, 1, file);
		fread (&filepos, sizeof (int), 1, file);
		fread (&filelen, sizeof (int), 1, file);

		if (!mx_strcasecmp (name, lumpName))
		{
			FILE *out = fopen (outFile, "wb");
			if (!out)
			{
				fclose (file);
				return 0;
			}

			fseek (file, filepos, SEEK_SET);

			while (filelen--)
				fputc (fgetc (file), out);

			fclose (out);
			fclose (file);

			return 1;
		}
	}

	fclose (file);

	return 0;
}



PAKViewer::PAKViewer (mxWindow *window)
: mxWindow (window, 0, 0, 0, 0, "", mxWindow::Normal)
{
	strcpy (d_pakFile, "");
	strcpy (d_currLumpName, "");

	tvPAK = new mxTreeView (this, 0, 0, 0, 0, IDC_PAKVIEWER);
	pmMenu = new mxPopupMenu ();
	pmMenu->add ("Load Model", 1);
	pmMenu->addSeparator ();
	pmMenu->add ("Load Background", 2);
	pmMenu->add ("Load Ground", 3);
	pmMenu->addSeparator ();
	pmMenu->add ("Play Sound", 4);
	pmMenu->addSeparator ();
	pmMenu->add ("Extract File...", 5);
	setLoadEntirePAK (true);

	setVisible (false);
}



PAKViewer::~PAKViewer ()
{
	//tvPAK->remove (0);
	//tvPAK->remove();
	closePAKFile ();
}



void
_makeTempFileName (char *str, const char *suffix)
{
	strcpy (str, mx_gettemppath ());

	strcat (str, "/hltempmodel");
	strcat (str, suffix);
}



int
PAKViewer::handleEvent (mxEvent *event)
{
	switch (event->event)
	{
	case mxEvent::Action:
	{
		switch (event->action)
		{
		case IDC_PAKVIEWER: // tvPAK
			if (event->flags & mxEvent::RightClicked)
			{
				pmMenu->setEnabled (1, strstr (d_currLumpName, ".mdl") != 0);
				pmMenu->setEnabled (2, strstr (d_currLumpName, ".tga") != 0);
				pmMenu->setEnabled (3, strstr (d_currLumpName, ".tga") != 0);
				pmMenu->setEnabled (4, strstr (d_currLumpName, ".wav") != 0);
				int ret = pmMenu->popup (tvPAK, event->x, event->y);
				switch (ret)
				{
				case 1:
					OnLoadModel ();
					break;

				case 2:
					OnLoadTexture (0);
					break;

				case 3:
					OnLoadTexture (1);
					break;

				case 4:
					OnPlaySound ();
					break;

				case 5:
					OnExtract ();
					break;
				}
			}
			else if (event->flags & mxEvent::DoubleClicked)
			{
				OnPAKViewer ();
				char e[16];

				strncpy (e, mx_getextension (d_currLumpName), 16);
				int mode = g_FileAssociation->getMode (&e[1]);
				if (mode == -1)
					return 1;

				char *program = g_FileAssociation->getProgram (&e[1]);

#ifdef WIN32
				if (mode == 0)
				{
					char str[256];
					_makeTempFileName (str, e);
					if (!pak_ExtractFile (d_pakFile, d_currLumpName, str))
						mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
					else
					{
						if (program)
						{
							char path[256];
							strcpy (path, program);
							strcat (path, " ");
							strcat (path, str);
							if ((int) WinExec (path, SW_SHOW) <= 32)
								mxMessageBox (this, "Error executing specified program.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
						}
					}
				}

				// associated program
				else if (mode == 1)
				{
					char str[256];
					_makeTempFileName (str, e);
					if (!pak_ExtractFile (d_pakFile, d_currLumpName, str))
						mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
					else
						if ((int) ShellExecute ((HWND) getHandle (), "open", str, 0, 0, SW_SHOW) <= 32)
							mxMessageBox (this, "Error executing document with associated program.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
				}

				// HLMV default
				else	
#endif
				if (mode == 2)
				{
					if (!strcmp (e, ".mdl"))
						OnLoadModel ();

					else if (!strcmp (e, ".tga"))
						OnLoadTexture (0);

					else if (!strcmp (e, ".wav"))
						OnPlaySound ();

					return 1;
				}
			}
			
			return OnPAKViewer ();
		} // event->action
	} // mxEvent::Action
	break;

	case mxEvent::Size:
	{
		tvPAK->setBounds (0, 0, event->width, event->height);
	} // mxEvent::Size
	break;

	} // event->event

	return 1;
}



int
PAKViewer::OnPAKViewer ()
{
	mxTreeViewItem *tvi = tvPAK->getSelectedItem ();
	if (tvi)
	{
		strcpy (d_currLumpName, tvPAK->getLabel (tvi));

		// find the full lump name
		mxTreeViewItem *tviParent = tvPAK->getParent (tvi);
		char tmp[128];
		while (tviParent)
		{
			strcpy (tmp, d_currLumpName);
			strcpy (d_currLumpName, tvPAK->getLabel (tviParent));
			strcat (d_currLumpName, "/");
			strcat (d_currLumpName, tmp);
			tviParent = tvPAK->getParent (tviParent);
		}

		if (!d_loadEntirePAK)
		{
			// finally insert "models/"
			strcpy (tmp, d_currLumpName);
			strcpy (d_currLumpName, "models/");
			strcat (d_currLumpName, tmp);
		}
	}

	return 1;
}



int PAKViewer::OnLoadModel ()
{
	static char str2[256];
	char suffix[16];

	strcpy (suffix, ".mdl");
	_makeTempFileName (str2, suffix);

	if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
	{
		mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
		return 1;
	}

	g_pStudioModel->FreeModel ( false );
	if( !g_pStudioModel->LoadModel (str2) )
	{
		mxMessageBox (this, "Error reading model header.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
		return 1;
	}
		
	return 1;
}



int PAKViewer::OnLoadTexture (int pos)
{
	static char str2[256];
	char suffix[16] = "";

	if (strstr (d_currLumpName, ".tga"))
		sprintf (suffix, "%d%s", pos, ".tga");

	_makeTempFileName (str2, suffix);

	if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
	{
		mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
		return 1;
	}

	if (0 /* g_MDLViewer->getGlWindow ()->loadTexture (str2, pos) */)
	{
		if (pos == 0)
			g_ControlPanel->setShowBackground (true);
		else
			g_ControlPanel->setShowGround (true);
	}
	else
		mxMessageBox (this, "Error loading texture.",  g_appTitle, MX_MB_OK | MX_MB_ERROR);

	return 1;
}



int
PAKViewer::OnPlaySound ()
{
#ifdef WIN32
	static char str2[256];
	char suffix[16] = "";

	// stop any playing sound
	PlaySound (0, 0, SND_FILENAME | SND_ASYNC);

	if (strstr (d_currLumpName, ".wav"))
		sprintf (suffix, "%d%s", 44, ".wav");

	_makeTempFileName (str2, suffix);

	if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
	{
		mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
		return 1;
	}

	PlaySound (str2, 0, SND_FILENAME | SND_ASYNC);

#endif
	return 1;
}



int
PAKViewer::OnExtract ()
{
	char *ptr = (char *) mxGetSaveFileName (this, "", "*.*");
	if (ptr)
	{
		if (!pak_ExtractFile (d_pakFile, d_currLumpName, ptr))
			mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
	}

	return 1;
}



int
_compare(const void *arg1, const void *arg2)
{
	if (strchr ((char *) arg1, '/') && !strchr ((char *) arg2, '/'))
		return -1;

	else if (!strchr ((char *) arg1, '/') && strchr ((char *) arg2, '/'))
		return 1;

	else
		return strcmp ((char *) arg1, (char *) arg2);
}



bool
PAKViewer::openPAKFile (const char *pakFile)
{
	FILE *file = fopen (pakFile, "rb");
	if (!file)
		return false;

	int ident, dirofs, dirlen;

	// check for id
	fread (&ident, sizeof (int), 1, file);
	if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'))
	{
		fclose (file);
		return false;
	}

	// load lumps
	fread (&dirofs, sizeof (int), 1, file);
	fread (&dirlen, sizeof (int), 1, file);
	int numLumps = dirlen / 64;

	fseek (file, dirofs, SEEK_SET);
	lump_t *lumps = new lump_t[numLumps];
	if (!lumps)
	{
		fclose (file);
		return false;
	}

	fread (lumps, sizeof (lump_t), numLumps, file);
	fclose (file);

	qsort (lumps, numLumps, sizeof (lump_t), _compare);

	// save pakFile for later
	strcpy (d_pakFile, pakFile);

	tvPAK->remove (0);

	char namestack[32][32];
	mxTreeViewItem *tvistack[32];
	for (int k = 0; k < 32; k++)
	{
		strcpy (namestack[k], "");
		tvistack[k] = 0;
	}

	for (int i = 0; i < numLumps; i++)
	{
		if (d_loadEntirePAK || !strncmp (lumps[i].name, "models", 6))
		{
			char *tok;
			if (d_loadEntirePAK)
				tok = &lumps[i].name[0];
			else
				tok = &lumps[i].name[7];

			int i = 1;
			while (tok)
			{
				char *end = strchr (tok, '/');
				if (end)
					*end = '\0';

				if (strcmp (namestack[i], tok))
				{
					strcpy (namestack[i], tok);
/*
					if (i == 0)
						tvistack[i] = tvPAK->add (0, tok);
					else*/
						tvistack[i] = tvPAK->add (tvistack[i - 1], tok);

					for (int j = i + 1; j < 32; j++)
					{
						strcpy (namestack[j], "");
						tvistack[j] = 0;
					}
				}

				++i;

				if (end)
					tok = end + 1;
				else
					tok = 0;
			}
		}
	}

	delete[] lumps;

	setVisible (true);

	return true;
}



void
PAKViewer::closePAKFile ()
{
	strcpy (d_pakFile, "");
	setVisible (false);
}
