/*

Copyright (C) 2007 John W. Eaton

This file is part of Octave.

Octave 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 3 of the License, or (at your
option) any later version.

Octave 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 Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

#if !defined (graphics_h)
#define graphics_h 1

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <cctype>

#include <algorithm>
#include <list>
#include <map>
#include <set>
#include <string>

#include "gripes.h"
#include "oct-map.h"
#include "ov.h"

class caseless_str : public std::string
{
public:
  typedef std::string::iterator iterator;
  typedef std::string::const_iterator const_iterator;

  caseless_str (void) : std::string () { }
  caseless_str (const std::string& s) : std::string (s) { }
  caseless_str (const char *s) : std::string (s) { }

  caseless_str (const caseless_str& name) : std::string (name) { }

  caseless_str& operator = (const caseless_str& pname)
  {
    std::string::operator = (pname);
    return *this;
  }

  operator std::string (void) const { return *this; }

  // Case-insensitive comparison.
  bool compare (const std::string& s, size_t limit = NPOS) const
  {
    const_iterator p1 = begin ();
    const_iterator p2 = s.begin ();

    size_t k = 0;

    while (p1 != end () && p2 != s.end () && k++ < limit)
      {
	if (std::tolower (*p1) != std::tolower (*p2))
	  return false;

	*p1++;
	*p2++;
      }

    return (limit == NPOS) ? size () == s.size () : k == limit;
  }
};

// ---------------------------------------------------------------------

class graphics_handle
{
public:
  graphics_handle (void) : val (octave_NaN) { }

  graphics_handle (const octave_value& a);

  graphics_handle (int a) : val (a) { }

  graphics_handle (double a) : val (a) { }

  graphics_handle (const graphics_handle& a) : val (a.val) { }

  graphics_handle& operator = (const graphics_handle& a)
  {
    if (&a != this)
      val = a.val;

    return *this;
  }

  ~graphics_handle (void) { }

  double value (void) const { return val; }

  octave_value as_octave_value (void) const
  {
    return ok () ? octave_value (val) : octave_value (Matrix ());
  }

  graphics_handle operator ++ (void)
  {
    ++val;
    return *this;
  }

  graphics_handle operator ++ (int)
  {
    graphics_handle h = *this;
    ++val;
    return h;
  }

  graphics_handle operator -- (void)
  {
    --val;
    return *this;
  }

  graphics_handle operator -- (int)
  {
    graphics_handle h = *this;
    --val;
    return h;
  }

  bool ok (void) const { return ! xisnan (val); }

private:
  double val;
};

inline bool
operator == (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () == b.value ();
}

inline bool
operator != (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () != b.value ();
}

inline bool
operator < (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () < b.value ();
}

inline bool
operator <= (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () <= b.value ();
}

inline bool
operator >= (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () >= b.value ();
}

inline bool
operator > (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () > b.value ();
}

// ---------------------------------------------------------------------

class property;

class base_property
{
public:
  friend class property;

public:
  base_property (void) : count (0) { }

  base_property (const std::string& s, const graphics_handle& h)
    : count (0), name (s), parent (h), hidden (false) { }

  base_property (const base_property& p)
    : count (0), name (p.name), parent (p.parent), hidden (p.hidden) { }

  virtual ~base_property (void) { }

  bool ok (void) const { return parent.ok (); }

  std::string get_name (void) const { return name; }

  void set_name (const std::string& s) { name = s; }

  graphics_handle get_parent (void) const { return parent; }

  void set_parent (const graphics_handle &h) { parent = h; }

  bool is_hidden (void) const { return hidden; }

  void set_hidden (bool flag) { hidden = flag; }

  virtual void set (const octave_value&)
    { error ("set: invalid property \"%s\"", name.c_str ()); }

  virtual octave_value get (void) const
    {
      error ("get: invalid property \"%s\"", name.c_str ());
      return octave_value ();
    }

  base_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  int count;
  std::string name;
  graphics_handle parent;
  bool hidden;
};

// ---------------------------------------------------------------------

class string_property : public base_property
{
public:
  string_property (const std::string& s, const graphics_handle& h,
                   const std::string& val = "")
    : base_property (s, h), str (val) { }

  string_property (const string_property& p)
    : base_property (p), str (p.str) { }

  void set (const octave_value& val)
    {
      if (val.is_string ())
        str = val.string_value ();
      else
        error ("set: invalid string property value for \"%s\"",
               get_name ().c_str ());
    }

  octave_value get (void) const
    { return octave_value (str); }

  std::string string_value (void) const { return str; }

  string_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  std::string str;
};

// ---------------------------------------------------------------------

class radio_values
{
public:
  OCTINTERP_API radio_values (const std::string& opt_string = std::string ());

  radio_values (const radio_values& a)
    : default_val (a.default_val), possible_vals (a.possible_vals) { }

  radio_values& operator = (const radio_values& a)
  {
    if (&a != this)
      {
	default_val = a.default_val;
	possible_vals = a.possible_vals;
      }

    return *this;
  }

  std::string default_value (void) const { return default_val; }

  bool validate (const std::string& val)
  {
    bool retval = true;

    if (! contains (val))
      {
	error ("invalid value = %s", val.c_str ());
	retval = false;
      }

    return retval;
  }
  
  bool contains (const std::string& val)
  {
    return (possible_vals.find (val) != possible_vals.end ());
  }

private:
  // Might also want to cache
  std::string default_val;
  std::set<caseless_str> possible_vals;
};

class radio_property : public base_property
{
public:
  radio_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v = radio_values ())
    : base_property (nm, h),
      vals (v), current_val (v.default_value ()) { }

  radio_property (const std::string& nm, const graphics_handle& h,
                  const std::string& v)
    : base_property (nm, h),
      vals (v), current_val (vals.default_value ()) { }

  radio_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v, const std::string& def)
    : base_property (nm, h),
      vals (v), current_val (def) { }

  radio_property (const radio_property& p)
    : base_property (p), vals (p.vals), current_val (p.current_val) { }

  void set (const octave_value& newval) 
  {
    if (newval.is_string ())
      {
        std::string s = newval.string_value ();
        if (vals.validate (s))
          current_val = s;
        else
          error ("set: invalid value for radio property \"%s\" (value = %s)",
              get_name ().c_str (), s.c_str ());
      }
    else	
      error ("set: invalid value for radio property \"%s\"",
          get_name ().c_str ());
  }

  octave_value get (void) const { return octave_value (current_val); }

  const std::string& current_value (void) const { return current_val; }

  bool is (const caseless_str& v) const
    { return v.compare (current_val); }

  radio_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  radio_values vals;
  std::string current_val;
};

// ---------------------------------------------------------------------

class color_values
{
public:
  color_values (double r = 0, double g = 0, double b = 1)
    : xrgb (1, 3)
  {
    xrgb(0) = r;
    xrgb(1) = g;
    xrgb(2) = b;

    validate ();
  }

  color_values (std::string str)
    : xrgb (1, 3)
  {
    if (! str2rgb (str))
      error ("invalid color specification: %s", str.c_str ());
  }

  color_values (const color_values& c)
    : xrgb (c.xrgb)
  { }

  color_values& operator = (const color_values& c)
  {
    if (&c != this)
      xrgb = c.xrgb;

    return *this;
  }

  Matrix rgb (void) const { return xrgb; }

  operator octave_value (void) const { return xrgb; }

  void validate (void) const
  {
    for (int i = 0; i < 3; i++)
      {
	if (xrgb(i) < 0 ||  xrgb(i) > 1)
	  {
	    error ("invalid RGB color specification");
	    break;
	  }
      }
  }

private:
  Matrix xrgb;

  OCTINTERP_API bool str2rgb (std::string str);
};

class color_property : public base_property
{
public:
  color_property (const color_values& c, const radio_values& v)
    : base_property ("", graphics_handle ()),
      current_type (color_t), color_val (c), radio_val (v),
      current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const color_values& c = color_values (),
                  const radio_values& v = radio_values ())
    : base_property (nm, h),
      current_type (color_t), color_val (c), radio_val (v),
      current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v)
    : base_property (nm, h),
      current_type (radio_t), color_val (color_values ()), radio_val (v),
      current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const std::string& v)
    : base_property (nm, h),
      current_type (radio_t), color_val (color_values ()), radio_val (v),
      current_val (radio_val.default_value ())
  { }
  
  color_property (const std::string& nm, const graphics_handle& h,
                  const color_property& v)
    : base_property (nm, h),
      current_type (v.current_type), color_val (v.color_val),
      radio_val (v.radio_val), current_val (v.current_val)
  { }

  color_property (const color_property& p)
    : base_property (p), current_type (p.current_type),
      color_val (p.color_val), radio_val (p.radio_val),
      current_val (p.current_val) { }

  octave_value get (void) const
  {
    if (current_type == color_t)
      return color_val.rgb ();

    return current_val;
  }

  OCTINTERP_API void set (const octave_value& newval);

  bool is_rgb (void) const { return (current_type == color_t); }

  bool is_radio (void) const { return (current_type == radio_t); }

  bool is (const std::string& v) const
    { return (is_radio () && current_val == v); }

  Matrix rgb (void) const
  {
    if (current_type != color_t)
      error ("color has no rgb value");

    return color_val.rgb ();
  }

  const std::string& current_value (void) const
  {
    if (current_type != radio_t)
      error ("color has no radio value");

    return current_val;
  }

  color_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

  operator octave_value (void) const { return get (); }

private:
  enum current_enum { color_t, radio_t } current_type;
  color_values color_val;
  radio_values radio_val;
  std::string current_val;
};

// ---------------------------------------------------------------------

class double_property : public base_property
{
public:
  double_property (const std::string& nm, const graphics_handle& h,
                   double d = 0)
    : base_property (nm, h),
      current_val (d) { }

  double_property (const double_property& p)
    : base_property (p), current_val (p.current_val) { }

  void set (const octave_value& v)
    {
      if (v.is_scalar_type () && v.is_real_type ())
        current_val = v.double_value ();
      else
        error ("set: invalid value for double property \"%s\"",
               get_name ().c_str ());
    }

  octave_value get (void) const { return octave_value (current_val); }

  double double_value (void) const { return current_val; }

  double_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  double current_val;
};

// ---------------------------------------------------------------------

class array_property : public base_property
{
public:
  array_property (const std::string& nm, const graphics_handle& h,
                  const octave_value& m)
    : base_property (nm, h), data (m) { }

  octave_value get (void) const { return data; }

  void set (const octave_value& v)
    {
      if (validate (v))
        data = v;
      else
        error ("invalid value for array property \"%s\"",
               get_name ().c_str ());
    }

  void add_constraint (const std::string& type)
    { type_constraints.push_back (type); }

  void add_constraint (dim_vector dims)
    { size_constraints.push_back (dims); }

  array_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  OCTINTERP_API bool validate (const octave_value& v);

private:
  octave_value data;
  std::list<std::string> type_constraints;
  std::list<dim_vector> size_constraints;
};

// ---------------------------------------------------------------------

class data_property : public base_property
{
public:
  data_property (void)
    : base_property ("", graphics_handle ()) { }

  data_property (const std::string& nm, const graphics_handle& h,
                 const NDArray& m = NDArray ())
    : base_property (nm, h),
      data (m), xmin (octave_Inf), xmax (-octave_Inf), xminp (octave_Inf)
  {
    get_data_limits ();
  }

  data_property (const std::string& nm, const graphics_handle& h,
                 const Matrix& m)
    : base_property (nm, h),
      data (m), xmin (octave_Inf), xmax (-octave_Inf), xminp (octave_Inf)
  {
    get_data_limits ();
  }

  data_property (const data_property& p)
    : base_property (p), data (p.data),
      xmin (p.xmin), xmax (p.xmax), xminp (p.xminp) { }

  void set (const octave_value& val)
  {
    data = val.array_value ();

    get_data_limits ();
  }

  octave_value get (void) const { return data; }

  NDArray array_value (void) const { return data; }

  Matrix matrix_value (void) const { return data.matrix_value (); }

  double min_val (void) const { return xmin; }
  double max_val (void) const { return xmax; }
  double min_pos (void) const { return xminp; }

  data_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  NDArray data;
  double xmin;
  double xmax;
  double xminp;

  void get_data_limits (void)
  {
    octave_idx_type nel = data.numel ();

    xmin = xminp = octave_Inf;
    xmax = -octave_Inf;

    if (nel > 0)
      {
	const double *d = data.data ();

	for (octave_idx_type i = 0; i < nel; i++)
	  {
	    double val = d[i];

	    if (! (xisinf (val) || xisnan (val)))
	      {
		if (val < xmin)
		  xmin = val;

		if (val > xmax)
		  xmax = val;

		if (val > 0 && val < xminp)
		  xminp = val;
	      }
	  }
      }
  }
};

// ---------------------------------------------------------------------

class bool_property : public radio_property
{
public:
  bool_property (const std::string& nm, const graphics_handle& h,
                 bool val)
    : radio_property (nm, h, radio_values (val ? "{on}|off" : "on|{off}"))
    { }

  bool_property (const std::string& nm, const graphics_handle& h,
                 const char* val)
    : radio_property (nm, h, radio_values ("on|off"), val)
    { }

  bool_property (const bool_property& p)
    : radio_property (p) { }

  void set (const octave_value& val)
    {
      if (val.is_bool_scalar ())
        radio_property::set (val.bool_value () ? "on" : "off");
      else
        radio_property::set (val);
    }

  bool is_on (void) const { return is ("on"); }
  
  bool_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }
};

// ---------------------------------------------------------------------

class handle_property : public base_property
{
public:
  handle_property (const std::string& nm, const graphics_handle& h,
                   const graphics_handle& val = graphics_handle ())
    : base_property (nm, h),
      current_val (val) { }

  handle_property (const handle_property& p)
    : base_property (p), current_val (p.current_val) { }

  OCTINTERP_API void set (const octave_value& v);

  octave_value get (void) const { return current_val.as_octave_value (); }

  graphics_handle handle_value (void) const { return current_val; }

  handle_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

  handle_property& operator = (const graphics_handle& h)
    {
      set (octave_value (h.value ()));
      return *this;
    }

private:
  graphics_handle current_val;
};

// ---------------------------------------------------------------------

class any_property : public base_property
{
public:
  any_property (const std::string& nm, const graphics_handle& h,
                  const octave_value& m = Matrix ())
    : base_property (nm, h), data (m) { }

  octave_value get (void) const { return data; }

  void set (const octave_value& v) { data = v; }

  any_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  octave_value data;
};

// ---------------------------------------------------------------------

class callback_property : public base_property
{
public:
  callback_property (const std::string& nm, const graphics_handle& h,
                     const octave_value& m)
    : base_property (nm, h), callback (m) { }

  octave_value get (void) const { return callback; }

  void set (const octave_value& v)
    {
      if (validate (v))
        callback = v;
      else
        error ("invalid value for callback property \"%s\"",
               get_name ().c_str ());
    }

  OCTINTERP_API void execute (const octave_value& data = octave_value ()) const;

  callback_property& operator = (const octave_value& val)
    {
      set (val);
      return *this;
    }

private:
  OCTINTERP_API bool validate (const octave_value& v) const;

private:
  octave_value callback;
};

// ---------------------------------------------------------------------

class property
{
public:
  property (void) : rep (new base_property ("", graphics_handle ()))
    { rep->count++; }

  property (base_property *bp, bool persist = false) : rep (bp)
    { rep->count++; if (persist) rep->count++; }

  property (const property& p)
    {
      rep = p.rep;
      rep->count++;
    }

  ~property (void)
    {
      if (--rep->count <= 0)
        delete rep;
    }

  bool ok (void) const
    { return rep->ok (); }

  std::string get_name (void) const
    { return rep->get_name (); }

  void set_name (const std::string& name)
    { rep->set_name (name); }

  graphics_handle get_parent (void) const
    { return rep->get_parent (); }

  void set_parent (const graphics_handle& h)
    { rep->set_parent (h); }

  bool is_hidden (void) const
    { return rep->is_hidden (); }

  void set_hidden (bool flag)
    { rep->set_hidden (flag); }

  octave_value get (void) const
    { return rep->get (); }

  void set (const octave_value& val)
    { rep->set (val); }

  property& operator = (const octave_value& val)
    {
      *rep = val;
      return *this;
    }

  property& operator = (const property& p)
    {
      if (rep && --rep->count <= 0)
        delete rep;
      
      rep = p.rep;
      rep->count++;

      return *this;
    }

  /*
  const string_property& as_string_property (void) const
    { return *(dynamic_cast<string_property*> (rep)); }

  const radio_property& as_radio_property (void) const
    { return *(dynamic_cast<radio_property*> (rep)); }

  const color_property& as_color_property (void) const
    { return *(dynamic_cast<color_property*> (rep)); }

  const double_property& as_double_property (void) const
    { return *(dynamic_cast<double_property*> (rep)); }

  const data_property& as_data_property (void) const
    { return *(dynamic_cast<data_property*> (rep)); }
  
  const bool_property& as_bool_property (void) const
    { return *(dynamic_cast<bool_property*> (rep)); }
  
  const handle_property& as_handle_property (void) const
    { return *(dynamic_cast<handle_property*> (rep)); }
    */

