/* 
CCIDE
Copyright 2001-2011 David Lindauer.

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 St, Fifth Floor, Boston, MA  02110-1301  USA

You may contact the author at:
	mailto::camille@bluegrass.net
 */
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <richedit.h>
#include <stdio.h>

#include "header.h"

extern HWND hwndProject;
extern int making;
extern char szInstallPath[];
extern PROJLIST *projectList;
static char szFileName[256];

static void putname(FILE *out, char *name)
{
    char buf[256];
    int i = 0;
    strcpy(buf, relpath(name, szFileName));

    while (buf[i])
    {
        buf[i] = tolower(buf[i]);
        i++;
    }
    buf[i] = 0;
    fprintf(out, "%s ", buf);
}

//-------------------------------------------------------------------------

static void getmacname(char *buf, char *name)
{
    char *p = buf;
    int i;
    strcpy(buf, name);

    while (*p)
    {
        *p = toupper(*p);
        if (*p == '.')
            *p = '_';
        p++;
    }
    return ;

}

//-------------------------------------------------------------------------

static void put_explicit(FILE *out, FILELIST *list)
{
    DEPENDSLIST *n = list->depends;
    int count = 0;
    putname(out, list->output);
    fprintf(out, ": ");
    putname(out, list->name);
    while (n)
    {
        if (!(++count % 5))
            fprintf(out, " \\\n\t");
        putname(out, n->name);
        n = n->next;
    }
    fputc('\n', out);
}

//-------------------------------------------------------------------------

static void put_buildfile(FILE *out, PROJLIST *l, FILELIST *list)
{
    char dirty[256], ext[100], buf[256],  *p;
    put_explicit(out, list);
    _splitpath(list->name, dirty, dirty, dirty, ext);
    getmacname(dirty, l->title);
    strcpy(buf, relpath(list->name, szFileName));
    if (!xstricmpz(ext, ".c") || !xstricmpz(ext, ".cpp"))
    {
        fprintf(out, 
            "\t$(CC) -f&&|\n$(CFLAGS_%s) \"-o%s\" -I$(INCLUDE_%s)\n $(DEFINES_%s)\n \"%s\"\n|\n", 
			dirty, relpath(list->output, szFileName), dirty, dirty, buf);
        p = strrchr(buf, '.');
        if (!p)
            p = buf + strlen(buf);
        strcpy(p, ".asm");
        if (l->buildFlags &BF_COMPILEVIAASM)
            fprintf(out, 
                "\t$(ASM) -@&&|\n -o \"%s\" $(AFLAGS_%s) $(DEFINES_%s)\n \"%s\"\n|\n", 
				relpath(list->output, szFileName), dirty, dirty, buf);
        fprintf(out, "\n");
    }
    else if (!xstricmpz(ext, ".asm"))
        fprintf(out, "\t$(ASM) -@&&|\n -o \"%s\" $(AFLAGS_%s) $(DEFINES_%s)\n \"%s\"\n|\n\n",
            relpath(list->output, szFileName), dirty, dirty, buf);
    else if (!xstricmpz(ext, ".rc"))
        fprintf(out, 
            "\t$(RC) $(RCFLAGS_%s) \"-fo%s\" -i$(INCLUDE_%s) @&&|\n$(DEFINES_%s)\n| %s\n\n", 
			dirty, relpath(list->output, szFileName), dirty, dirty, buf);
}

//-------------------------------------------------------------------------

static void put_libFile(FILE *out, PROJLIST *l)
{
    char buf[256];
    getmacname(buf, l->title);
    fprintf(out, "\n\tdel %s\n", relpath(l->name, szFileName));
    fprintf(out, "\t$(LIB) $(LIBFLAGS_%s) %s @&&|\n", buf, relpath(l->name, szFileName));
    fprintf(out, "+ $(%s)\n|\n\n", buf);
}

//-------------------------------------------------------------------------

