// -*- C++ -*-
// $RCSfile: saveconfig.C,v $
// $Revision: 1.7 $
// $Author: langer $
// $Date: 2000/11/19 02:05:48 $



/* This software was produced by NIST, an agency of the U.S. government,
 * and by statute is not subject to copyright in the United States.
 * Recipients of this software assume all responsibilities associated
 * with its operation, modification and maintenance. However, to
 * facilitate maintenance we ask that before distributing modifed
 * versions of this software, you first contact the authors at
 * oof_manager@ctcms.nist.gov. 
 */

#include "adaptmesh.h"
#include "goof.h"
#include "imageform.h"
#include "material.h"
#include "compressedfile.h"
#include "ppm2oof.h"
#include "saveconfig.h"
#include <iostream.h>

static int cfgversion = 1;
static istream *configfile;

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

class GoofCmd : public CommandM {
private:
  int height, width;
public:
  GoofCmd() : CommandM("goof", "create a new goof"),
    width(0), height(0)
  {
    AddArgument(this, "height", height);
    AddArgument(this, "width", width);
  }
  virtual void func() {
#ifdef DEBUG
    cerr << "creating Goof" << endl << flush;
#endif // DEBUG
    current_goof = new Goof(height, width);
  }
  virtual CommandM *clone() const {
    GoofCmd *gc = new GoofCmd;
    gc->width = width;
    gc->height = height;
    return gc;
  }
};

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

class LoadImageCmd : public CommandM {
private:
  CharString name;
public: LoadImageCmd() : CommandM("image", "load image")
  {
    AddArgument(this, "name", name);
  }
  virtual CommandFn func() {
#ifdef DEBUG
    cerr << "reading image " << name << "..." << flush;
#endif // DEBUG
    Image picture;
    picture.read_image(*configfile);
    current_goof->addimage(picture, name);
#ifdef DEBUG
    cerr << " ok" << endl;
#endif // DEBUG
  }
  virtual CommandM *clone() const {
    LoadImageCmd *lic = new LoadImageCmd;
    lic->name = name;
    return lic;
  }
};

void Goof::savegallery(ostream &file) const {
  for(int i=0; i<gallery.capacity(); i++) {
    file <<  "image name=" << gallery[i]->name() << endl;
    file << *gallery[i] << endl;
  }
  file << endl;
}

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

static const int no_material = -1;

void Goof::savematerials(ostream &file) const {
  // Materials are saved in the order in which they were created, so
  // their index data is correct.  The config file just uses the
  // commands in the main material menu -- they will create the
  // Materials but won't assign them to pixels because there's no
  // current selection yet.  So it's important that the materials are
  // saved before the selections are!
  for(int i=0; i<Material::allmaterials.capacity(); i++)
    file << "ppm2oof material " << Material::allmaterials[i]->parameters\
	 << endl;
  file << "materials" << endl;
  ArrayIterator iter(material);
  Cell_coordinate cell;
  while(iter(cell)) {
    if(material[cell])
      file << material[cell]->index() << endl;
    else
      file << no_material << endl;
  }
}

static CommandFn loadmaterials() {
  Cell_coordinate cell;
  ArrayIterator iter(current_goof->material);
#ifdef DEBUG
  cerr << "Reading " << current_goof->material.query_height() << "x"
       << current_goof->material.query_width() << " array of materials..."
       << flush;
#endif // DEBUG
  while(iter(cell) && *configfile) {
    int mat;
    *configfile >> mat;
    if(mat != no_material)
      current_goof->assign_material(cell, Material::allmaterials[mat]);
  }
#ifdef DEBUG
  cerr << " ok" << endl;
#endif // DEBUG
}

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

void Goof::savefirewalls(ostream &file) const {
  file << "firewalls" << endl;
  Cell_coordinate cell;
  ArrayIterator iter(fireproof);
  while(iter(cell))
    file << fireproof[cell] << endl;
}