private:
  base_property *rep;
};

// ---------------------------------------------------------------------

class property_list
{
public:
  typedef std::map<std::string, octave_value> pval_map_type;
  typedef std::map<std::string, pval_map_type> plist_map_type;
  
  typedef pval_map_type::iterator pval_map_iterator;
  typedef pval_map_type::const_iterator pval_map_const_iterator;

  typedef plist_map_type::iterator plist_map_iterator;
  typedef plist_map_type::const_iterator plist_map_const_iterator;

  property_list (const plist_map_type& m = plist_map_type ())
    : plist_map (m) { }

  ~property_list (void) { }

  void set (const caseless_str& name, const octave_value& val);

  octave_value lookup (const caseless_str& name) const;

  plist_map_iterator begin (void) { return plist_map.begin (); }
  plist_map_const_iterator begin (void) const { return plist_map.begin (); }

  plist_map_iterator end (void) { return plist_map.end (); }
  plist_map_const_iterator end (void) const { return plist_map.end (); }

  plist_map_iterator find (const std::string& go_name)
  {
    return plist_map.find (go_name);
  }

  plist_map_const_iterator find (const std::string& go_name) const
  {
    return plist_map.find (go_name);
  }

  Octave_map as_struct (const std::string& prefix_arg) const;

private:
  plist_map_type plist_map;
};

