/** \file dprmfile.c
 * Param File Dialog
 */

/*  XTrkCad - Model Railroad CAD
 *  Copyright (C) 2005 Dave Bullis
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "custom.h"
#include "dynstring.h"
#include "fileio.h"
#include "param.h"
#include "include/paramfile.h"
#include "include/paramfilelist.h"
#include "paths.h"
#include "track.h"

static struct wFilSel_t * paramFile_fs;


#include "bitmaps/greendot.image1"
#include "bitmaps/greydot.image1"
#include "bitmaps/yellowdot.image1"
#include "bitmaps/reddot.image1"
#include "bitmaps/greenstar.image1"
#include "bitmaps/greystar.image1"
#include "bitmaps/yellowstar.image1"
#include "bitmaps/redstar.image1"

#define FAVORITE_PARAM 1
#define STANDARD_PARAM 0

#define PARAMBUTTON_UNLOAD "Unload"
#define PARAMBUTTON_REFRESH "Reload"

#define PARAMFILE_UNLOAD (0)
#define PARAMFILE_REFRESH (1)

static wIcon_p indicatorIcons[ 2 ][PARAMFILE_MAXSTATE];

static wWin_p paramFileW;

static long paramFileSel = 0;

static void ParamFileFavorite(void * favorite);
static void ParamRefreshSelectedFiles(void * action);
static void ParamUnloadSelectedFiles(void * action);
static void ParamFileBrowse(void * junk);
static void ParamFileSelectAll(void * junk);

static paramListData_t paramFileListData = { 15, 370 };
static char * paramFileLabels[] = { N_("Show File Names"), NULL };
static paramData_t paramFilePLs[] = {
#define I_PRMFILLIST	(0)
#define paramFileL				((wList_p)paramFilePLs[I_PRMFILLIST].control)
	{	PD_LIST, NULL, "inx", PDO_NOPREF | PDO_DLGRESIZE, &paramFileListData, NULL, BL_DUP|BL_SETSTAY|BL_MANY },
#define I_PRMFILTOGGLE	(1)
	{	PD_TOGGLE, &paramFileSel, "mode", 0, paramFileLabels, NULL, BC_HORZ|BC_NOBORDER },
#define I_MESSAGE (2)
	{ PD_MESSAGE, "", NULL, 0, I2VP(370) },
	{	PD_BUTTON, ParamFileSelectAll, "selectall", PDO_DLGCMDBUTTON, NULL, N_("Select all") },
#define I_PRMFILEFAVORITE (4)
	{   PD_BUTTON, ParamFileFavorite, "favorite", PDO_DLGCMDBUTTON, I2VP(TRUE), N_("Favorite")},
	{	PD_BUTTON, ParamUnloadSelectedFiles, "unload", PDO_DLGCMDBUTTON, NULL, N_(PARAMBUTTON_UNLOAD), 0L, FALSE },
	{   PD_BUTTON, ParamRefreshSelectedFiles, "refresh", PDO_DLGCMDBUTTON, NULL, N_(PARAMBUTTON_REFRESH), 0L, FALSE },
	{	PD_BUTTON, DoSearchParams, "find", 0, NULL, N_("Library...") },
	{	PD_BUTTON, ParamFileBrowse, "browse", 0, NULL, N_("Browse...") },


};

static paramGroup_t paramFilePG = { "prmfile", 0, paramFilePLs, COUNT( paramFilePLs ) };

#define MESSAGETEXT ((wMessage_p)paramFilePLs[I_MESSAGE].control)

static dynArr_t *sortFiles;

/** Comparison function per C runtime conventions. Elements are ordered by compatibility
 *  state first and name of contents second.
 *
 * \param index1 IN first element
 * \param index2 IN second element
 * \return
 */

int
CompareParameterFiles(const void *index1, const void *index2)
{
	paramFileInfo_t paramFile1 = DYNARR_N(paramFileInfo_t, (*sortFiles),
	                                      *(int*)index1);
	paramFileInfo_t paramFile2 = DYNARR_N(paramFileInfo_t, (*sortFiles),
	                                      *(int*)index2);

	if (paramFile2.trackState != paramFile1.trackState) {
		return (paramFile2.trackState - paramFile1.trackState);
	} else {
		return (strcmp(paramFile1.contents, paramFile2.contents));
	}
}

/**
 * Create a sorted list of indexes into the parameter file array. That way, the elements
 * in the array will not be moved. Instead the list is used for the order in which the
 * list box is populated.
 *
 * \param cnt IN number of parameter files
 * \param files IN parameter file array
 * \param list OUT the ordered list
 */

void
SortParamFileList(size_t cnt,  dynArr_t *files, int *list)
{
	for (size_t i = 0; i < cnt; i++) {
		list[i] = (int)i;
	}

	sortFiles = files;

	qsort(list, (size_t)cnt, sizeof(int), CompareParameterFiles);
}