static CommandFn loadfirewalls() {
#ifdef DEBUG
  cerr << "reading firewalls..." << flush;
#endif // DEBUG
  Cell_coordinate cell;
  ArrayIterator iter(current_goof->fireproof);
  while(iter(cell) && *configfile)
    *configfile >> current_goof->fireproof[cell];
#ifdef DEBUG
  cerr << " ok" << endl;
#endif // DEBUG
}

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

void Goof::savegroups(ostream &file) const {
  for(int i=0; i<grouplist.capacity(); i++) {
    const PixelGroup &grp = *grouplist[i];
#ifdef DEBUG
    cerr << "saving group " << grp.query_name() << " with "
	 << grp.size() << " pixels" << endl;
#endif // DEBUG
    file << "group name=" << grp.query_name()
	 << " npixels=" << grp.size() << endl;
    for(int j=0; j<grp.size(); j++)
      file << grp[j] << endl;
  }
}

class LoadGroupCmd : public CommandM {
private:
  int npixels;
  CharString name;
public:
  LoadGroupCmd() : CommandM("group", "load a group"),
    npixels(0)
  {
    AddArgument(this, "name", name);
    AddArgument(this, "npixels", npixels);
  }
  virtual CommandFn func() {
#ifdef DEBUG
    cerr << "loading group " << name << " with "
	 << npixels << " pixels..." << flush;
#endif // DEBUG
    PixelGroup *grp = current_goof->add_group(name);
    grp->setphysicalsize(npixels);
    for(int i=0; i<npixels; i++) {
      Cell_coordinate cell;
      *configfile >> cell;
      grp->append(cell);
    }
#ifdef DEBUG
    cerr << " ok" << endl;
#endif // DEBUG
  }
  virtual CommandM *clone() const {
    LoadGroupCmd *lgc = new LoadGroupCmd;
    lgc->npixels = npixels;
    lgc->name = name;
    return lgc;
  }
};

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

void Goof::saveselections(ostream &file) const {
  // All Goofs are constructed with an initial empty selection. We
  // don't want to save it, so this loop starts with i=1.
  for(int i=1; i<selections.capacity(); i++) {
    const PixelGroup &sel = *selections[i];
    file << "selection npixels=" << sel.size() << endl;
    for(int j=0; j<sel.size(); j++)
      file << sel[j] << endl;
  }
  file << "currentselection=" << selected_index << endl;
}

class LoadSelectionsCmd : public CommandM {
private:
  int npixels;
public:
  LoadSelectionsCmd()
    : CommandM("selection", "load a set of selected pixels"),
      npixels(0)
  {
    AddArgument(this, "npixels", npixels);
  }
  virtual void func() {
    // make a list of pixels in the selection
    Vec<Cell_coordinate> pxl(npixels);
#ifdef DEBUG
    cerr << "Reading selection with " << npixels << " pixels..." << flush;
#endif // DEBUG
    for(int i=0; i<npixels && *configfile; i++) 
      *configfile >> pxl[i];

    // select all pixels at once
    // override may be set in menu commands that were read at the
    // start of the configuration file, so it has to be overridden
    // here.
    TrueFalse active_override_save = Goof::active_override;
    Goof::active_override = TF_FALSE;
    current_goof->init_selection(0);
    current_goof->select(pxl);
    Goof::active_override = active_override_save;
#ifdef DEBUG
    cerr << " ok" << endl;
#endif // DEBUG
  }
  virtual CommandM *clone() const {
    LoadSelectionsCmd *lsc = new LoadSelectionsCmd;
    lsc->npixels = npixels;
    return lsc;
  }
};

static int currentselection = 0;

static void set_currentselection(const CharString&) {
  current_goof->set_selection(currentselection);
}

void Goof::set_selection(int which) {
  if(which < selections.capacity()) {
    selected_index = which;
    selected_grp = selections[which];
  }
}

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

static void write_activearea(const ActiveArea &active, ostream &file) {
  ArrayIterator iter(active);
  Cell_coordinate cell;
  while(iter(cell))
    if(active[cell])		// write coords of active pixels
      file << cell << endl;
}