// ---------------------------------------------------------------------

class graphics_backend;

class base_graphics_backend
{
public:
  friend class graphics_backend;

public:
  base_graphics_backend (const std::string& nm)
      : name (nm), count (0) { }

  virtual ~base_graphics_backend (void) { }

  std::string get_name (void) const { return name; }

  virtual bool is_valid (void) const { return false; }

  virtual void close_figure (const octave_value&) const
    { error ("close_figure: invalid graphics backend"); }

  virtual void redraw_figure (const graphics_handle&) const
    { error ("redraw_figure: invalid graphics backend"); }

  virtual void print_figure (const graphics_handle&, const std::string&,
			     const std::string&, bool,
			     const std::string& = "") const
    { error ("print_figure: invalid graphics backend"); }

  virtual Matrix get_canvas_size (const graphics_handle&) const
    {
      error ("get_canvas_size: invalid graphics backend");
      return Matrix (1, 2, 0.0);
    }

private:
  std::string name;
  int count;
};

class graphics_backend
{
public:
  graphics_backend (void)
      : rep (new base_graphics_backend ("unknown"))
    {
      rep->count++;
    }

  graphics_backend (base_graphics_backend* b)
      : rep (b)
    {
      rep->count++;
    }

  graphics_backend (const graphics_backend& b)
      : rep (b.rep)
    {
      rep->count++;
    }

  ~graphics_backend (void)
    {
      if (--rep->count == 0)
	delete rep;
    }

  graphics_backend& operator = (const graphics_backend& b)
    {
      if (rep != b.rep)
	{
	  if (--rep->count == 0)
	    delete rep;

	  rep = b.rep;
	  rep->count++;
	}

      return *this;
    }

  operator bool (void) const { return rep->is_valid (); }

  std::string get_name (void) const { return rep->get_name (); }

  void close_figure (const octave_value& pstream) const
    { rep->close_figure (pstream); }

  void redraw_figure (const graphics_handle& fh) const
    { rep->redraw_figure (fh); }
  
  void print_figure (const graphics_handle& fh, const std::string& term,
		     const std::string& file, bool mono,
		     const std::string& debug_file = "") const
    { rep->print_figure (fh, term, file, mono, debug_file); }

  Matrix get_canvas_size (const graphics_handle& fh) const
    { return rep->get_canvas_size (fh); }

  OCTINTERP_API static graphics_backend default_backend (void);

  static void register_backend (const graphics_backend& b)
    { available_backends[b.get_name ()] = b; }

  static void unregister_backend (const std::string& name)
    { available_backends.erase (name); }

private:
  base_graphics_backend *rep;

private:
  static std::map<std::string, graphics_backend> available_backends;
};

// ---------------------------------------------------------------------

class base_graphics_object;

class OCTINTERP_API base_properties
{
public:
  base_properties (const std::string& ty = "unknown",
                   const graphics_handle& mh = graphics_handle (),
                   const graphics_handle& p = graphics_handle ())
    : beingdeleted ("beingdeleted", mh, false),
      busyaction ("parent", mh, "{queue}|cancel"),
      buttondownfcn ("buttondownfcn", mh, Matrix ()),
      children (),
      clipping ("clipping", mh, true),
      createfcn ("createfcn", mh, Matrix ()),
      deletefcn ("deletefcn", mh, Matrix ()),
      handlevisibility ("handlevisibility", mh, "{on}|callback|off"),
      hittest ("hittest", mh, true),
      interruptible ("interruptible", mh, true),
      parent ("parent", mh, p), 
      selected ("selected", mh, false),
      selectionhighlight ("selectionhighlight", mh, true),
      tag ("tag", mh),
      type ("type", mh, ty),
      userdata ("userdata", mh, Matrix ()),
      visible ("visible", mh, true),
      __modified__ ("__modified__", mh, true),
      __myhandle__ (mh),
      uicontextmenu ("uicontextmenu", mh, graphics_handle ())
    { }

  virtual ~base_properties (void) { }

  virtual std::string graphics_object_name (void) const { return "unknonwn"; }

  void mark_modified (void);

  void override_defaults (base_graphics_object& obj);

