/** \file layout.c
 * Layout data and dialog
 */

/*  XTrkCad - Model Railroad CAD
 *  Copyright (C) 2017 Martin Fischer
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <string.h>
#include <dynstring.h>

#include "custom.h"
#include "i18n.h"
#include "layout.h"
#include "misc2.h"
#include "param.h"
#include "paths.h"
#include "track.h"
#include "wlib.h"

#define MINTRACKRADIUSPREFS "minTrackRadius"

struct sLayoutProps {
    char			title1[TITLEMAXLEN];
    char			title2[TITLEMAXLEN];
    SCALEINX_T		curScaleInx;
    SCALEDESCINX_T	curScaleDescInx;
    GAUGEINX_T		curGaugeInx;
    DIST_T			minTrackRadius;
    DIST_T			maxTrackGrade;
    coOrd			roomSize;
};

struct sDataLayout {
    struct sLayoutProps props;
    DynString	fullFileName;
    struct sLayoutProps *copyOfLayoutProps;
};

struct sDataLayout thisLayout = {
    { "", "", -1, 0, 0, 0.0, 5.0, {0.0, 0.0} },
    NaS,
    NULL,
};

static paramFloatRange_t r0_90 = { 0, 90 };
static paramFloatRange_t r1_10000 = { 1, 10000 };
static paramFloatRange_t r1_9999999 = { 1, 9999999 };

static void LayoutDlgUpdate(paramGroup_p pg, int inx, void * valueP);

/**
* Update the full file name. Do not do anything if the new filename is identical to the old one.
*
* \param filename IN the new filename
*/

void
SetLayoutFullPath(const char *fileName)
{
    if (DynStringToCStr(&thisLayout.fullFileName) != fileName) {
        if (isnas(&thisLayout.fullFileName)) {
            DynStringMalloc(&thisLayout.fullFileName, strlen(fileName) + 1);
        } else {
            DynStringClear(&thisLayout.fullFileName);
        }

        DynStringCatCStr(&thisLayout.fullFileName, fileName);
    }
}

/**
* Set the minimum radius for the selected scale/gauge into the dialog
*
* \param scaleName IN name of the scale/gauge eg. HOn3
* \param defaltValue IN default value will be used if no preference is set
*/

void
LoadLayoutMinRadiusPref(char *scaleName, double defaultValue)
{
    DynString prefString = { NULL };

    DynStringPrintf(&prefString, MINTRACKRADIUSPREFS "-%s", scaleName);
    wPrefGetFloat("misc", DynStringToCStr(&prefString),
                  &thisLayout.props.minTrackRadius, defaultValue);
    DynStringFree(&prefString);
}

static void
CopyLayoutTitle(char* dest, char *src)
{
    strncpy(dest, src, TITLEMAXLEN);
    *(dest + TITLEMAXLEN - 1) = '\0';
}

void
SetLayoutTitle(char *title)
{
    CopyLayoutTitle(thisLayout.props.title1, title);
}

void
SetLayoutSubtitle(char *title)
{
    CopyLayoutTitle(thisLayout.props.title2, title);
}

void
SetLayoutMinTrackRadius(DIST_T radius)
{
    thisLayout.props.minTrackRadius = radius;
}

void
SetLayoutMaxTrackGrade(ANGLE_T angle)
{
    thisLayout.props.maxTrackGrade = angle;
}


void
SetLayoutRoomSize(coOrd size)
{
    thisLayout.props.roomSize = size;
}

void
SetLayoutCurScale(SCALEINX_T scale)
{
    thisLayout.props.curScaleInx = scale;
}

void
SetLayoutCurScaleDesc(SCALEDESCINX_T desc)
{
    thisLayout.props.curScaleDescInx = desc;
}

void
SetLayoutCurGauge(GAUGEINX_T gauge)
{
    thisLayout.props.curGaugeInx = gauge;
}

/**
* Return the full filename.
*
* \return    pointer to the full filename, should not be modified or freed
*/

