/*
 * $Id: prefio.cxx,v 1.1 2003/06/03 16:11:54 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 * Saves and loads user preferences to and from ${HOME}/.imview/prefs
 *
 * Preferences themselves are just a bunch of variables in a struct. When
 * egcs supports namespaces, I may convert to this.
 * 
 * Hugues Talbot	18 Oct 1998
 *
 *-----------------------------------------------------------------------*/


#include <fstream>
#include <sstream>  // string streams
#include <string>   // the C++ ones
#include <string.h> // the C ones
#include "imnmspc.hxx" // contains the namespace definition, if needed
#include "imview.hxx"
#include "nocase.hxx" // order is important
#include "prefio.hxx"

// This really sucks
#define MAXPREFS 16

using std::setw;

// the preference class

prefs::prefs()
{
  /* IMPORTANT */
  /* G++ copes with declaring the following in a static array (for example)
     but other compilers may not. Experiments with DEC cxx showed that the
     method below is stable. This is probably the one to use, then, even
     though it might not be the most elegant. Who cares?
     
     The problem is when are static array initialized ? before the classes
     or after the classes? The standard doesn't seem to say. I've had problems
     with initialization of dynamic arrays as well, so stay away from those
     if the data in the array depends on the definition of classes. 
     
     Hugues Talbot	12 Nov 1998
     */
  
    valid_prefs = new prefdata[MAXPREFS];
      // general stuff
    valid_prefs[0].prefString = "SmoothZoomOut";
    valid_prefs[0].prefMethod =  &prefs::input_smoothZoomout;
    valid_prefs[0].prefWrite  =  &prefs::format_smoothZoomout;
    valid_prefs[0].comment    =  "Smooth to zoom out. True or False";

    valid_prefs[1].prefString = "KeepPoints";
    valid_prefs[1].prefMethod =  &prefs::input_keepPoints;
    valid_prefs[1].prefWrite  =  &prefs::format_keepPoints;
    valid_prefs[1].comment    =  "Keep the points when switching images. True or false";

    valid_prefs[2].prefString = "MouseZoomType";
    valid_prefs[2].prefMethod =  &prefs::input_mouseZoomType;
    valid_prefs[2].prefWrite  =  &prefs::format_mouseZoomType;
    valid_prefs[2].comment    =  "Constrained or unconstrained";

    valid_prefs[3].prefString = "OverlayTransparency";
    valid_prefs[3].prefMethod =  &prefs::input_overlayTransparency;
    valid_prefs[3].prefWrite  =  &prefs::format_overlayTransparency;
    valid_prefs[3].comment    =  "Transparency of overlays: 0.0= opaque, 1.0=perfectly transparent";
    
    // Postscript stuff
    valid_prefs[4].prefString = "GSPath";
    valid_prefs[4].prefMethod =  &prefs::input_gspath;
    valid_prefs[4].prefWrite  =  &prefs::format_gspath;
    valid_prefs[4].comment    =  "Path to Ghostscript interpreter";

    valid_prefs[5].prefString = "PSRenderRes";
    valid_prefs[5].prefMethod =  &prefs::input_psRenderRes;
    valid_prefs[5].prefWrite  =  &prefs::format_psRenderRes;
    valid_prefs[5].comment    =  "Postscript rendering resolution (DPI)";

    valid_prefs[6].prefString = "PSDisplayRes";
    valid_prefs[6].prefMethod =  &prefs::input_psDisplayRes;
    valid_prefs[6].prefWrite  =  &prefs::format_psDisplayRes;
    valid_prefs[6].comment    =  "Postscript display resolution (DPI)";

    valid_prefs[7].prefString = "PSRenderDepth";
    valid_prefs[7].prefMethod = &prefs::input_psRenderDepth;
    valid_prefs[7].prefWrite  = &prefs::format_psRenderDepth;
    valid_prefs[7].comment    = "Postscript display depth: colour, grey, BW";

    valid_prefs[8].prefString = "PSRenderSmooth";
    valid_prefs[8].prefMethod = &prefs::input_psRenderSmooth;
    valid_prefs[8].prefWrite  = &prefs::format_psRenderSmooth;
    valid_prefs[8].comment    = "Smooth the Postscript rendering: True or False";

    valid_prefs[9].prefString = "PreviewerPath";
    valid_prefs[9].prefMethod =  &prefs::input_gvpath;
    valid_prefs[9].prefWrite  =  &prefs::format_gvpath;
    valid_prefs[9].comment    =  "Path to Postscript Previewer";
    
    // Advanced stuff
    valid_prefs[10].prefString = "XORBreakLines";
    valid_prefs[10].prefMethod = &prefs::input_xorBreakLines;
    valid_prefs[10].prefWrite  = &prefs::format_xorBreakLines;
    valid_prefs[10].comment    = "32-bit hexadecimal XOR value used when drawing `break' lines";

    valid_prefs[11].prefString = "XORProfileLines";
    valid_prefs[11].prefMethod = &prefs::input_xorProfileLines;
    valid_prefs[11].prefWrite  = &prefs::format_xorProfileLines;
    valid_prefs[11].comment    = "32-bit hexadecimal XOR value used for drawing the profile line";

    valid_prefs[15].prefString = "hideMenu";
    valid_prefs[15].prefMethod = &prefs::input_hideMenu;
    valid_prefs[15].prefWrite  = &prefs::format_hideMenu;
    valid_prefs[15].comment    = "Hide the main menu to provide more useful screen estate";

    // server prefs
    valid_prefs[12].prefString = "PollFrequency";
    valid_prefs[12].prefMethod = &prefs::input_pollFrequency;
    valid_prefs[12].prefWrite  = &prefs::format_pollFrequency;
    valid_prefs[12].comment    = "Polling frequency for imview server";

    valid_prefs[13].prefString = "RequirePassword";
    valid_prefs[13].prefMethod = &prefs::input_requirePassword;
    valid_prefs[13].prefWrite  = &prefs::format_requirePassword;
    valid_prefs[13].comment    = "Login to imview server requires a password";

    valid_prefs[14].prefString = "LocalhostOnly";
    valid_prefs[14].prefMethod = &prefs::input_localhostOnly;
    valid_prefs[14].prefWrite  = &prefs::format_localhostOnly;
    valid_prefs[14].comment    = "Only processes on the local host can log onto the imview server";



    
    dbgprintf("Preference structure constructed\n");
    

    // set up the generic preferences
    // General
    input_prefspath("");
    input_smoothZoomout(falsestr);
    input_keepPoints(falsestr);
    input_mouseZoomType(CONSTRAINEDstr);
    input_overlayTransparency("0.0");
    // Postscript
    input_gspath("gs");
    input_gvpath("ghostview");
    input_psRenderRes("150");
    input_psDisplayRes("75");
    input_psRenderDepth(BWstr);
    input_psRenderSmooth(truestr);
    // server stuff
    input_pollFrequency("100");
    input_requirePassword(falsestr);
    input_localhostOnly(falsestr);
    // Expert stuff
    input_hideMenu(falsestr);
    // Hack stuff
    input_xorBreakLines("0xffffeffe");
    input_xorProfileLines("0x1b2dc3a4");
	
    
    prefitems_ = new map<string,string,Nocase>;
}