void Goof::saveactiveareas(ostream &file) const {
  for(int i=0; i<stored_active_areas.capacity(); i++) {
    const ActiveArea &active = *stored_active_areas[i];
    file << "activearea name=" << active.name
	 << " npixels=" << query_width()*query_height()-active.ninactive
	 << endl;
    write_activearea(active, file);
  }
  if(current_active_area.ninactive != 0) {
    file << "currentactivearea npixels="
	 << query_height()*query_width()-current_active_area.ninactive << endl;
    write_activearea(current_active_area, file);
  }
}


class LoadActiveAreaCmd : public CommandM {
private:
  CharString name;
  int npixels;
public:
  LoadActiveAreaCmd()
    : CommandM("activearea", "load a stored active area")
  {
    AddArgument(this, "name", name);
    AddArgument(this, "npixels", npixels);
  }
  virtual void func() {
#ifdef DEBUG
    cerr << "reading active area " << name << "..." << endl << flush;
#endif // DEBUG
    PixelGroup grp;		// store pixels in here temporarily
    grp.setphysicalsize(npixels);
    for(int i=0; i<npixels; i++) {
      Cell_coordinate cell;
      *configfile >> cell;
      grp.append(cell);
    }
    
    current_goof->remove_restrictions();
    current_goof->activate_group(grp, Goof::AM_INTERSECT);
    current_goof->store_active_area(name);
    current_goof->remove_restrictions();
#ifdef DEBUG
    cerr << " ok" << endl;
#endif // DEBUG
  }
  virtual CommandM *clone() const {
    LoadActiveAreaCmd *laac = new LoadActiveAreaCmd;
    laac->name = name;
    laac->npixels = npixels;
    return laac;
  }
};

class LoadCurrentActiveAreaCmd : public CommandM {
private:
  int npixels;
public:
  LoadCurrentActiveAreaCmd()
    : CommandM("currentactivearea", "load a stored active area")
  {
    AddArgument(this, "npixels", npixels);
  }
  virtual void func() {
#ifdef DEBUG
    cerr << "reading current active area..." << flush;
#endif // DEBUG
    PixelGroup grp;		// store pixels in here temporarily
    grp.setphysicalsize(npixels);
    for(int i=0; i<npixels; i++) {
      Cell_coordinate cell;
      *configfile >> cell;
      grp.append(cell);
    }

    current_goof->remove_restrictions();
    current_goof->activate_group(grp, Goof::AM_INTERSECT);
#ifdef DEBUG
    cerr << " ok" << endl;
#endif // DEBUG
  }
  virtual CommandM *clone() const {
    LoadCurrentActiveAreaCmd *lcaac = new LoadCurrentActiveAreaCmd;
    lcaac->npixels = npixels;
    return lcaac;
  }
};

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

void Goof::savemeshes(ostream &file) const {
  for(CircleStackIterator<AdaptiveMesh*> iter(meshstack); !iter.end(); ++iter)
    {
      file << "mesh" << endl;
      meshstack[iter]->save(file);
    }
}

void Goof::readmesh(istream &file) {
#ifdef DEBUG
  cerr << "Reading mesh..." << flush;
#endif // DEBUG
  add_mesh(new AdaptiveMesh);
  the_mesh->load(file);
  the_mesh->inherit_pixel_groups(1);
  the_mesh->inherit_pixel_materials(1);
#ifdef DEBUG
  cerr << " ok" << endl;
#endif // DEBUG
}

CommandFn read_mesh() {
  current_goof->readmesh(*configfile);
}

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

static CommandFn read_imageform() {
#ifdef DEBUG
  cerr << "reading imageform..." << flush;
#endif // DEBUG
  ImageForm::read_imageform(*configfile);
#ifdef DEBUG
  cerr << " ok" << endl;
#endif // DEBUG
}

static CommandFn read_commands() {
  // Everything from here on in the config file will be simply loaded
  // into the command log, so this command had better be the last one
  // in the file.
#ifdef DEBUG
  cerr << "reading commands..." << flush;
#endif // DEBUG
  garcon()->readlog(*configfile);
#ifdef DEBUG
  cerr << " ok" << endl;
#endif // DEBUG
}

