/*
 * print format program copyright (C) 2009 - 2013 H.Niwa
 */


/*
 * 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, 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 "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <errno.h>
#include <setjmp.h>
#include <sys/time.h>
#include <math.h>
#include <libgen.h>
#include <setjmp.h>

#include <string>
#include <complex>

#include "syserr.h"
#include "bin_node.h"
#include "gc.h"
#include "var.h"
#include "pred.h"
#include "context.h"
#include "unify.h"
#include "builtin.h"

#define MAXPATTERNLEN	4096

extern int FuncArg(Context* cx, Node*& args, Node* goalscar, List* module);

void FlagStr(std::string input, int& inputp, std::string& output);
void WidthStr(std::string input, int& inputp, std::string& output);
void PrecisionStr(std::string input, int& inputp, std::string& output);
int  ConversionStr(std::string input, int& inputp, std::string& output);


int Format(Context* cx, Node* goalscar, List* module)
{
	if (ListLength(goalscar->Cdr()) != 3) {
		syserr("usage : <%_PATTERN VAL> \n");
		return 0;
	}

	Node*	n = goalscar->Cdr()->Val();
	Node*	nvar = n->Car();
	n = n->Cdr();
	Node*	npattern = n->Car();
	n = n->Cdr();
	Node*	nval = n->Car();
	
	if (nvar->Val()->kind() != UNDEF) {
		syserr("usage : <%_PATTERN VAL> \n");
		return 0;
	}

	int rn;

	if ((rn = FuncArg(cx, npattern, goalscar, module)) <= 0) {
		syserr("% : failed in the evaluation of the argument. \n");
		return 0;
	}

	if (npattern->kind() != ATOM) {
		syserr("usage : <%_PATTERN VAL> \n");
		return 0;
	}

	if ((rn = FuncArg(cx, nval, goalscar, module)) <= 0) {
		syserr("% : failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nval->kind() != ATOM) {
		syserr("usage : <%_PATTERN VAL> \n");
		return 0;
	}
	
	std::string inputpattern, outputpattern;
	int	inputp = 0;
	
	((Atom*)npattern)->toString(inputpattern);
	outputpattern = "%";

	FlagStr(inputpattern, inputp, outputpattern);
	WidthStr(inputpattern, inputp, outputpattern);
	PrecisionStr(inputpattern, inputp, outputpattern);
	int f = ConversionStr(inputpattern, inputp, outputpattern);

	char* buf = (char*)malloc(MAXPATTERNLEN);
	if (f == 'd') {
		long long d;
		((Atom*)nval)->toInt(d);
		
		snprintf(buf, MAXPATTERNLEN, outputpattern.c_str(), d);
	} else if (f == 'f') {
		long double f;
		((Atom*)nval)->toFloat(f);

		snprintf(buf, MAXPATTERNLEN, outputpattern.c_str(), f);
	} else if (f == 's') {
		std::string s;
		((Atom*)nval)->toString(s);
		
		snprintf(buf, MAXPATTERNLEN, outputpattern.c_str(), s.c_str());
	}
	buf[MAXPATTERNLEN-1] = 0;

	Node* env = Nil->Cons(Nil);

	SetEnv(env, nvar);
	((Undef*)(nvar->Val()))->Set(mka(buf));

	PushStack(cx, Nil, Nil, env);

	free(buf);
	
	return 1;
}

void FlagStr(std::string input, int& inputp, std::string& output)
{
	int c = input[inputp];

	switch(c) {
	case '#':
	case '0':
	case '-':
	case ' ':
	case '+':
	case '\'':
		inputp++;
		output += (char)c;
		break;
	default :
		break;
	}
}


void WidthStr(std::string input, int& inputp, std::string& output)
{
	int l = input.length();
	
	for ( ; inputp < l; inputp++) {
		int c = input[inputp];
		if ((c < '0') || (c > '9')) {
			break;
		}
		output += (char)c;
	}
}

void PrecisionStr(std::string input, int& inputp, std::string& output)
{
	if (input[inputp] == '.') {
		inputp++;
		output += '.';
	}
	
	int l = input.length();
	
	for ( ; inputp < l; inputp++) {
		int c = input[inputp];
		if ((c < '0') || (c > '9')) {
			break;
		}
		output += (char)c;
	}

}

int ConversionStr(std::string input, int& inputp, std::string& output)
{
	int c = input[inputp];

	switch(c) {
	case 'd':
		inputp++;
		output += "lld";
		return 'd';
		break;
	case 'i':
		inputp++;
		output += "lli";
		return 'd';
		break;
	case 'o':
		inputp++;
		output += "llo";
		return 'd';
		break;
	case 'u':
		inputp++;
		output += "llu";
		return 'd';
		break;
	case 'x':
		inputp++;
		output += "llx";
		return 'd';
		break;
	case 'X':
		inputp++;
		output += "llX";
		return 'd';
		break;
	case 'e':
		inputp++;
		output += "Le";
		return 'f';
		break;
	case 'E':
		inputp++;
		output += "LE";
		return 'f';
		break;
	case 'f':
		inputp++;
		output += "Lf";
		return 'f';
		break;
	case 'F':
		inputp++;
		output += "LF";
		return 'f';
		break;
	case 'g':
		inputp++;
		output += "Lg";
		return 'f';
		break;
	case 'G':
		inputp++;
		output += "LG";
		return 'f';
		break;
	case 's':
		inputp++;
		output += "s";
		return 's';
		break;
	default :
		break;
	}
	return 0;
}