prefs::~prefs()
{
//    delete prefitems_;
    delete[] valid_prefs;
}

// returns 0 if prefs not read
int prefs::readprefs(const char *fromfile)
{
    int      res = 0;
    string   keystr, valuestr;
    char     ch;
    map <string, string, Nocase> &prefitems = *prefitems_;     // easier access;
    CMI      p; // constant iterator

    // declare and initialize `from'
    ifstream isfrom(fromfile);
    
    if (!isfrom) {
	warnprintf("No preference input file %s\n", fromfile);
	res = 0;
    } else {
	res = 1;

        // read the whole lot by pairs, skip to the end of the line
	while (isfrom >> keystr) {
	    if (keystr[0] != '#') { // ignore comments
 		// read the value as well
  		if (isfrom >> valuestr) {
		    //cout << keystr << ":  " << valuestr << endl;
		    // according to insure this creates a leak but I don't believe it
  		    prefitems[keystr] = valuestr;
 		}
 	    }
	    // read to the end of the line
	    while (isfrom.get(ch) && (ch != '\n'))
	      ;
	}

	
	
	if (debugIsOn) {
	    dbgprintf("Content of preference file:\n");
	    for (p = prefitems.begin() ; p != prefitems.end() ; p++) {
		dbgprintf("   %s : %s\n", (p->first).c_str(),  (p->second).c_str());
	    }
	}

	// interpret the content of the map.
	for (int i = 0; i < MAXPREFS ; i++) {
	    if ((p = prefitems.find(valid_prefs[i].prefString)) != prefitems.end()) {
		// item found, call the relevant method
		dbgprintf("Valid preference found: %s, argument:%s\n", (p->first).c_str(), (p->second).c_str());
		(this->*(valid_prefs[i].prefMethod))(p->second);
	    }
	}

    }

    return res;
}


