//
// "$Id: code.cpp,v 1.4 2003/08/30 16:56:45 laza2000 Exp $"
//
// Code output routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-1999 by Bill Spitzak and others.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
// Please report all bugs and problems to "fltk-bugs@easysw.com".
//

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include <efltk/Fl.h>
#include <efltk/filename.h>

#include "Fl_Type.h"
#include "alignment_panel.h"
#include "coding_style.h"

extern bool i18n;

static FILE *code_file;
static FILE *header_file;

static bool code_file_w = false;
static bool header_file_w = false;

// return true if c can be in a C identifier.  I needed this so
// it is not messed up by locale settings:
int is_id(char c) {
  return c>='a' && c<='z' || c>='A' && c<='Z' || c>='0' && c<='9' || c=='_';
}

////////////////////////////////////////////////////////////////
// Generate unique but human-readable identifiers:

struct id {
  char* text;
  void* object;
  id *left, *right;
  id (const char* t, void* o) : text(strdup(t)), object(o) {left = right = 0;}
  ~id();
};

id::~id() {
  delete left;
  free((void *)text);
  delete right;
}

static id* id_root;

const char* unique_id(void* o, const char* type, const char* name, const char* label) {
  char buffer[128];
  char* q = buffer;
  while (*type) *q++ = *type++;
  *q++ = '_';
  const char* n = name;
  if (!n || !*n) n = label;
  if (n && *n) {
    while (*n && !is_id(*n)) n++;
    while (is_id(*n)) *q++ = *n++;
  }
  *q = 0;
  // okay, search the tree and see if the name was already used:
  id** p = &id_root;
  int which = 0;
  while (*p) {
    int i = strcmp(buffer, (*p)->text);
    if (!i) {
      if ((*p)->object == o) return (*p)->text;
      // already used, we need to pick a new name:
      sprintf(q,"%x",++which);
      p = &id_root;
      continue;
    }
    else if (i < 0) p = &((*p)->left);
    else p  = &((*p)->right);
  }
  *p = new id(buffer, o);
  return (*p)->text;
}

////////////////////////////////////////////////////////////////
// return current indentation:

int indentation;
const char* indent() {
	return get_indent_string(indentation / 2);
}

////////////////////////////////////////////////////////////////
// declarations/include files:
// Each string generated by write_declare is written only once to
// the header file.  This is done by keeping a binary tree of all
// the calls so far and not printing it if it is in the tree.

struct included {
  char *text;
  included *left, *right;
  included(const char *t) {
    text = strdup(t);
    left = right = 0;
  }
  ~included();
};

included::~included() {
  delete left;
  free((void *)text);
  delete right;
}
static included *included_root;

int write_declare(const char *format, ...) 
{
	va_list args;
	char buf[4096];
	va_start(args, format);
	vsprintf(buf, format, args);
	va_end(args);

	// Add to binary tree..
	included **p = &included_root;
	while (*p) {
		int i = strcmp(buf,(*p)->text);
		if (!i) return 0;
		else if (i < 0) p = &((*p)->left);
		else p  = &((*p)->right);
	}
	*p = new included(buf);

	fprintf(header_file,"%s\n", buf);

	header_file_w = true;

	return 1;
}

////////////////////////////////////////////////////////////////

// silly thing to prevent declaring unused variables:
// When this symbol is on, all attempts to write code don't write
// anything, but set a variable if it looks like the varaible "o" is used:
int varused_test;
int varused;

// write an array of C characters (adds a null):
void write_cstring(const char *w, int length) 
{
  if (varused_test) return;
  const char *e = w+length;
  int linelength = 1;
  putc('\"', code_file);
  for (; w < e;) {
    int c = *w++;
    switch (c) {
    case '\b': c = 'b'; goto QUOTED;
    case '\t': c = 't'; goto QUOTED;
    case '\n': c = 'n'; goto QUOTED;
    case '\f': c = 'f'; goto QUOTED;
    case '\r': c = 'r'; goto QUOTED;
    case '\"':
    case '\'':
    case '\\':
    QUOTED:
      if (linelength >= 77) {fputs("\\\n",code_file); linelength = 0;}
      putc('\\', code_file);
      putc(c, code_file);
      linelength += 2;
      break;
    case '?': // prevent trigraphs by writing ?? as ?\?
      if (*(w-2) == '?') goto QUOTED;
      // else fall through:
    default:
      if (c >= ' ' && c < 127) {
	// a legal ASCII character
	if (linelength >= 78) {fputs("\\\n",code_file); linelength = 0;}
	putc(c, code_file);
	linelength++;
	break;
      }
      // otherwise we must print it as an octal constant:
      c &= 255;
      if (c < 8) {
	if (linelength >= 76) {fputs("\\\n",code_file); linelength = 0;}
	fprintf(code_file, "\\%o",c);
	linelength += 2;
      } else if (c < 64) {
	if (linelength >= 75) {fputs("\\\n",code_file); linelength = 0;}
	fprintf(code_file, "\\%o",c);
	linelength += 3;
      } else {
	if (linelength >= 74) {fputs("\\\n",code_file); linelength = 0;}
	fprintf(code_file, "\\%o",c);
	linelength += 4;
      }
      // We must not put more numbers after it, because some C compilers
      // consume them as part of the quoted sequence.  Use string constant
      // pasting to avoid this:
      c = *w;
      if (w < e && (c>='0'&&c<='9' || c>='a'&&c<='f' || c>='A'&&c<='F')) {
	putc('\"', code_file); linelength++;
	if (linelength >= 79) {fputs("\n",code_file); linelength = 0;}
	putc('\"', code_file); linelength++;
      }
      break;
    }
  }
  putc('\"', code_file);

  code_file_w = true;
}