static void put_linkFile(FILE *out, PROJLIST *l)
{
    char dirty[256], buf[256],  *p;
    FILELIST *f = l->resourceFiles;
    while (f)
    {
        p = strrchr(f->output, '.');
        if (p && !xstricmp(p, ".res"))
        {
            fprintf(out, " ");
            putname(out, f->output);
        }
        f = f->next;
    }
    getmacname(dirty, l->title);
    fprintf(out, "\n\t$(LINK) $(LFLAGS_%s) @&&|\n", dirty);
    fprintf(out, "$(START_%s) $(%s),", dirty, dirty);
    strcpy(buf, relpath(l->name, szFileName));
    fprintf(out, "%s,", buf);
    p = strrchr(buf, '.');
    if (p)
         *p = 0;
    fprintf(out, "%s,", buf);
    fprintf(out, "$(LIBS_%s),", dirty);
    f = l->resourceFiles;
    while (f)
    {
        p = strrchr(f->output, '.');
        if (p && !xstricmp(p, ".res"))
            putname(out, f->output);
        f = f->next;
    }
    fprintf(out, ",,\n|\n\n");
}

//-------------------------------------------------------------------------

static void put_defines(FILE *out, PROJLIST *l)
{
    FILELIST *m = l->otherFiles;
    DEFINES *d = l->defines;
	PROJLIST *projList = projectList;
    char buf[256], libs[4000], startupfile[256],  *p, data[256], *dllVariant1, *dllVariant2;
	char includeroot[256];
	p = strcpy(includeroot, relpath(l->projectName, szFileName));
    p = strrchr(includeroot, '\\');
	libs[0] = 0;
    if (p)
    {
        if (p[ - 1] == ':')
            p[1] = 0;
        else
            p[0] = 0;
    }
	else
	{
		strcpy(includeroot, ".\\;");
	}
	
    getmacname(buf, l->title);
	while (projList)
	{
		int len = strlen(projList->name);
		if (len >= 4)
		{
			if (!stricmp(projList->name + len -4, ".lib"))
			{
				sprintf(libs + strlen(libs),"\"%s\" ",projList->name);
			}
		}	
		projList = projList->next;
	}
	while (m)
	{
		int len = strlen(m->name);
		if (!stricmp(m->name + len -4, ".lib"))
		{
			sprintf(libs + strlen(libs),"\"%s\" ",m->name);
		}
		m = m->next;
	}
	dllVariant1 = "";
	dllVariant2 = "";
    switch (l->buildType)
    {
        case BT_CONSOLE:
            p = "/PE /CON";
			dllVariant1 = "/Wc";
            if (l->libType != LT_NONE)
                if (l->libType == LT_LSCRTDLL)
            		sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0xdll.obj\" ");
                else
		            sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0xwin.obj\" ");
            break;
        case BT_WINDOWS:
            p = "/PE /WIN";
			dllVariant1 = "/Wg";
            if (l->libType != LT_NONE)
                if (l->libType == LT_LSCRTDLL)
		            sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0dll.obj\" ");
                else
		            sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0win.obj\" ");
            break;
        case BT_DLL:
            p = "/PE /BDL";
			dllVariant1 = "/Wd";
            if (l->libType != LT_NONE)
                if (l->libType == LT_LSCRTDLL)
		            sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0ddll.obj\" ");
                else
		            sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0dwin.obj\" ");
            break;
        case BT_DOS:
			p = "/LX";
			dllVariant1 = "/Wa";
            if (l->libType != LT_NONE)
		    	sprintf(startupfile, "\"$(INSTALLDIR)\\lib\\c0dos.obj\" ");
            break;
        case BT_RAW:
			p = "/SYS";
			dllVariant1 = "/Wa";
            break;
    }
    if (l->buildType == BT_DOS || l->buildType == BT_RAW)
	{
        if (l->libType == LT_STANDARD)
		{
			dllVariant2 = "";
	        sprintf(libs + strlen(libs), "\"$(INSTALLDIR)\\lib\\cldos.lib\" ");
		}
	}
    else
    {
		if (l->libType == LT_MSVCRT)
		{
            sprintf(libs + strlen(libs), 
                "\"$(INSTALLDIR)\\lib\\climp.lib\" \"$(INSTALLDIR)\\lib\\msvcrt.lib\" ");
			dllVariant2 = "m";
		}
        else if (l->libType == LT_CRTDLL)
		{
	            sprintf(libs + strlen(libs), 
    	            "\"$(INSTALLDIR)\\lib\\climp.lib\" \"$(INSTALLDIR)\\lib\\crtdll.lib\" ");
			dllVariant2 = "l";
		}
            else if (l->libType == LT_STANDARD)
			{
	            sprintf(libs + strlen(libs), 
    	            "\"$(INSTALLDIR)\\lib\\climp.lib\" \"$(INSTALLDIR)\\lib\\clwin.lib\" ");
				dllVariant2 = "";
			}
            else if (l->libType == LT_LSCRTDLL)
			{
	            sprintf(libs + strlen(libs), 
    	            "\"$(INSTALLDIR)\\lib\\climp.lib\" \"$(INSTALLDIR)\\lib\\lscrtl.lib\" ");
				dllVariant2 = "l";
			}
            else
			{
				dllVariant2 = "";
	            sprintf(libs + strlen(libs), 
    	            "\"$(INSTALLDIR)\\lib\\climp.lib\"");
			}
    }
    fprintf(out, "#\n# Options\n#\n\n");
    fprintf(out, "CFLAGS_%s = %s %s %s %s %s %s %s %s%s\n", buf, 
		l->compileopts, 
		(l->buildFlags &BF_COMPILEVIAASM) ? "/C+N " : "/c", 
		(l->buildFlags &BF_DEBUGINFO) ? "+v" : "", 
		(l->buildFlags & BF_C99) ? "/9" : "", 
		(l->buildFlags & BF_ANSI) ? "+A" : "",
		(l->buildFlags & BF_ALIGNSTACK) ? "/C+s" : "",
		(l->buildFlags & BF_SHOWWARNINGS) ? "" : "/w-all", 
		dllVariant1, dllVariant2);
    fprintf(out, "AFLAGS_%s = -fobj %s\n", buf, (l->buildFlags &BF_DEBUGINFO) ?
        "-Fls -g" : "");
    fprintf(out, "RCFLAGS_%s = -r\n", buf);
    fprintf(out, "LFLAGS_%s = %s /32 /nci %s %s %s\n", buf, l->linkopts, p, (l
        ->buildFlags &BF_MAPFILE) ? "/MP" : "", (l->buildFlags &BF_DEBUGINFO) ?
        "/DEB" : "");
    fprintf(out, "LIBFLAGS_%s = %s\n\n", buf, l->libopts);
    if (l->includePath[0])
        fprintf(out, "INCLUDE_%s = %s;%s\n", buf, includeroot, relincludepath(l->includePath, szFileName));
    else
        fprintf(out, "INCLUDE_%s = %s\n", buf, includeroot);
    fprintf(out, "LIBS_%s = %s\n", buf, libs);
    fprintf(out, "START_%s = %s\n", buf, startupfile);
    data[0] = 0;
    if (l->outputPath[0])
        strcpy(data, l->outputPath);
    else
    {
        strcpy(data, includeroot) ;
    }
    fprintf(out, "OUTPUTDIR_%s=%s\n", buf, data);

    fprintf(out, "#\n# Rules\n#\n\n");

    fprintf(out, "%s = ", buf);
	m = l->sourceFiles;
    while (m)
    {
        p = strrchr(m->output, '.');
        if (p && !xstricmp(p, ".obj"))
        {
            fprintf(out, " \\\n\t");
            putname(out, m->output);
        }
        m = m->next;
    }
    fprintf(out, "\n\n");
    if (d)
    {
        fprintf(out, "#\n# User defines\n#\n\nDEFINES_%s = ", buf);
        while (d)
        {
            if (d->value[0])
            {
                fprintf(out, "\t-D%s=%s", d->name, d->value);
            }
            else
            {
                fprintf(out, "\t-D%s", d->name);
            }
            d = d->next;
            if (d)
                fprintf(out, " \\\n");
            else
                fprintf(out, "\n");
        }
    }

    fprintf(out, "\n\n");
}