static CommandFn configfile_cmd() {
  // This command does nothing... A command with the same name in the
  // main ppm2oof menu transfers control to the config menu, though.
  // That lets the same routines load either a config file or a log
  // file.
}

//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//

Menu *configmenu() {
  static Menu *menu = 0;
  if(!menu) {
    menu = new Menu("load");
    // commands only used when parsing the config file
    AddVariable(menu, "cfgversion", "configuration file version number",
		cfgversion);
    menu->AddCommand(new GoofCmd);
    menu->AddCommand("imageform", "read imageform data", read_imageform);
    menu->AddCommand(new LoadImageCmd);
    menu->AddCommand("materials", "load materials", loadmaterials);
    menu->AddCommand("firewalls", "load firewalls", loadfirewalls);
    menu->AddCommand(new LoadGroupCmd);
    menu->AddCommand(new LoadSelectionsCmd);
    AddVariable(menu, "currentselection", "current selection",
		currentselection, set_currentselection);
    menu->AddCommand(new LoadActiveAreaCmd);
    menu->AddCommand(new LoadCurrentActiveAreaCmd);
    menu->AddCommand("mesh", "read a mesh", read_mesh);
    // all of the regular ppm2oof commands
    menu->AddSubMenu("ppm2oof", "all of the ppm2oof commands", ppm2oofmenu());
    menu->AddCommand("commands", "read command history", read_commands);
    menu->AddCommand("configfile", "a dummy command", configfile_cmd);
  }
  return menu;
}

void loadconfig(const CharString &filename) {
  CompressedFile file(filename);
  if(!file.ok()) return;
  loadconfig(file.stream());
}
  
void loadconfig(istream &file) {
  configfile = &file;

#ifdef IOSTREAM_HAS_EXCEPTIONS
  configfile->exceptions(ios::badbit);
#endif // IOSTREAM_HAS_EXCEPTIONS

  if(current_goof)
    delete current_goof;

  garcon()->graphics_off();
  garcon()->inhibit_logging();
#ifdef IOSTREAM_HAS_EXCEPTIONS
  try {
    garcon()->runfile(*configfile, configmenu(), 0);
  }
  catch(ios::failure)
#else
    garcon()->runfile(*configfile, configmenu(), 0);
    if(configfile->bad())
#endif // IOSTREAM_HAS_EXCEPTIONS
      {
	garcon()->msout << ms_error << "Error reading configuration file!"
			<< endl << ms_normal;
	if(current_goof)
	  delete current_goof;
	current_goof = 0;
      }
    //    cerr << "Final state of configuration file:" << endl
    //  	    << "  good = " << configfile->good() << endl
    //  	    << "   eof = " << configfile->eof() << endl
    //  	    << "  fail = " << configfile->fail() << endl
    //  	    << "   bad = " << configfile->bad() << endl;
    garcon()->disinhibit_logging();
    garcon()->graphics_on();
    if(current_goof)
      current_goof->redraw();
}

void saveconfig(ostream &file, const Goof *goof) {
  // The order in which things are saved depends on the order in which
  // they must be read.

  // Zeroth, label this file as a config file, instead of a log file.
  file << "configfile" << endl;

  //  First, save the minimal information necessary to make a goof.
  file << "goof height=" << current_goof->query_height()
       << " width=" << current_goof->query_width() << endl;

  // Save all menu variables.
  ppm2oofmenu()->print(file, "ppm2oof", TF_TRUE);

  ImageForm::save_imageforms(file);
  goof->savegallery(file); // Save the images.
  goof->savematerials(file);
  goof->savefirewalls(file);
  goof->savegroups(file);	// save pixel groups
  goof->saveselections(file);
  goof->saveactiveareas(file);
  goof->savemeshes(file);

  // Save the command history. This must be done last!
  file << "commands" << endl;
  garcon()->printlog(file);
}