/**
 * Reload the listbox showing the current parameter files
 */
void ParamFileListLoad(int paramFileCnt,  dynArr_t *paramFiles)
{
	DynString description;
	DynStringMalloc(&description, STR_SHORT_SIZE);
	int *sortedIndex = MyMalloc(sizeof(int)*paramFileCnt);
	int log_params = LogFindIndex("params");

	SortParamFileList(paramFileCnt, paramFiles, sortedIndex);

	wControlShow((wControl_p)paramFileL, FALSE);
	wListClear(paramFileL);

	for (int i = 0; i < paramFileCnt; i++) {
		paramFileInfo_t paramFileInfo = DYNARR_N(paramFileInfo_t, (*paramFiles),
		                                sortedIndex[ i ]);
		if (paramFileInfo.valid) {
			DynStringClear(&description);
			DynStringCatCStr(&description,
			                 ((!paramFileSel) && paramFileInfo.contents) ?
			                 paramFileInfo.contents :
			                 paramFileInfo.name);

			wListAddValue(paramFileL,
			              DynStringToCStr(&description),
			              indicatorIcons[ paramFileInfo.favorite ][paramFileInfo.trackState],
			              I2VP(sortedIndex[i]));

			LOG1(log_params, ("ParamFileListLoad: = %s: %d\n", paramFileInfo.contents,
			                  paramFileInfo.trackState))
		}
	}
	wControlShow((wControl_p)paramFileL, TRUE);
	DynStringFree(&description);
	MyFree(sortedIndex);
}


static void ParamFileBrowse(void * junk)
{
	wMessageSetValue(MESSAGETEXT, "");
	wFilSelect(paramFile_fs, GetParamFileDir());
	return;
}

/**
 * Update the action buttons.
 *
 * If at least one selected file is not a favorite, the favorite button is set to 'SetFavorite'
 *
 */

static void UpdateParamFileButton(void)
{
	wIndex_t selcnt = wListGetSelectedCount(paramFileL);
	wIndex_t inx, cnt;
	wIndex_t fileInx;

	//nothing selected -> leave
	if (selcnt <= 0) {
		return;
	}

	// set the default
	paramFilePLs[I_PRMFILEFAVORITE].context = FALSE;

	// get the number of items in list
	cnt = wListGetCount(paramFileL);

	// walk through the whole list box
	for (inx=0; inx<cnt; inx++) {
		if (wListGetItemSelected((wList_p)paramFileL, inx)) {
			// if item is selected, get status
			fileInx = (wIndex_t)VP2L(wListGetItemContext(paramFileL, inx));

			if (fileInx < 0 || fileInx >= GetParamFileCount()) {
				return;
			}
			if (!IsParamFileFavorite(fileInx)) {
				paramFilePLs[I_PRMFILEFAVORITE].context = I2VP(TRUE);
			}
		}
	}
}

/**
 * Set the property for a parameter file in memory
 *
 * \param newState IN new value for property
 */

void
UpdateParamFileProperties( bool newState)
{
	wIndex_t inx, cnt;
	wIndex_t fileInx;

	// get the number of items in list
	cnt = wListGetCount(paramFileL);

	// walk through the whole list box
	for (inx = 0; inx < cnt; inx++) {
		if (wListGetItemSelected((wList_p)paramFileL, inx)) {
			fileInx = (wIndex_t)VP2L(wListGetItemContext(paramFileL, inx));
			SetParamFileFavorite(fileInx, newState);
		}
	}
	DoChangeNotification(CHANGE_PARAMS);
}

/**
 * Mark selected files as favorite
 *
 * \param favorite IN FALSE = remove, TRUE = set favorite
 * \return
 */

static void ParamFileFavorite(void * setFavorite)
{
	wIndex_t selcnt = wListGetSelectedCount(paramFileL);
	wMessageSetValue(MESSAGETEXT, "");
	if (selcnt) {
		UpdateParamFileProperties(setFavorite?TRUE:FALSE);
	}
}

/**
 * Parameter change selected files
 *
 * \param  paramFileChange The parameter file change.
 */

static void
ParamChangeSelectedFiles(unsigned paramFileChange)
{
	wIndex_t inx, cnt;
	wIndex_t fileInx;

	// get the number of items in list
	cnt = wListGetCount(paramFileL);

	for (inx = 0; inx < cnt; inx++) {
		if (wListGetItemSelected((wList_p)paramFileL, inx)) {
			fileInx = (wIndex_t)VP2L(wListGetItemContext(paramFileL, inx));

			switch (paramFileChange) {
			case PARAMFILE_UNLOAD:
				if (IsParamFileFavorite(fileInx)) {
					SetParamFileDeleted(fileInx, TRUE);
				} else {
					UnloadParamFile(fileInx);
				}
				break;
			case PARAMFILE_REFRESH:
				if (IsParamFileFavorite(fileInx) && IsParamFileDeleted(fileInx)) {
					SetParamFileDeleted(fileInx, FALSE);
				} else {
					ReloadParamFile(fileInx);
				}
				break;
			default:
				CHECKMSG( FALSE, ("Invalid change type %d in  ParamChangeSelectedFiles",
				                  paramFileChange) );
			}
		}
	}
	ParamFileListLoad(paramFileInfo_da.cnt, &paramFileInfo_da);
	DoChangeNotification(CHANGE_PARAMS);
}