//-------------------------------------------------------------------------

static void put_prepost(FILE *out, char *macname, char *name, char *steps)
{
    char buf[248],  *p;
    strcpy(buf, steps);
    steps = buf;
    p = steps;
    while (p = strstr(p, "$(OUTPUTDIR)"))
    {
        int l;
        p += 11;
        l = strlen(p);
        memmove(p + strlen(macname) + 1, p, l + 1);
        p[0] = '_';
        memcpy(p + 1, macname, strlen(macname));
    }
    if (steps[0])
    {
        fprintf(out, "%s:\n\t", name);
        while (*steps)
        {
            if (*steps != '\r')
            {
                fputc(*steps++, out);
                if (*steps == '\n')
                    fputc('\t', out);
            }
            else
                steps++;
        }
        fprintf(out, "\n\n");
    }
}

//-------------------------------------------------------------------------

static void put_target(FILE *out, PROJLIST *l)
{
    FILELIST *m = l->sourceFiles;
    char buf[MAX_PATH], pre[MAX_PATH], post[MAX_PATH];
    fprintf(out, "#\n# Build rules for target %s\n#\n", l->title);
    put_defines(out, l);
    getmacname(buf, l->title);
    putname(out, l->name);
    fprintf(out, ": $(%s)", buf);
    if (l->buildType == BT_LIBRARY)
        put_libFile(out, l);
    else
    {
        put_linkFile(out, l);
    }
    if (l->prebuildsteps[0] || l->postbuildsteps[0])
    {
        pre[0] = post[0] = 0;
        if (l->prebuildsteps[0])
            sprintf(pre, "%s_prebuild", buf);
        if (l->postbuildsteps[0])
            sprintf(post, "%s_postbuild", buf);
        fprintf(out, "%s_I: %s ", buf, pre);
        putname(out, l->name);
        fprintf(out, " %s\n\n", post);
        put_prepost(out, buf, pre, l->prebuildsteps);
        put_prepost(out, buf, post, l->postbuildsteps);
    }
    while (m)
    {
        put_buildfile(out, l, m);
        m = m->next;
    }
	m = l->resourceFiles;
    while (m)
    {
        put_buildfile(out, l, m);
        m = m->next;
    }

}