  // Look through DEFAULTS for properties with given CLASS_NAME, and
  // apply them to the current object with set (virtual method).

  void set_from_list (base_graphics_object& obj, property_list& defaults);

  void insert_property (const std::string& name, property p)
    {
      p.set_name (name);
      p.set_parent (__myhandle__);
      all_props[name] = p;
    }

  virtual void set (const caseless_str&, const octave_value&);

  virtual octave_value get (const caseless_str&) const;

  virtual octave_value get (bool all = false) const;

  property get_property (const caseless_str&) const;

  std::string get_tag (void) const { return tag.string_value (); }

  graphics_handle get_parent (void) const { return parent.handle_value (); }

  std::string get_type (void) const { return type.string_value (); }

  bool is_modified (void) const { return __modified__.is_on (); }
 
  graphics_handle get___myhandle__ (void) const { return __myhandle__; }
  
  std::string get_busyaction (void) const { return busyaction.current_value (); }

  octave_value get_buttondownfcn (void) const { return buttondownfcn.get (); }

  std::string get_clipping (void) const { return clipping.current_value (); }

  void execute_createfcn (const octave_value& data = octave_value ()) const
    { createfcn.execute (data); }

  octave_value get_createfcn (void) const { return createfcn.get (); }

  void execute_deletefcn (const octave_value& data = octave_value ()) const
    { deletefcn.execute (data); }

  octave_value get_deletefcn (void) const { return deletefcn.get (); }

  std::string get_handlevisibility (void) const { return handlevisibility.current_value (); }

  std::string get_hittest (void) const { return hittest.current_value (); }

  std::string get_interruptible (void) const { return interruptible.current_value (); }

  std::string get_selected (void) const { return selected.current_value (); }

  std::string get_selectionhighlight (void) const { return selectionhighlight.current_value (); }
  
  octave_value get_uicontextmenu (void) const { return uicontextmenu.get (); }

  octave_value get_userdata (void) const { return userdata.get (); }
 
  bool is_visible (void) const { return visible.is_on (); }
  std::string get_visible (void) const { return visible.current_value (); }

  bool is_beingdeleted (void) const { return beingdeleted.is_on (); }
  std::string get_beingdeleted (void) const { return beingdeleted.current_value (); }

  virtual void remove_child (const graphics_handle& h);

  virtual void adopt (const graphics_handle& h)
  {
    octave_idx_type n = children.numel ();
    children.resize (1, n+1);
    children(n) = h.value ();
  }

  virtual graphics_backend get_backend (void) const;

  void set_tag (const octave_value& val) { tag = val; }

  void set_parent (const octave_value& val);

  void set_modified (const octave_value& val) { __modified__ = val; }

  void set_busyaction (const octave_value& val)
  {
    if (! error_state)
      {
        busyaction = val;
        mark_modified ();
      }
  }

  void set_buttondownfcn (const octave_value& val)
  {
    if (! error_state)
      {
        buttondownfcn = val;
        mark_modified ();
      }
  }

  void set_clipping (const octave_value& val)
  {
    if (! error_state)
      {
        clipping = val;
        mark_modified ();
      }
  }

  void set_createfcn (const octave_value& val)
  {
    if (! error_state)
      {
        createfcn = val;
        mark_modified ();
      }
  }

  void set_deletefcn (const octave_value& val)
  {
    if (! error_state)
      {
        deletefcn = val;
        mark_modified ();
      }
  }

  void set_handlevisibility (const octave_value& val)
  {
    if (! error_state)
      {
        handlevisibility = val;
        mark_modified ();
      }
  }

  void set_hittest (const octave_value& val)
  {
    if (! error_state)
      {
        hittest = val;
        mark_modified ();
      }
  }

  void set_interruptible (const octave_value& val)
  {
    if (! error_state)
      {
        interruptible = val;
        mark_modified ();
      }
  }

  void set_selected (const octave_value& val)
  {
    if (! error_state)
      {
        selected = val;
        mark_modified ();
      }
  }

  void set_selectionhighlight (const octave_value& val)
  {
    if (! error_state)
      {
        selectionhighlight = val;
        mark_modified ();
      }
  }

  void set_uicontextmenu (const octave_value& val)
  {
    if (! error_state)
      {
        uicontextmenu = val;
        mark_modified ();
      }
  }

  void set_userdata (const octave_value& val)
  {
    if (! error_state)
      {
        userdata = val;
        mark_modified ();
      }
  }

  virtual void set_visible (const octave_value& val)
  {
    if (! error_state)
      {
        visible = val;
        mark_modified ();
      }
  }

  void set_beingdeleted (const octave_value& val)
  {
    if (! error_state)
      {
	beingdeleted = val;
	mark_modified ();
      }
  }



  void reparent (const graphics_handle& new_parent) { parent = new_parent; }

  // Update data limits for AXIS_TYPE (xdata, ydata, etc.) in the parent
  // axes object.

  virtual void update_axis_limits (const std::string& axis_type) const;

  virtual void delete_children (void);

  Matrix get_children (void) const { return children; }

  // FIXME -- these functions should be generated automatically by the
  // genprops.awk script.
  //
  // EMIT_BASE_PROPERTIES_GET_FUNCTIONS
  virtual data_property get_xdata_property (void) const
  {
    error ("get: invalid property \"xdata\"");
    return data_property ();
  }

  virtual data_property get_ydata_property (void) const
  {
    error ("get: invalid property \"ydata\"");
    return data_property ();
  }

  virtual data_property get_zdata_property (void) const
  {
    error ("get: invalid property \"zdata\"");
    return data_property ();
  }

  virtual data_property get_ldata_property (void) const
  {
    error ("get: invalid property \"ldata\"");
    return data_property ();
  }

  virtual data_property get_udata_property (void) const
  {
    error ("get: invalid property \"udata\"");
    return data_property ();
  }

  virtual data_property get_xldata_property (void) const
  {
    error ("get: invalid property \"xldata\"");
    return data_property ();
  }

  virtual data_property get_xudata_property (void) const
  {
    error ("get: invalid property \"xudata\"");
    return data_property ();
  }

  virtual data_property get_cdata_property (void) const
  {
    error ("get: invalid property \"cdata\"");
    return data_property ();
  }

protected:
  // properties common to all objects
  bool_property beingdeleted;
  radio_property busyaction;
  callback_property buttondownfcn;
  // FIXME: use a property class for children
  Matrix children;
  bool_property clipping;
  callback_property createfcn;
  callback_property deletefcn;
  radio_property handlevisibility;
  bool_property hittest;
  bool_property interruptible;
  handle_property parent;
  bool_property selected;
  bool_property selectionhighlight;
  string_property tag;
  string_property type;
  any_property userdata;
  bool_property visible;
  // additional (octave-specific) properties
  bool_property __modified__;
  graphics_handle __myhandle__;
  // FIXME: should this really be here?
  handle_property uicontextmenu;

protected:
  std::map<caseless_str, property> all_props;

protected:
  void insert_static_property (const std::string& name, base_property& p)
    { insert_property (name, property (&p, true)); }
  
  virtual void init (void) { }
};

class OCTINTERP_API base_graphics_object
{
public:
  friend class graphics_object;

  base_graphics_object (void) : count (1) { }

  base_graphics_object (const base_graphics_object&) { }

  virtual ~base_graphics_object (void) { }

  virtual void mark_modified (void)
  {
    if (valid_object ())
      get_properties ().mark_modified ();
    else
      error ("base_graphics_object::mark_modified: invalid graphics object");
  }

  virtual void override_defaults (base_graphics_object& obj)
  {
    if (valid_object ())
      get_properties ().override_defaults (obj);
    else
      error ("base_graphics_object::override_defaults: invalid graphics object");
  }