/**
 * Refresh selected files.
 *
 * \param action IN FALSE = unload, TRUE = reload parameter files
 * \return
 */

static void ParamRefreshSelectedFiles(void * action)
{
	wIndex_t selcnt = wListGetSelectedCount(paramFileL);

	//nothing selected -> leave
	if (selcnt) {
		DynString reloadMessage;
		ParamChangeSelectedFiles(PARAMFILE_REFRESH);

		DynStringMalloc(&reloadMessage, 16);
		if (selcnt > 1) {
			DynStringPrintf(&reloadMessage, _("%d parameter files reloaded."), selcnt);
		} else {
			DynStringCatCStr(&reloadMessage, _("One parameter file reloaded."));
		}
		wMessageSetValue(MESSAGETEXT, DynStringToCStr(&reloadMessage));
		DynStringFree(&reloadMessage);
	} else {
		wBeep();
	}
}

static void ParamUnloadSelectedFiles(void * action)
{
	wIndex_t selcnt = wListGetSelectedCount(paramFileL);
	wMessageSetValue(MESSAGETEXT, "");
	//nothing selected -> leave
	if (selcnt) {
		ParamChangeSelectedFiles(PARAMFILE_UNLOAD);
	} else {
		wBeep();
	}
}


/**
 * Select all files in the list and set action button
 *
 * \param junk IN ignored
 * \return
 */

static void ParamFileSelectAll(void *junk)
{
	wMessageSetValue(MESSAGETEXT, "");
	wListSelectAll(paramFileL);
	UpdateParamFileButton();
}

static void ParamFileOk(void * junk)
{
	SearchUiOk(junk);

	DoChangeNotification(CHANGE_PARAMS);
	wHide(paramFileW);
}


static void ParamFileDlgUpdate(
        paramGroup_p pg,
        int inx,
        void * valueP)
{
	switch (inx) {
	case I_PRMFILLIST:
		UpdateParamFileButton();
		break;
	case I_PRMFILTOGGLE:
		DoChangeNotification(CHANGE_PARAMS);
		break;
	}
}


void ParamFilesChange(long changes)
{
	if (changes & CHANGE_PARAMS || changes & CHANGE_SCALE) {
		UpdateParamFileList();
		if (paramFileW) {
			ParamFileListLoad(paramFileInfo_da.cnt, &paramFileInfo_da);
		}
	}
}

/**
 * Create and open the parameter file dialog.
 *
 * \param junk
 */

void DoParamFiles(void * junk)
{
	void * data;

	if (paramFileW == NULL) {
		indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_UNLOADED ] = wIconCreatePixMap(
		                        greydot_image1);
		indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_NOTUSABLE ] = wIconCreatePixMap(
		                        reddot_image1);
		indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_COMPATIBLE ] = wIconCreatePixMap(
		                        yellowdot_image1);
		indicatorIcons[ STANDARD_PARAM ][ PARAMFILE_FIT] = wIconCreatePixMap(
		                        greendot_image1);
		indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_UNLOADED ] = wIconCreatePixMap(
		                        greystar_image1);
		indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_NOTUSABLE ] = wIconCreatePixMap(
		                        redstar_image1);
		indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_COMPATIBLE ] = wIconCreatePixMap(
		                        yellowstar_image1);
		indicatorIcons[ FAVORITE_PARAM ][ PARAMFILE_FIT ] = wIconCreatePixMap(
		                        greenstar_image1);

		ParamRegister(&paramFilePG);

		paramFileW = ParamCreateDialog(&paramFilePG,
		                               MakeWindowTitle(_("Parameter Files")), _("Done"), ParamFileOk, ParamCancel_Null,
		                               TRUE, NULL, F_RESIZE | F_RECALLSIZE, ParamFileDlgUpdate);
		paramFile_fs = wFilSelCreate(mainW, FS_LOAD, FS_MULTIPLEFILES,
		                             _("Load Parameters"), _("Parameter files (*.xtp)|*.xtp"), LoadParamFile, NULL);
	}
	ParamLoadControls(&paramFilePG);
	ParamGroupRecord(&paramFilePG);
	if ((wListGetValues(paramFileL, NULL, 0, NULL, &data))>=0) {
		UpdateParamFileButton();
	}

	wShow(paramFileW);
}