// write an array of C characters in a decimal format
void write_carray(const char *w, int length) 
{
  if (varused_test) return;
#if 0
  write_cstring(w,length);
#else
  const char *e = w+length;
  int linelength = 1;
  for (; w < e;) {
    if (linelength >= 75) {fputs("\n",code_file); linelength = 0;}
    int c = (uchar)*w++;
    fprintf(code_file, "%d", c);
    if (w < e) fputc(',',code_file);
    linelength+=2;
    if(c>=10) linelength++;
    if(c>=100) linelength++;
  }
  code_file_w = true;
#endif
}

// write a C string, quoting characters if necessary:
void write_cstring(const char *w) {write_cstring(w,strlen(w));}
void write_carray(const char *w) {write_carray(w,strlen(w));}

// write some raw data in the code file (used to write inlined XPM)
void write_craw(const char* str) {
  fputs(str, code_file);
  code_file_w = true;
}

void write_c(const char* format,...) {
  if (varused_test) {varused = 1; return;}
  va_list args;
  va_start(args, format);
  vfprintf(code_file, format, args);
  va_end(args);
  code_file_w = true;
}

void write_h(const char* format,...) {
  if (varused_test) return;
  va_list args;
  va_start(args, format);
  vfprintf(header_file, format, args);
  va_end(args);
  header_file_w = true;
}

int write_number;

int write_code(const char *s, const char *t) 
{
	write_number++;
	
	delete id_root; 
	id_root = 0;

	indentation = 0;
	if (!s) code_file = stdout;
	else {
		FILE *f = fopen(s,"w");
		if (!f) return 0;
		code_file = f;
	}
	
	if (!t) header_file = stdout;
	else {
		FILE *f = fopen(t,"w");
		if (!f) { fclose(code_file); return 0; }
		header_file = f;
	}
  
	const char *hdr = "// generated by Extended Fast Light User Interface Designer (eFluid) version %.4f\n\n";
	fprintf(header_file, hdr, FL_VERSION);
	fprintf(code_file, hdr, FL_VERSION);

	{
		Fl_String define_name;
		const char* a = fl_file_filename(t);
		define_name += '_';
		while (*a) { define_name += isalnum(*a) ? toupper(*a) : '_'; a++; }
		define_name += '_';
		fprintf(header_file, "#ifndef %s\n", define_name.c_str());
		fprintf(header_file, "#define %s\n\n", define_name.c_str());
	}  

	write_declare("#include <efltk/Fl.h>");
	if(i18n) write_declare("#include <efltk/Fl_Locale.h>");

	if(t && include_H_from_C)
		write_c("#include \"%s\"\n", fl_file_filename(t));

	Fl_Type* p;

	code_file_w = false;
	header_file_w = false;

	// write all include directives for this & all children first
	for (p = Fl_Type::first; p; p = p->next_brother) {
		p->write_static(DIRECTIVES);
		for (Fl_Type* q = p->first_child; q; q = q->walk(p))
			q->write_static(DIRECTIVES);
	}
	if(header_file_w)	write_h("\n");
	if(code_file_w)		write_c("\n");
	
	header_file_w = false;

	// write all external function declarations for this & all children first
	for (p = Fl_Type::first; p; p = p->next_brother) {
		p->write_static(FUNCTIONS);
		for (Fl_Type* q = p->first_child; q; q = q->walk(p))
			q->write_static(FUNCTIONS);
	}
	if(header_file_w) write_h("\n");

	header_file_w = false;

	// write all (external) variable declarations for this & all children first
	for (p = Fl_Type::first; p; p = p->next_brother) {
		p->write_static(VARIABLES);
		for (Fl_Type* q = p->first_child; q; q = q->walk(p))
			q->write_static(VARIABLES);
	}
	if(header_file_w) write_h("\n");

	// then write declarations.. SIGH!
	for (p = Fl_Type::first; p; p = p->next_brother) {
		if(p->is_decl()) 
			p->write_code();
	}

	code_file_w = false;
	header_file_w = false;

	// write inline callbacks and variable decls in src
	// and write the nested code:
	for (p = Fl_Type::first; p; p = p->next_brother) {
		p->write_static(CODE);
		for (Fl_Type* q = p->first_child; q; q = q->walk(p))
			q->write_static(CODE);
		
		// No declarations - already wrote them
		if(!p->is_decl()) 
			p->write_code();
	}

	delete included_root; 
	included_root = 0;

	if (!s) return 1;

	fprintf(header_file, "\n#endif\n");
  
	int y = fclose(header_file);
	int x = fclose(code_file);
	header_file = 0;
	code_file = 0;
	
	return x >= 0 && y >= 0;
}

////////////////////////////////////////////////////////////////

int write_strings(const char *sfile)
{
    FILE *fp = fopen(sfile, "w");
    Fl_Type *p;
    //Fl_Widget_Type *w;

    if (!fp) return 1;

    fprintf(fp, "# generated by Fast Light User Interface Designer (fluid) version %.4f\n\n",
            FL_VERSION);

    for (p = Fl_Type::first; p; p = p->next_brother) {
        // write the nested strings
        p->write_strings(fp);
    }

    return fclose(fp);
}


void Fl_Type::write_static(int type) { }

void Fl_Type::write_code() 
{
  write_h("// Header for %s\n", title());
  write_c("// Code for %s\n", title());
}

void Fl_Type::write_strings(FILE *fp) 
{
    for (Fl_Type* q = first_child; q; q = q->next_brother)
        q->write_strings(fp);
}

//
// End of "$Id: code.cpp,v 1.4 2003/08/30 16:56:45 laza2000 Exp $".
//