  virtual void set_from_list (property_list& plist)
  {
    if (valid_object ())
      get_properties ().set_from_list (*this, plist);
    else
      error ("base_graphics_object::set_from_list: invalid graphics object");
  }

  virtual void set (const caseless_str& pname, const octave_value& pval)
  {
    if (valid_object ())
      get_properties ().set (pname, pval);
    else
      error ("base_graphics_object::set: invalid graphics object");
  }

  virtual void set_defaults (const std::string&)
  {
    error ("base_graphics_object::set_defaults: invalid graphics object");
  }

  virtual octave_value get (bool all = false) const
  {
    if (valid_object ())
      return get_properties ().get (all);
    else
      {
        error ("base_graphics_object::get: invalid graphics object");
        return octave_value ();
      }
  }

  virtual octave_value get (const caseless_str& pname) const
  {
    if (valid_object ())
      return get_properties ().get (pname);
    else
      {
        error ("base_graphics_object::get: invalid graphics object");
        return octave_value ();
      }
  }

  virtual octave_value get_default (const caseless_str&) const;

  virtual octave_value get_factory_default (const caseless_str&) const;

  virtual octave_value get_defaults (void) const
  {
    error ("base_graphics_object::get_defaults: invalid graphics object");
    return octave_value ();
  }

  virtual octave_value get_factory_defaults (void) const
  {
    error ("base_graphics_object::get_factory_defaults: invalid graphics object");
    return octave_value ();
  }

  virtual graphics_handle get_parent (void) const
  {
    if (valid_object ())
      return get_properties ().get_parent ();
    else
      {
        error ("base_graphics_object::get_parent: invalid graphics object");
        return graphics_handle ();
      }
  }

  virtual void remove_child (const graphics_handle& h)
  {
    if (valid_object ())
      get_properties ().remove_child (h);
    else
      error ("base_graphics_object::remove_child: invalid graphics object");
  }

  virtual void adopt (const graphics_handle& h)
  {
    if (valid_object ())
      get_properties ().adopt (h);
    else
      error ("base_graphics_object::adopt: invalid graphics object");
  }

  virtual void reparent (const graphics_handle& np)
  {
    if (valid_object ())
      get_properties ().reparent (np);
    else
      error ("base_graphics_object::reparent: invalid graphics object");
  }

  virtual void defaults (void) const
  {
    if (valid_object ())
      {
        std::string msg = (type () + "::defaults");
        gripe_not_implemented (msg.c_str ());
      }
    else
      error ("base_graphics_object::default: invalid graphics object");
  }

  virtual base_properties& get_properties (void)
  {
    static base_properties properties;
    error ("base_graphics_object::get_properties: invalid graphics object");
    return properties;
  }

  virtual const base_properties& get_properties (void) const
  {
    static base_properties properties;
    error ("base_graphics_object::get_properties: invalid graphics object");
    return properties;
  }

  virtual void update_axis_limits (const std::string&)
  {
    error ("base_graphics_object::update_axis_limits: invalid graphics object");
  }

  virtual bool valid_object (void) const { return false; }

  virtual std::string type (void) const
  {
    return (valid_object () ? get_properties ().graphics_object_name ()
        : "unknown");
  }

  bool isa (const std::string& go_name) const
  {
    return type () == go_name;
  }

  virtual graphics_backend get_backend (void) const
  {
    if (valid_object ())
      return get_properties ().get_backend ();
    else
      {
	error ("base_graphics_object::get_backend: invalid graphics object");
	return graphics_backend ();
      }
  }

protected:
  // A reference count.
  int count;
};

class OCTINTERP_API graphics_object
{
public:
  graphics_object (void) : rep (new base_graphics_object ()) { }

  graphics_object (base_graphics_object *new_rep)
    : rep (new_rep) { }

  graphics_object (const graphics_object& obj)
  {
    rep = obj.rep;
    rep->count++;
  }

  graphics_object& operator = (const graphics_object& obj)
  {
    if (rep != obj.rep)
      {
	if (--rep->count == 0)
	  delete rep;

	rep = obj.rep;
	rep->count++;
      }

    return *this;
  }

  ~graphics_object (void)
  {
    if (--rep->count == 0)
      delete rep;
  }

  void mark_modified (void) { rep->mark_modified (); }

  void override_defaults (base_graphics_object& obj)
  {
    rep->override_defaults (obj);
  }

  void set_from_list (property_list& plist) { rep->set_from_list (plist); }

  void set (const caseless_str& name, const octave_value& val)
  {
    rep->set (name, val);
  }

  void set (const octave_value_list& args);

  void set_defaults (const std::string& mode) { rep->set_defaults (mode); }

  octave_value get (bool all = false) const { return rep->get (all); }

  octave_value get (const caseless_str& name) const
  {
    return name.compare ("default")
      ? get_defaults ()
      : (name.compare ("factory")
	 ? get_factory_defaults () : rep->get (name));
  }

  octave_value get_default (const caseless_str& name) const
  {
    return rep->get_default (name);
  }

  octave_value get_factory_default (const caseless_str& name) const
  {
    return rep->get_factory_default (name);
  }

  octave_value get_defaults (void) const { return rep->get_defaults (); }

  octave_value get_factory_defaults (void) const
  {
    return rep->get_factory_defaults ();
  }

  graphics_handle get_parent (void) const { return rep->get_parent (); }

  void remove_child (const graphics_handle& h) { rep->remove_child (h); }

  void adopt (const graphics_handle& h) { rep->adopt (h); }

  void reparent (const graphics_handle& h) { rep->reparent (h); }

  void defaults (void) const { rep->defaults (); }

  bool isa (const std::string& go_name) const { return rep->isa (go_name); }

  base_properties& get_properties (void) { return rep->get_properties (); }

  const base_properties& get_properties (void) const
  {
    return rep->get_properties ();
  }

  void update_axis_limits (const std::string& axis_type)
  {
    rep->update_axis_limits (axis_type);
  }

  bool valid_object (void) const { return rep->valid_object (); }

  operator bool (void) const { return rep->valid_object (); }

  // FIXME -- these functions should be generated automatically by the
  // genprops.awk script.
  //
  // EMIT_GRAPHICS_OBJECT_GET_FUNCTIONS
  data_property get_xdata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_xdata_property ();
  }

  data_property get_ydata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_ydata_property ();
  }

  data_property get_zdata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_zdata_property ();
  }

  data_property get_ldata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_ldata_property ();
  }

  data_property get_udata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_udata_property ();
  }

  data_property get_xldata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_xldata_property ();
  }

  data_property get_xudata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_xudata_property ();
  }

  data_property get_cdata_property (void) const
  {
    const base_properties& props = get_properties ();
    return props.get_cdata_property ();
  }

  graphics_backend get_backend (void) const { return rep->get_backend (); }

private:
  base_graphics_object *rep;
};

// ---------------------------------------------------------------------

class OCTINTERP_API root_figure : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(root_figure)
      handle_property currentfigure S , graphics_handle ()
    END_PROPERTIES
  };

private:
  properties xproperties;

