#include <phantasmal/log.h>
#include <phantasmal/lpc_names.h>

#include <limits.h>

private object dtd;
private string dtd_text;
private string dtd_filename;

private void update_dtd_vars(void);

mixed* to_dtd_unq(void);
void   from_unq(mixed* unq);
void   from_dtd_unq(mixed* unq);

static void create(varargs int clone) {
  if(!find_object(UNQ_PARSER)) { compile_object(UNQ_PARSER); }
  if(!find_object(UNQ_DTD)) { compile_object(UNQ_DTD); }
}

void destructed(int clone) {
  if(dtd)
    destruct_object(dtd);
}

void upgraded(varargs int clone) {
  if(dtd_filename) {
    update_dtd_vars();
  }
}

/* This sets the DTD object given as this object's DTD.  That means
   the object may destruct the DTD at its leisure, among other things. */
void set_dtd(object new_dtd) {
  dtd = new_dtd;
}

/* This supplies the text of a DTD file to be this object's DTD. */
void set_dtd_text(string new_dtd_text) {
  dtd_text = new_dtd_text;
  update_dtd_vars();
}

/* This supplies a filename to load the object's DTD from. */
void set_dtd_file(string new_dtd_filename) {
  dtd_filename = new_dtd_filename;
  update_dtd_vars();
}

/* The updates dtd and dtd_text from themselves and dtd_filename. */
private void update_dtd_vars(void) {
  if(dtd_filename) {
    string buf;

    buf = read_file(dtd_filename);
    if(!buf) {
      error("Can't read DTD file " + dtd_filename + "!");
    }
    if(strlen(buf) > MAX_STRING_SIZE - 3) {
      error("DTD file is too large in update_dtd_vars!");
    }

    dtd_text = buf;
  }
  if(dtd_text) {
    if(dtd) {
      dtd->clear();
    } else {
      dtd = clone_object(UNQ_DTD);
    }

    dtd->load(dtd_text);
  }
}

/* This method serializes the object as UNQ text and returns the
   appropriate string, suitable for writing to an UNQ file. */
string to_unq_text(void) {
  string result;

  if(!dtd) {
    error("You must override the default to_unq_text method for "
	  + object_name(this_object()) + " or supply a DTD!");
  }

  result = dtd->serialize_to_dtd(to_dtd_unq());
  if(!result) {
    LOGD->write_syslog(dtd->get_parse_error_stack(), LOG_WARNING);
  }
  return result;
}


/* Just in case we need it... */
string get_parse_error_stack(void) {
  return dtd->get_parse_error_stack();
}


/* This method converts the object to the same sort of template
   that parsing an UNQ file with a DTD yields.  This template
   can then be converted to text. */
mixed* to_dtd_unq(void) {
  error("You must override the default to_dtd_unq method for "
	+ object_name(this_object()));
}

/* This method takes a string of UNQ text as loads it into the
   object. */
void from_unq_text(string text) {
  from_unq(UNQ_PARSER->basic_unq_parse(text));
}

/* This method takes a chunk of parsed UNQ and loads it into the
   object. */
void from_unq(mixed* unq) {
  mixed* dtd_unq;

  if(!dtd) {
    error("You must override the default from_unq method for "
	  + object_name(this_object()) + " or supply a DTD!");
  }

  dtd_unq = dtd->parse_to_dtd(unq);
  if(!dtd_unq)
    error("Can't parse UNQ according to DTD!");

  from_dtd_unq(dtd_unq);
}


/* This method takes the result of parsing the object according to
   its DTD and loads it into the object. */
void from_dtd_unq(mixed* unq) {
  error("You must override the default from_dtd_unq method for "
	+ object_name(this_object()));
}

/* This method loads the necessary file, parses it with the supplied
   DTD, and calls the from_dtd_unq method to load its contents into
   the object. */
void load_from_file(string filename) {
  string str;
  mixed* unq, *dtd_unq;

  str = read_file(filename);
  if(!str)
    error("Can't read file '" + filename + "' in load_from_file!");
  if(strlen(str) > MAX_STRING_SIZE - 3)
    error("File '" + filename + "' is too large in load_from_file!");

  unq = UNQ_PARSER->basic_unq_parse(str);
  if(!unq)
    error("Can't parse file contents to UNQ! (" + filename + ")");

  dtd_unq = dtd->parse_to_dtd(unq);
  if(!dtd_unq) {
    LOGD->write_syslog("Error stack:\n"
		       + dtd->get_parse_error_stack(), LOG_WARN);
    error("Can't parse UNQ according to DTD!");
  }

  from_dtd_unq(dtd_unq);
}