//-------------------------------------------------------------------------

static void put_defaultdefs(FILE *out)
{
    char data[MAX_PATH],  *p;
    fprintf(out, "\n#\n# Default paths\n#\n");
    fprintf(out, "INSTALLDIR=%s\n", szInstallPath);
    strcpy(data, szFileName);
    p = strrchr(data, '\\');
    if (p)
        if (p[ - 1] == ':')
            p[1] = 0;
        else
            p[0] = 0;
    fprintf(out, "PROJECTDIR=%s\n", data);
    GetSystemDirectory(data, MAX_PATH);
    fprintf(out, "SYSTEMDIR=%s\n", data);
    GetWindowsDirectory(data, MAX_PATH);
    fprintf(out, "WINDIR=%s\n", data);
}

//-------------------------------------------------------------------------

static void putall(FILE *out, PROJLIST *l)
{
    char buf[MAX_PATH];
    fprintf(out, "all: ");
    while (l)
    {
        if (l->prebuildsteps[0] || l->postbuildsteps[0])
        {
            getmacname(buf, l->title);
            fprintf(out, "%s_I ", buf);
        }
        else
            putname(out, l->name);
        l = l->next;
    }
    fprintf(out, "\n\n");
}

//-------------------------------------------------------------------------

static void genMake(PROJLIST *l)
{
    FILE *out;
    OPENFILENAME ofn;

    if (!SaveFileDialog(&ofn, "makefile", hwndProject, FALSE, 0, "Save Makefile As"))
        return ;
	strcpy(szFileName, ofn.lpstrFile);
    out = fopen(ofn.lpstrFile, "w");
    if (!out)
    {
        ExtendedMessageBox("Makefile Generation", MB_SETFOREGROUND |
            MB_SYSTEMMODAL, "Could not open %s for write", ofn.lpstrFile);
        return ;
    }
    making = 1;
    CalcRebuilds(); // to set up the output paths if they aren't 
    fprintf(out, "#\n#  Generated make file for workspace %s\n#\n", szFileName)
        ;
    putall(out, l);
    fprintf(out, "#\n# Tools Definitions\n#\n");
    fprintf(out, "\nCC=CC386\nLINK=VALX\nASM=NASM\nRC=XRC\nLIB=XLIB\n");
    put_defaultdefs(out);
    while (l)
    {
        put_target(out, l);
        l = l->next;
    }
    fclose(out);
    ExtendedMessageBox("Makefile Generation", MB_SETFOREGROUND | MB_SYSTEMMODAL, 
        "Makefile generated");
    making = 0;
}

//-------------------------------------------------------------------------

void genMakeFile(PROJLIST *l)
{
    DWORD threadhand;
    CloseHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)genMake, (LPVOID)l,
        0, &threadhand));
}