public:

  root_figure (void) : xproperties (0, graphics_handle ()), default_properties () { }

  ~root_figure (void) { xproperties.delete_children (); }

  void mark_modified (void) { }

  void override_defaults (base_graphics_object& obj)
  {
    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      default_properties.set (name.substr (7), value);
    else
      xproperties.set (name, value);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    if (name.compare ("default", 7))
      return get_default (name.substr (7));
    else if (name.compare ("factory", 7))
      return get_factory_default (name.substr (7));
    else
      retval = xproperties.get (name);

    return retval;
  }

  octave_value get_default (const caseless_str& name) const
  {
    octave_value retval = default_properties.lookup (name);

    if (retval.is_undefined ())
      error ("get: invalid default property `%s'", name.c_str ());

    return retval;
  }

  octave_value get_factory_default (const caseless_str& name) const
  {
    octave_value retval = factory_properties.lookup (name);

    if (retval.is_undefined ())
      error ("get: invalid factory default property `%s'", name.c_str ());

    return retval;
  }

  octave_value get_defaults (void) const
  {
    return default_properties.as_struct ("default");
  }

  octave_value get_factory_defaults (void) const
  {
    return factory_properties.as_struct ("factory");
  }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }

private:
  property_list default_properties;

  static property_list factory_properties;

  static property_list::plist_map_type init_factory_properties (void);
};

// ---------------------------------------------------------------------

class OCTINTERP_API figure : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    void close (bool pop = true);

    void set_visible (const octave_value& val);

    graphics_backend get_backend (void) const
      {
	if (! backend)
	  backend = graphics_backend::default_backend ();

	return backend;
      }

    void set_backend (const graphics_backend& b) { backend = b; }

    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(figure)
      any_property __plot_stream__ h , Matrix ()
      bool_property __enhanced__ h , "on"
      radio_property nextplot , "new|{add}|replace_children|replace"
      callback_property closerequestfcn , "closereq"
      handle_property currentaxes S , graphics_handle ()
      array_property colormap , jet_colormap ()
      radio_property paperorientation , "{portrait}|landscape|rotated"
      color_property color , color_values (1, 1, 1)
      array_property alphamap , Matrix (64, 1, 1)
      string_property currentcharacter r , ""
      handle_property currentobject r , graphics_handle ()
      array_property current_point r , Matrix (2, 1, 0)
      bool_property dockcontrols , "off"
      bool_property doublebuffer , "on"
      string_property filename r , ""
      bool_property integerhandle , "on"
      bool_property inverthardcopy , "off"
      callback_property keypressfcn , Matrix ()
      callback_property keyreleasefcn , Matrix ()
      radio_property menubar , "none|{figure}"
      double_property mincolormap , 64
      string_property name , ""
      bool_property numbertitle , "on"
      radio_property paperunits , "{inches}|centimeters|normalized|points"
      array_property paperposition , Matrix (1, 4 , 0) 
      radio_property paperpositionmode , "auto|{manual}"
      array_property papersize r , Matrix (1, 4, 0)
      radio_property papertype , "{usletter}|uslegal|a0|a1|a2|a3|a4|a5|b0|b1|b2|b3|b4|b5|arch-a|arch-b|arch-c|arch-d|arch-e|a|b|c|d|e|tabloid"
      radio_property pointer , "crosshair|fullcrosshair|{arrow}|ibeam|watch|topl|topr|botl|botr|left|top|right|bottom|circle|cross|fleur|custom|hand"
      array_property pointershapecdata , Matrix (16, 16, 0)
      array_property pointershapehotspot , Matrix (1, 2, 0)
      array_property position , Matrix (1, 4, 0)
      radio_property renderer , "{painters}|zbuffer|opengl|none"
      radio_property renderermode , "{auto}|manual"
      bool_property resize , "on"
      callback_property resizefcn , Matrix ()
      radio_property selectiontype , "{normal}|open|alt|extend"
      radio_property toolbar , "none|{auto}|figure"
      radio_property units , "inches|centimeters|normalized|points|{pixels}|characters"
      callback_property windowbuttondownfcn , Matrix ()
      callback_property windowbuttonmotionfcn , Matrix ()
      callback_property windowbuttonupfcn , Matrix ()
      callback_property windowbuttonwheelfcn , Matrix ()
      radio_property windowstyle , "{normal}|modal|docked"
      string_property wvisual , ""
      radio_property wvisualmode , "{auto}|manual"
      string_property xdisplay , ""
      string_property xvisual , ""
      radio_property xvisualmode , "{auto}|manual"
      callback_property buttondownfcn , Matrix ()
    END_PROPERTIES
    
  protected:
    void init (void)
      {
        colormap.add_constraint (dim_vector (-1, 3));
	alphamap.add_constraint (dim_vector (-1, 1));
	paperposition.add_constraint (dim_vector (1, 4));
	pointershapecdata.add_constraint (dim_vector (16, 16));
	pointershapehotspot.add_constraint (dim_vector (1, 2));
	position.add_constraint (dim_vector (1, 4));
      }

  private:
    mutable graphics_backend backend;
  };

private:
  properties xproperties;

public:
  figure (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p), default_properties ()
  {
    xproperties.override_defaults (*this);
  }

  ~figure (void)
  {
    xproperties.delete_children (); 
    xproperties.close ();
  }

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (root figure) to override first (properties knows how
    // to find the parent object).
    xproperties.override_defaults (obj);

    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      default_properties.set (name.substr (7), value);
    else
      xproperties.set (name, value);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    if (name.compare ("default", 7))
      retval = get_default (name.substr (7));
    else
      retval = xproperties.get (name);

    return retval;
  }

  octave_value get_default (const caseless_str& name) const;

  octave_value get_defaults (void) const
  {
    return default_properties.as_struct ("default");
  }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }

private:
  property_list default_properties;
};

// ---------------------------------------------------------------------