char *
GetLayoutFullPath()
{
    return (DynStringToCStr(&thisLayout.fullFileName));
}

/**
* Return the filename part of the full path
*
* \return    pointer to the filename part, NULL is no filename is set
*/

char *
GetLayoutFilename()
{
    char *string = DynStringToCStr(&thisLayout.fullFileName);

    if (string) {
        return (FindFilename(string));
    } else {
        return (NULL);
    }
}

char *
GetLayoutTitle()
{
    return (thisLayout.props.title1);
}

char *
GetLayoutSubtitle()
{
    return (thisLayout.props.title2);
}

DIST_T
GetLayoutMinTrackRadius()
{
    return (thisLayout.props.minTrackRadius);
}

ANGLE_T
GetLayoutMaxTrackGrade()
{
    return (thisLayout.props.maxTrackGrade);
}

SCALEDESCINX_T
GetLayoutCurScaleDesc()
{
    return (thisLayout.props.curScaleDescInx);
}

SCALEINX_T
GetLayoutCurScale()
{
    return (thisLayout.props.curScaleInx);
}
/****************************************************************************
*
* Layout Dialog
*
*/

static wWin_p layoutW;

static paramData_t layoutPLs[] = {
    { PD_FLOAT, &thisLayout.props.roomSize.x, "roomsizeX", PDO_NOPREF | PDO_DIM | PDO_NOPSHUPD | PDO_DRAW, &r1_9999999, N_("Room Width"), 0, (void*)(CHANGE_MAIN | CHANGE_MAP) },
    { PD_FLOAT, &thisLayout.props.roomSize.y, "roomsizeY", PDO_NOPREF | PDO_DIM | PDO_NOPSHUPD | PDO_DRAW | PDO_DLGHORZ, &r1_9999999, N_("    Height"), 0, (void*)(CHANGE_MAIN | CHANGE_MAP) },
    { PD_STRING, &thisLayout.props.title1, "title1", PDO_NOPSHUPD | PDO_STRINGLIMITLENGTH, NULL, N_("Layout Title"), 0, 0, sizeof(thisLayout.props.title1)},
    { PD_STRING, &thisLayout.props.title2, "title2", PDO_NOPSHUPD | PDO_STRINGLIMITLENGTH, NULL, N_("Subtitle"), 0, 0, sizeof(thisLayout.props.title2)},
#define SCALEINX (4)
    { PD_DROPLIST, &thisLayout.props.curScaleDescInx, "scale", PDO_NOPREF | PDO_NOPSHUPD | PDO_NORECORD | PDO_NOUPDACT, (void *)120, N_("Scale"), 0, (void*)(CHANGE_SCALE) },
#define GAUGEINX (5)
    { PD_DROPLIST, &thisLayout.props.curGaugeInx, "gauge", PDO_NOPREF | PDO_NOPSHUPD | PDO_NORECORD | PDO_NOUPDACT | PDO_DLGHORZ, (void *)120, N_("     Gauge"), 0, (void *)(CHANGE_SCALE) },
#define MINRADIUSENTRY (6)
    { PD_FLOAT, &thisLayout.props.minTrackRadius, "mintrackradius", PDO_DIM | PDO_NOPSHUPD | PDO_NOPREF, &r1_10000, N_("Min Track Radius"), 0, (void*)(CHANGE_MAIN | CHANGE_LIMITS) },
    { PD_FLOAT, &thisLayout.props.maxTrackGrade, "maxtrackgrade", PDO_NOPSHUPD | PDO_DLGHORZ, &r0_90, N_(" Max Track Grade (%)"), 0, (void*)(CHANGE_MAIN) }
};

static paramGroup_t layoutPG = { "layout", PGO_RECORD | PGO_PREFMISC, layoutPLs, sizeof layoutPLs / sizeof layoutPLs[0] };

/**
* Apply the changes entered to settings
*
* \param junk IN unused
*/