int prefs::writeprefs(const char *tofile)
{
    int                           res = 0;
    ofstream                      to(tofile);
    time_t                        now;
    string                        dummyStr;
    
    if (!to) {
	errprintf("Couldn't save preferences to %s, reason: %s\n",
		   tofile,
		   strerror(errno));
	res = 0;
    } else {
	res = 1;


	// write a few comments;
	now = time(0);
	to << "#" << endl << "# Preferences saved on " << ctime(&now); // ctime ends with '\n'
	to << "# Argument names and value are not case-sensitive\n" << endl;
	// write the whole lot by pairs, including comments this time.
	for (int i = 0 ; i < MAXPREFS ; i++) {
#ifndef PICKY
		to << std::setw((int)8) << valid_prefs[i].prefString << "\t"
	       << (this->*(valid_prefs[i].prefWrite))() << "\t"
	       << "# " << valid_prefs[i].comment << endl ;
#else
	    to << valid_prefs[i].prefString << "\t"
	       << (this->*(valid_prefs[i].prefWrite))() << "\t"
	       << "# " << valid_prefs[i].comment << endl ;
#endif
	}

    }

    return res;
}

// private functions




psrendertype prefs::interpretPSRenderString(const string &s)
{
    if (cmp_nocase(s,"GREY") == 0)
	return PSREND_GREY;
    else if (cmp_nocase(s, "COLOUR") == 0)
	return PSREND_COLOUR;
    else
	return PSREND_BW;  // this is the default;
}


// quick functions
// input functions
void prefs::input_prefspath(const string &s) {prefspath_ = s;}
void prefs::input_smoothZoomout(const string &s) {smoothZoomout_ = (cmp_nocase(s, truestr) ==  0);}
void prefs::input_keepPoints(const string &s) {keepPoints_ = (cmp_nocase(s, truestr) ==  0);}
void prefs::input_mouseZoomType(const string &s) {
	mouseZoomType_ = (cmp_nocase(s, UNCONSTRAINEDstr)==0) ? ZOOM_UNCONSTRAINED:ZOOM_CONSTRAINED; 
}
void prefs::input_overlayTransparency(const string &s) { overlayTransparency_ = atof(s.c_str());}
void prefs::input_overlayTransparency(double v) { overlayTransparency_ = v;}
void prefs::input_gspath(const string &s) { gspath_ = s; }
void prefs::input_gvpath(const string &s) { gvpath_ = s; }
void prefs::input_psRenderRes(const string &s) { psRenderRes_ = s;}
void prefs::input_psDisplayRes(const string &s) { psDisplayRes_ = s;}
void prefs::input_psRenderDepth(const string &s) { psRenderDepth_ =  interpretPSRenderString(s);}
void prefs::input_psRenderSmooth(const string &s) { psRenderSmooth_ = (cmp_nocase(s, truestr) ==  0);}
void prefs::input_xorBreakLines(const string &s) { xorBreakLines_ = s; }
void prefs::input_xorProfileLines(const string &s) { xorProfileLines_ = s; }
void prefs::input_pollFrequency(const string &s) { pollFrequency_ = atof(s.c_str());}
void prefs::input_requirePassword(const string &s) { requirePassword_ = (cmp_nocase(s, truestr) == 0);}
void prefs::input_localhostOnly(const string &s) { localhostOnly_ = (cmp_nocase(s, truestr) == 0);}
void prefs::input_hideMenu(const string &s) { hideMenu_ = (cmp_nocase(s, truestr) == 0);}

// output functions
string& prefs::format_prefspath(void)       {return prefspath_;}
string& prefs::format_smoothZoomout(void)   {return (smoothZoomout_? truestr:falsestr);}
string& prefs::format_keepPoints(void)      {return (keepPoints_ ? truestr:falsestr);}
string& prefs::format_mouseZoomType(void)   {return (mouseZoomType_ == ZOOM_CONSTRAINED) ? CONSTRAINEDstr:UNCONSTRAINEDstr;}
string& prefs::format_overlayTransparency(void) {
    ostringstream os;
    static string as; // we are returning a reference
    os << overlayTransparency_;
    as = os.str();
    return as;
}
string& prefs::format_gspath(void)          {return gspath_;}
string& prefs::format_gvpath(void)          {return gvpath_;}
string& prefs::format_psRenderRes(void)     {return psRenderRes_;}
string& prefs::format_psDisplayRes(void)    {return psDisplayRes_;}
string& prefs::format_psRenderDepth(void)   {return (psRenderDepth_ == PSREND_BW) ? BWstr:((psRenderDepth_ == PSREND_GREY) ? GREYstr:COLOURstr);}
string& prefs::format_psRenderSmooth(void)  {return (psRenderSmooth_ ? truestr:falsestr);} 
string& prefs::format_xorBreakLines(void)   {return xorBreakLines_;}
string& prefs::format_xorProfileLines(void) {return xorProfileLines_;}
string& prefs::format_pollFrequency(void) {
    ostringstream os;
    static string as; // necessary as we are returning a reference
    os << pollFrequency_ ;
    as = os.str();
    return as;
}
string& prefs::format_requirePassword(void) {return (requirePassword_ ? truestr:falsestr);}
string& prefs::format_localhostOnly(void) {return (localhostOnly_ ? truestr:falsestr);}
string& prefs::format_hideMenu(void) {return (hideMenu_ ? truestr:falsestr);}