class OCTINTERP_API axes : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    void set_defaults (base_graphics_object& obj, const std::string& mode);

    void remove_child (const graphics_handle& h);

    void delete_children (void);

    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(axes)
      array_property position , Matrix ()
      mutable handle_property title GSO , graphics_handle ()
      bool_property box , "on"
      bool_property key , "off"
      bool_property keybox , "off"
      double_property keypos , 1
      array_property colororder , default_colororder ()
      array_property dataaspectratio m , Matrix (1, 3, 1.0)
      radio_property dataaspectratiomode , "{auto}|manual"
      radio_property layer , "{bottom}|top"
      array_property xlim m , default_lim ()
      array_property ylim m , default_lim ()
      array_property zlim m , default_lim ()
      array_property clim m , default_lim ()
      array_property alim m , default_lim ()
      radio_property xlimmode al , "{auto}|manual"
      radio_property ylimmode al , "{auto}|manual"
      radio_property zlimmode al , "{auto}|manual"
      radio_property climmode al , "{auto}|manual"
      radio_property alimmode    , "{auto}|manual"
      mutable handle_property xlabel GSO , graphics_handle ()
      mutable handle_property ylabel GSO , graphics_handle ()
      mutable handle_property zlabel GSO , graphics_handle ()
      bool_property xgrid , "off"
      bool_property ygrid , "off"
      bool_property zgrid , "off"
      bool_property xminorgrid , "off"
      bool_property yminorgrid , "off"
      bool_property zminorgrid , "off"
      array_property xtick m , Matrix ()
      array_property ytick m , Matrix ()
      array_property ztick m , Matrix ()
      radio_property xtickmode , "{auto}|manual"
      radio_property ytickmode , "{auto}|manual"
      radio_property ztickmode , "{auto}|manual"
      bool_property xminortick , "off"
      bool_property yminortick , "off"
      bool_property zminortick , "off"
      // FIXME: should be kind of string array
      any_property xticklabel m , ""
      any_property yticklabel m , ""
      any_property zticklabel m , ""
      radio_property xticklabelmode , "{auto}|manual"
      radio_property yticklabelmode , "{auto}|manual"
      radio_property zticklabelmode , "{auto}|manual"
      color_property color , color_property (color_values (1, 1, 1), radio_values ("none"))
      color_property xcolor , color_values (0, 0, 0)
      color_property ycolor , color_values (0, 0, 0)
      color_property zcolor , color_values (0, 0, 0)
      radio_property xscale al , "{linear}|log"
      radio_property yscale al , "{linear}|log"
      radio_property zscale al , "{linear}|log"
      radio_property xdir , "{normal}|reverse"
      radio_property ydir , "{normal}|reverse"
      radio_property zdir , "{normal}|reverse"
      radio_property yaxislocation , "{left}|right|zero"
      radio_property xaxislocation , "{bottom}|top|zero"
      array_property view , Matrix ()
      radio_property nextplot , "add|replace_children|{replace}"
      array_property outerposition , Matrix ()
      radio_property activepositionproperty , "{outerposition}|position"
      radio_property __colorbar__ h , "{none}|north|south|east|west|northoutside|southoutside|eastoutside|westoutside"
      color_property ambientlightcolor , color_values (1, 1, 1)
      array_property cameraposition m , Matrix (1, 3, 0.0)
      array_property cameratarget m , Matrix (1, 3, 0.0)
      array_property cameraupvector m , Matrix ()
      double_property cameraviewangle m , 10.0
      radio_property camerapositionmode , "{auto}|manual"
      radio_property cameratargetmode , "{auto}|manual"
      radio_property cameraupvectormode , "{auto}|manual"
      radio_property cameraviewanglemode , "{auto}|manual"
      array_property currentpoint , Matrix (2, 3, 0.0)
      radio_property drawmode , "{normal}|fast"
      radio_property fontangle , "{normal}|italic|oblique"
      string_property fontname , "Helvetica"
      double_property fontsize , 12
      radio_property fontunits , "{points}|normalized|inches|centimeters|pixels"
      radio_property fontweight , "{normal}|light|demi|bold"
      radio_property gridlinestyle , "{-}|--|:|-.|none"
      // FIXME: should be kind of string array
      string_property linestyleorder , "-"
      double_property linewidth , 0.5
      radio_property minorgridlinestyle , "{-}|--|:|-.|none"
      array_property plotboxaspectratio m , Matrix (1, 3, 1.0)
      radio_property plotboxaspectratiomode , "{auto}|manual"
      radio_property projection , "{orthographic}|perpective"
      radio_property tickdir m , "{in}|out"
      radio_property tickdirmode , "{auto}|manual"
      array_property ticklength , Matrix (1, 2, 0.1)
      array_property tightinset r , Matrix (1, 4, 0.0)
      // FIXME: uicontextmenu should be moved here
      radio_property units , "{normalized}|inches|centimeters|points|pixels|characters"
      // hidden properties for transformation computation
      array_property x_viewtransform h , Matrix (4, 4, 0.0)
      array_property x_projectiontransform h , Matrix (4, 4, 0.0)
      array_property x_viewporttransform h , Matrix (4, 4, 0.0)
      array_property x_normrendertransform h , Matrix (4, 4, 0.0)
      array_property x_rendertransform h , Matrix (4, 4, 0.0)
   END_PROPERTIES

  protected:
    void init (void)
      {
        position.add_constraint (dim_vector (1, 4));
	position.add_constraint (dim_vector (0, 0));
        outerposition.add_constraint (dim_vector (1, 4));
        colororder.add_constraint (dim_vector (-1, 3));
        dataaspectratio.add_constraint (dim_vector (1, 3));
        plotboxaspectratio.add_constraint (dim_vector (1, 3));
        xlim.add_constraint (dim_vector (1, 2));
        ylim.add_constraint (dim_vector (1, 2));
        zlim.add_constraint (dim_vector (1, 2));
        clim.add_constraint (dim_vector (1, 2));
	alim.add_constraint (dim_vector (1, 2));
        xtick.add_constraint (dim_vector (1, -1));
        ytick.add_constraint (dim_vector (1, -1));
        ztick.add_constraint (dim_vector (1, -1));
        Matrix vw (1, 2, 0);
        vw(1) = 90;
        view = vw;
        view.add_constraint (dim_vector (1, 2));
	cameraposition.add_constraint (dim_vector (1, 3));
	Matrix upv (1, 3, 0.0);
	upv(2) = 1.0;
	cameraupvector = upv;
	cameraupvector.add_constraint (dim_vector (1, 3));
	currentpoint.add_constraint (dim_vector (2, 3));
	ticklength.add_constraint (dim_vector (1, 2));
	tightinset.add_constraint (dim_vector (1, 4));
      }
  };

private:
  properties xproperties;

public:
  axes (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p), default_properties ()
  {
    xproperties.override_defaults (*this);
  }

  ~axes (void) { xproperties.delete_children (); }

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (figure) to override first (properties knows how
    // to find the parent object).
    xproperties.override_defaults (obj);

    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      default_properties.set (name.substr (7), value);
    else
      xproperties.set (name, value);
  }

  void set_defaults (const std::string& mode)
  {
    xproperties.set_defaults (*this, mode);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    // FIXME -- finish this.
    if (name.compare ("default", 7))
      retval = get_default (name.substr (7));
    else
      retval = xproperties.get (name);

    return retval;
  }

  octave_value get_default (const caseless_str& name) const;

  octave_value get_defaults (void) const
  {
    return default_properties.as_struct ("default");
  }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  void update_axis_limits (const std::string& axis_type);

  bool valid_object (void) const { return true; }

private:
  property_list default_properties;
};

// ---------------------------------------------------------------------

class OCTINTERP_API line : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    // properties which are not in matlab:
    // ldata, udata, xldata, xudata, keylabel, interpreter

    BEGIN_PROPERTIES(line)
      data_property xdata l , default_data ()
      data_property ydata l , default_data ()
      data_property zdata l , Matrix ()
      data_property ldata l , Matrix ()
      data_property udata l , Matrix ()
      data_property xldata l , Matrix ()
      data_property xudata l , Matrix ()
      color_property color , color_values (0, 0, 0)
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|s|o|x|+|.|*|<|>|v|^|d|p|h"
      color_property markeredgecolor , "{auto}|none"
      color_property markerfacecolor , "auto|{none}"
      double_property markersize , 6
      string_property keylabel , ""
      radio_property interpreter , "{tex}|none|latex"
      string_property displayname , ""
      radio_property erasemode , "{normal}|none|xor|background"
    END_PROPERTIES
  };

private:
  properties xproperties;

public:
  line (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~line (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }
};

// ---------------------------------------------------------------------

class OCTINTERP_API text : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(text)
      string_property string , ""
      radio_property units , "{data}|pixels|normalized|inches|centimeters|points"
      array_property position , Matrix (1, 3, 0.0)
      double_property rotation , 0
      radio_property horizontalalignment , "{left}|center|right"
      color_property color , color_values (0, 0, 0)
      string_property fontname , "Helvetica"
      double_property fontsize , 10
      radio_property fontangle , "{normal}|italic|oblique"
      radio_property fontweight , "light|{normal}|demi|bold"
      radio_property interpreter , "{tex}|none|latex"
      color_property backgroundcolor , "{none}"
      string_property displayname , ""
      color_property edgecolor , "{none}"
      radio_property erasemode , "{normal}|none|xor|background"
      bool_property editing , "off"
      radio_property fontunits , "inches|centimeters|normalized|{points}|pixel"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      double_property margin , 1
      radio_property verticalalignment , "top|cap|{middle}|baseline|bottom"
    END_PROPERTIES

  protected:
    void init (void)
      {
        position.add_constraint (dim_vector (1, 3));
      }
  };

private:
  properties xproperties;

public:
  text (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~text (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }
};

// ---------------------------------------------------------------------