static void LayoutOk(void * junk)
{
    long changes;

    changes = GetChanges(&layoutPG);

    /* [mf Nov. 15, 2005] Get the gauge/scale settings */
    if (changes & CHANGE_SCALE) {
        SetScaleGauge(thisLayout.props.curScaleDescInx, thisLayout.props.curGaugeInx);
    }

    /* [mf Nov. 15, 2005] end */

    if (changes & CHANGE_MAP) {
        SetRoomSize(thisLayout.props.roomSize);
    }

    DoChangeNotification(changes);

    if (changes & CHANGE_LIMITS) {
        char prefString[30];
        // now set the minimum track radius
        sprintf(prefString, "minTrackRadius-%s", curScaleName);
        wPrefSetFloat("misc", prefString, thisLayout.props.minTrackRadius);
    }

    free(thisLayout.copyOfLayoutProps);
    wHide(layoutW);
}

/**
* Discard the changes entered and replace with earlier values
*
* \param junk IN unused
*/

static void LayoutCancel(struct wWin_t *junk)
{
    thisLayout.props = *(thisLayout.copyOfLayoutProps);
    ParamLoadControls(&layoutPG);
    LayoutOk(junk);
}

static void LayoutChange(long changes)
{
    if (changes & (CHANGE_SCALE | CHANGE_UNITS))
        if (layoutW != NULL && wWinIsVisible(layoutW)) {
            ParamLoadControls(&layoutPG);
        }
}

void DoLayout(void * junk)
{
    thisLayout.props.roomSize = mapD.size;

    if (layoutW == NULL) {
        layoutW = ParamCreateDialog(&layoutPG, MakeWindowTitle(_("Layout Options")),
                                    _("Ok"), LayoutOk, LayoutCancel, TRUE, NULL, 0, LayoutDlgUpdate);
        LoadScaleList((wList_p)layoutPLs[4].control);
    }

    LoadGaugeList((wList_p)layoutPLs[5].control,
                  thisLayout.props.curScaleDescInx); /* set correct gauge list here */
    thisLayout.copyOfLayoutProps = malloc(sizeof(struct sLayoutProps));

    if (!thisLayout.copyOfLayoutProps) {
        exit(1);
    }

    *(thisLayout.copyOfLayoutProps) = thisLayout.props;

    ParamLoadControls(&layoutPG);
    wShow(layoutW);
}

EXPORT addButtonCallBack_t LayoutInit(void)
{
    ParamRegister(&layoutPG);
    RegisterChangeNotification(LayoutChange);
    return &DoLayout;
}

/**
* Update the dialog when scale was changed. The list of possible gauges for the selected scale is
* updated and the first entry is selected (usually standard gauge). After this the minimum gauge
* is set from the preferences.
*
* \param pg IN dialog
* \param inx IN changed entry field
* \param valueP IN new value
*/

static void
LayoutDlgUpdate(
    paramGroup_p pg,
    int inx,
    void * valueP)
{
    /* did the scale change ? */
    if (inx == SCALEINX) {
        char prefString[100];
        char scaleDesc[100];

        LoadGaugeList((wList_p)layoutPLs[GAUGEINX].control, *((int *)valueP));
        // set the first entry as default, usually the standard gauge for a scale
        wListSetIndex((wList_p)layoutPLs[GAUGEINX].control, 0);

        // get the minimum radius
        // get the selected scale first
        wListGetValues((wList_p)layoutPLs[SCALEINX].control, scaleDesc, 99, NULL, NULL);
        strtok(scaleDesc, " ");

        // now get the minimum track radius
        sprintf(prefString, "minTrackRadius-%s", scaleDesc);
        wPrefGetFloat("misc", prefString, &thisLayout.props.minTrackRadius, 0.0);

        // put the scale's minimum value into the dialog
        wStringSetValue((wString_p)layoutPLs[MINRADIUSENTRY].control,
                        FormatDistance(thisLayout.props.minTrackRadius));
    }
}