class OCTINTERP_API image : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(image)
      data_property xdata l , Matrix ()
      data_property ydata l , Matrix ()
      data_property cdata l , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
      {
      }
  };

private:
  properties xproperties;

public:
  image (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~image (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }
};

// ---------------------------------------------------------------------

class OCTINTERP_API patch : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(patch)
      data_property xdata l , Matrix ()
      data_property ydata l , Matrix ()
      data_property zdata l , Matrix ()
      data_property cdata l , Matrix ()
      radio_property cdatamapping , "{scaled}|direct"
      array_property faces , Matrix ()
      data_property facevertexalphadata , Matrix ()
      data_property facevertexcdata , Matrix ()
      array_property vertices , Matrix ()
      array_property vertexnormals , Matrix ()
      radio_property normalmode , "{auto}|manual"
      color_property facecolor , "{flat}|none|interp"
      double_property facealpha , 1.0
      radio_property facelighting , "{flat}|none|gouraud|phong"
      color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("flat|none|interp"))
      double_property edgealpha , 1.0
      radio_property edgelighting , "{none}|flat|gouraud|phong"
      radio_property backfacelighting , "{reverselit}|unlit|lit"
      double_property ambientstrength , 0.3
      double_property diffusestrength , 0.6
      double_property specularstrength , 0.6
      double_property specularexponent , 10.0
      double_property specularcolorreflectance , 1.0
      radio_property erasemode , "{normal}|background|xor|none"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|s|o|x|+|.|*|<|>|v|^|d|p|h"
      color_property markeredgecolor , "{auto}|none"
      color_property markerfacecolor , "auto|{none}"
      double_property markersize , 6
      string_property keylabel , ""
      radio_property interpreter , "{tex}|none|latex"
    END_PROPERTIES

  protected:
    void init (void)
      {
        vertices.add_constraint (dim_vector (-1, 2));
        vertices.add_constraint (dim_vector (-1, 3));
      }
  };

private:
  properties xproperties;

public:
  patch (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~patch (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }
};

// ---------------------------------------------------------------------

class OCTINTERP_API surface : public base_graphics_object
{
public:
  class properties : public base_properties
  {
  public:
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES(surface)
      data_property xdata l , Matrix ()
      data_property ydata l , Matrix ()
      data_property zdata l , Matrix ()
      data_property cdata l , Matrix ()
      color_property facecolor , "{flat}|none|interp"
      double_property facealpha , 1.0
      color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("flat|none|interp"))
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|s|o|x|+|.|*|<|>|v|^|d|p|h"
      color_property markeredgecolor , "{auto}|none"
      color_property markerfacecolor , "auto|{none}"
      double_property markersize , 6
      string_property keylabel , ""
      radio_property interpreter , "{tex}|none|latex"
    END_PROPERTIES

  protected:
    void init (void)
      {
      }
  };

private:
  properties xproperties;

public:
  surface (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~surface (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

  const base_properties& get_properties (void) const { return xproperties; }

  bool valid_object (void) const { return true; }
};

octave_value
get_property_from_handle (double handle, const std::string &property,
			  const std::string &func);
bool
set_property_in_handle (double handle, const std::string &property,
			const octave_value &arg, const std::string &func);

// ---------------------------------------------------------------------

class OCTINTERP_API gh_manager
{
protected:

  gh_manager (void);

public:

  static bool instance_ok (void)
  {
    bool retval = true;

    if (! instance)
      instance = new gh_manager ();

    if (! instance)
      {
	::error ("unable to create gh_manager!");

	retval = false;
      }

    return retval;
  }

  static void free (const graphics_handle& h)
  {
    if (instance_ok ())
      instance->do_free (h);
  }

  static graphics_handle lookup (double val)
  {
    return instance_ok () ? instance->do_lookup (val) : graphics_handle ();
  }

  static graphics_object get_object (const graphics_handle& h)
  {
    return instance_ok () ? instance->do_get_object (h) : graphics_object ();
  }

  static graphics_handle
  make_graphics_handle (const std::string& go_name,
			const graphics_handle& parent, bool do_createfcn = true)
  {
    return instance_ok ()
      ? instance->do_make_graphics_handle (go_name, parent, do_createfcn)
      : graphics_handle ();
  }

  static graphics_handle make_figure_handle (double val)
  {
    return instance_ok ()
      ? instance->do_make_figure_handle (val) : graphics_handle ();
  }

  static void push_figure (const graphics_handle& h)
  {
    if (instance_ok ())
      instance->do_push_figure (h);
  }

  static void pop_figure (const graphics_handle& h)
  {
    if (instance_ok ())
      instance->do_pop_figure (h);
  }

  static graphics_handle current_figure (void)
  {
    return instance_ok ()
      ? instance->do_current_figure () : graphics_handle ();
  }

  static Matrix handle_list (void)
  {
    return instance_ok () ? instance->do_handle_list () : Matrix ();
  }

  static Matrix figure_handle_list (void)
  {
    return instance_ok () ? instance->do_figure_handle_list () : Matrix ();
  }

private:

  static gh_manager *instance;

  typedef std::map<graphics_handle, graphics_object>::iterator iterator;
  typedef std::map<graphics_handle, graphics_object>::const_iterator const_iterator;

  typedef std::set<graphics_handle>::iterator free_list_iterator;
  typedef std::set<graphics_handle>::const_iterator const_free_list_iterator;

  typedef std::list<graphics_handle>::iterator figure_list_iterator;
  typedef std::list<graphics_handle>::const_iterator const_figure_list_iterator;

  // A map of handles to graphics objects.
  std::map<graphics_handle, graphics_object> handle_map;

  // The available graphics handles.
  std::set<graphics_handle> handle_free_list;

  // The next handle available if handle_free_list is empty.
  double next_handle;

  // The allocated figure handles.  Top of the stack is most recently
  // created.
  std::list<graphics_handle> figure_list;

  graphics_handle get_handle (const std::string& go_name);

  void do_free (const graphics_handle& h);

  graphics_handle do_lookup (double val)
  {
    iterator p = (xisnan (val) ? handle_map.end () : handle_map.find (val));

    return (p != handle_map.end ()) ? p->first : graphics_handle ();
  }

  graphics_object do_get_object (const graphics_handle& h)
  {
    iterator p = (h.ok () ? handle_map.find (h) : handle_map.end ());

    return (p != handle_map.end ()) ? p->second : graphics_object ();
  }

  graphics_handle do_make_graphics_handle (const std::string& go_name,
					   const graphics_handle& p, bool do_createfcn);

  graphics_handle do_make_figure_handle (double val);

  Matrix do_handle_list (void)
  {
    Matrix retval (1, handle_map.size ());
    octave_idx_type i = 0;
    for (const_iterator p = handle_map.begin (); p != handle_map.end (); p++)
      {
	graphics_handle h = p->first;
	retval(i++) = h.value ();
      }
    return retval;
  }

  Matrix do_figure_handle_list (void)
  {
    Matrix retval (1, figure_list.size ());
    octave_idx_type i = 0;
    for (const_figure_list_iterator p = figure_list.begin ();
	 p != figure_list.end ();
	 p++)
      {
	graphics_handle h = *p;
	retval(i++) = h.value ();
      }
    return retval;
  }

  void do_push_figure (const graphics_handle& h);

  void do_pop_figure (const graphics_handle& h);

  graphics_handle do_current_figure (void) const
  {
    return figure_list.empty () ? graphics_handle () : figure_list.front ();
  }
};


// This function is NOT equivalent to the scripting language function gcf.
OCTINTERP_API graphics_handle gcf (void);

// This function is NOT equivalent to the scripting language function gca.
OCTINTERP_API graphics_handle gca (void);

#endif

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/
