/*
 * Copyright (c) 2002-2006 Samit Basu
 *
 * 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-1307  USA
 *
 */
#include "HandleCommands.hpp"
#include "HandleFigure.hpp"

#include "Parser.hpp"
#include "Scanner.hpp"
#include "Token.hpp"


#include <qgl.h>
#include <QtGui>
#include <ctype.h>
#include <algorithm>
#include "HandleLineSeries.hpp"
#include "HandleObject.hpp"
#include "HandleText.hpp"
#include "HandleAxis.hpp"
#include "HandleImage.hpp"
#include <qapplication.h>
#include "HandleList.hpp"
#include "HandleSurface.hpp"
#include "HandleWindow.hpp"
#include "HandleContour.hpp"
#include "HandleUIControl.hpp"
#include "QTRenderEngine.hpp"
#include "Interpreter.hpp"
#include <math.h>

// Subplot
// labels don't always appear properly.
// linestyle/symbol specification
// images

QWidget *save = NULL;

void SaveFocus() {
  save = qApp->focusWidget();
}
  
void RestoreFocus() {
  if (save)
    save->setFocus();
}

HandleWindow* Hfigs[MAX_FIGS];
int HcurrentFig = -1;

static bool HGInitialized = false;

void InitializeHandleGraphics() {
  if (HGInitialized) return;
  for (int i=0;i<MAX_FIGS;i++) Hfigs[i] = NULL;
  HcurrentFig = -1;
  HGInitialized = true;
}

void ShutdownHandleGraphics() {
  for (int i=0;i<MAX_FIGS;i++) {
    if  (Hfigs[i]) {
      Hfigs[i]->hide();
      delete Hfigs[i];
    }
    Hfigs[i] = NULL;
  }
  HcurrentFig = -1;
}

// Magic constant - limits the number of figures you can have...
  
HandleList<HandleObject*> objectset;

void NotifyFigureClosed(unsigned figNum) {
  //    delete Hfigs[figNum];
  Hfigs[figNum] = NULL;
  if (figNum == HcurrentFig)
    HcurrentFig = -1;
  // Check for all figures closed
  bool allClosed = true;
  for (int i=0;allClosed && i<MAX_FIGS;i++)
    allClosed = Hfigs[i] == NULL;
}

void NotifyFigureActive(unsigned figNum) {
  HcurrentFig = figNum;
}

static void NewFig() {
  // First search for an unused fig number
  int figNum = 0;
  bool figFree = false;
  while ((figNum < MAX_FIGS) && !figFree) {
    figFree = (Hfigs[figNum] == NULL);
    if (!figFree) figNum++;
  }
  if (!figFree) {
    throw Exception("No more fig handles available!  Close some figs...");
  }
  Hfigs[figNum] = new HandleWindow(figNum);
  SaveFocus();
  Hfigs[figNum]->show();
  RestoreFocus();
  HcurrentFig = figNum;
}

static HandleWindow* CurrentWindow() {
  if (HcurrentFig == -1)
    NewFig();
  return (Hfigs[HcurrentFig]);
}

static HandleFigure* CurrentFig() {
  if (HcurrentFig == -1)
    NewFig();
  return (Hfigs[HcurrentFig]->HFig());
}


static void SelectFig(int fignum) {
  if (fignum < 0)
    throw Exception("Illegal argument to SelectFig");
  if (Hfigs[fignum] == NULL)
    Hfigs[fignum] = new HandleWindow(fignum);
  SaveFocus();
  Hfigs[fignum]->show();
  Hfigs[fignum]->raise();
  RestoreFocus();
  HcurrentFig = fignum;
}

//!
//@Module DRAWNOW Flush the Event Queue
//@@Section HANDLE
//@@Usage
//The @|drawnow| function can be used to process the events in the
//event queue of the FreeMat application.  The syntax for its use
//is
//@[
//   drawnow
//@]
//Now that FreeMat is threaded, you do not generally need to call this
//function, but it is provided for compatibility.
//!

bool AnyDirty(bool issueUpdates) {
  bool retval = false;
  if (!HGInitialized) return false;
  for (int i=0;i<MAX_FIGS;i++) {
    if (Hfigs[i] && (Hfigs[i]->isDirty()))  {
      retval = true;
      if (issueUpdates)
	Hfigs[i]->repaint();
    }
  }
  return retval;
}

static bool in_DoDrawNow = false;

void DoDrawNow() {
  if (in_DoDrawNow) return;
  in_DoDrawNow = true;
  if (AnyDirty(true))
    while (AnyDirty(false))
      qApp->processEvents();
  in_DoDrawNow = false;
}

ArrayVector DrawNowFunction(int nargout, const ArrayVector& arg) {
  GfxEnableRepaint();
  DoDrawNow();
  GfxDisableRepaint();
  return ArrayVector();
}

HandleObject* LookupHandleObject(unsigned handle) {
  return (objectset.lookupHandle(handle-HANDLE_OFFSET_OBJECT));
}

HandleFigure* LookupHandleFigure(unsigned handle) {
  if (Hfigs[handle-HANDLE_OFFSET_FIGURE] != NULL)
    return Hfigs[handle-HANDLE_OFFSET_FIGURE]->HFig();
  else {
    SelectFig(handle-HANDLE_OFFSET_FIGURE);
    return Hfigs[handle-HANDLE_OFFSET_FIGURE]->HFig();
  }
}

void ValidateHandle(unsigned handle) {
  if (handle == 0) return;
  if (handle >= HANDLE_OFFSET_OBJECT)
    LookupHandleObject(handle);
  else
    LookupHandleFigure(handle);
}

unsigned AssignHandleObject(HandleObject* hp) {
  return (objectset.assignHandle(hp)+HANDLE_OFFSET_OBJECT);
}

void FreeHandleObject(unsigned handle) {
  objectset.deleteHandle(handle-HANDLE_OFFSET_OBJECT);
}


//!
//@Module FIGURE Figure Window Select and Create Function
//@@Section HANDLE
//@@Usage
//Changes the active figure window to the specified figure
//number.  The general syntax for its use is 
//@[
//  figure(number)
//@]
//where @|number| is the figure number to use. If the figure window 
//corresponding to @|number| does not already exist, a new 
//window with this number is created.  If it does exist
//then it is brought to the forefront and made active.
//You can use @|gcf| to obtain the number of the current figure.
//
//Note that the figure number is also the handle for the figure.
//While for most graphical objects (e.g., axes, lines, images), the
//handles are large integers, for figures, the handle is the same
//as the figure number.  This means that the figure number can be
//passed to @|set| and @|get| to modify the properties of the
//current figure, (e.g., the colormap).  So, for figure @|3|, for 
//example, you can use @|get(3,'colormap')| to retrieve the colormap
//for the current figure.
//!
ArrayVector HFigureFunction(int nargout,const ArrayVector& arg) {
  if (arg.size() == 0) {
    NewFig();
    return singleArrayVector(Array::int32Constructor(HcurrentFig+1));
  } else {
    Array t(arg[0]);
    int fignum = t.getContentsAsIntegerScalar();
    if ((fignum<=0) || (fignum>MAX_FIGS))
      throw Exception("figure number is out of range - it must be between 1 and 50");
    SelectFig(fignum-1);
    return ArrayVector();
  }
}

void AddToCurrentFigChildren(unsigned handle) {
  HandleFigure *fig = CurrentFig();
  HPHandles *cp = (HPHandles*) fig->LookupProperty("children");
  std::vector<unsigned> children(cp->Data());
  // Check to make sure that children does not contain our handle already
  int i=0;
  while (i<children.size()) {
    if (children[i] == handle)
      children.erase(children.begin()+i);
    else
      i++;
  }
  children.insert(children.begin(),1,handle);
  cp->Data(children);
}

//!
//@Module AXES Create Handle Axes
//@@Section HANDLE
//@@Usage
//This function has three different syntaxes.  The first takes
//no arguments,
//@[
//  h = axes
//@]
//and creates a new set of axes that are parented to the current
//figure (see @|gcf|).  The newly created axes are made the current
//axes (see @|gca|) and are added to the end of the list of children 
//for the current figure.
//The second form takes a set of property names and values
//@[
//  h = axes(propertyname,value,propertyname,value,...)
//@]
//Creates a new set of axes, and then sets the specified properties
//to the given value.  This is a shortcut for calling 
//@|set(h,propertyname,value)| for each pair.
//The third form takes a handle as an argument
//@[
//  axes(handle)
//@]
//and makes @|handle| the current axes, placing it at the head of
//the list of children for the current figure.
//!
ArrayVector HAxesFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() != 1) {
    HandleObject *fp = new HandleAxis;
    unsigned int handle = AssignHandleObject(fp);
    ArrayVector t(arg);
    while (t.size() >= 2) {
      std::string propname(ArrayToString(t[0]));
      fp->LookupProperty(propname)->Set(t[1]);
      t.pop_front();
      t.pop_front();
    }
    // Get the current figure
    HandleFigure *fig = CurrentFig();
    fp->SetPropertyHandle("parent",HcurrentFig+1);
    fig->SetPropertyHandle("currentaxes",handle);
    // Add us to the children...
    AddToCurrentFigChildren(handle);
    fp->UpdateState();
    return singleArrayVector(Array::uint32Constructor(handle));
  } else {
    unsigned int handle = (unsigned int) ArrayToInt32(arg[0]);
    HandleObject* hp = LookupHandleObject(handle);
    if (!hp->IsType("axes"))
      throw Exception("single argument to axes function must be handle for an axes"); 
    AddToCurrentFigChildren(handle);
    // Get the current figure
    CurrentFig()->SetPropertyHandle("currentaxes",handle);     
    CurrentFig()->UpdateState();
    return ArrayVector();
  }
}

void HSetChildrenFunction(HandleObject *fp, Array children) {
  children.promoteType(FM_UINT32);
  const unsigned *dp = (const unsigned*) children.getDataPointer();
  // make sure they are all valid handles
  for (int i=0;i<children.getLength();i++) 
    ValidateHandle(dp[i]);
  // Retrieve the current list of children
  HandleObject *gp;
  HPHandles *hp = (HPHandles*) fp->LookupProperty("children");
  std::vector<unsigned> my_children(hp->Data());
  for (int i=0;i<my_children.size();i++) {
    unsigned handle = my_children[i];
    if (handle >= HANDLE_OFFSET_OBJECT) {
      gp = LookupHandleObject(handle);
      gp->Dereference();
    }
  }
  // Loop through the new list of children
  for (int i=0;i<children.getLength();i++) {
    unsigned handle = dp[i];
    if (handle >= HANDLE_OFFSET_OBJECT) {
      gp = LookupHandleObject(handle);
      gp->Reference();
    }
  }
  // Check for anyone with a zero reference count - it should
  // be deleted
  for (int i=0;i<my_children.size();i++) {
    unsigned handle = my_children[i];
    if (handle >= HANDLE_OFFSET_OBJECT) {
      gp = LookupHandleObject(handle);
      if (gp->RefCount() <= 0) {
	if (gp->StringPropertyLookup("type") == "uicontrol")
	  ((HandleUIControl*) gp)->Hide();
	FreeHandleObject(handle);
	delete gp;
      } 
    }
  }
  // Call the generic set function now
  hp->Set(children);
}


//!
//@Module SET Set Object Property
//@@Section HANDLE
//@@Usage
//This function allows you to change the value associated
//with a property.  The syntax for its use is
//@[
//  set(handle,property,value,property,value,...)
//@]
//where @|property| is a string containing the name of the
//property, and @|value| is the value for that property. The
//type of the variable @|value| depends on the property being
//set.  See the help for the properties to see what values
//you can set.
//!
ArrayVector HSetFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() < 3)
    throw Exception("set doesn't handle all cases yet!");
  int handle = ArrayToInt32(arg[0]);
  // Lookup the handle
  HandleObject *fp;
  if (handle >= HANDLE_OFFSET_OBJECT)
    fp = LookupHandleObject(handle);
  else
    fp = (HandleObject*) LookupHandleFigure(handle);
  int ptr = 1;
  while (arg.size() >= (ptr+2)) {
    // Use the address and property name to lookup the Get/Set handler
    std::string propname = ArrayToString(arg[ptr]);
    // Special case 'set' for 'children' - this can change reference counts
    // Special case 'set' for 'figsize' - this requires help from the OS
    if (propname == "children")
      HSetChildrenFunction(fp,arg[ptr+1]);
    else if ((handle < HANDLE_OFFSET_OBJECT) && (propname == "figsize")) {
      fp->LookupProperty(propname)->Set(arg[ptr+1]);
      HandleFigure *fig = (HandleFigure*) fp;
      fig->SetSize();
    } else {
      try {
	fp->LookupProperty(propname)->Set(arg[ptr+1]);
      } catch (Exception &e) {
	throw Exception(std::string("Got error ") + std::string(e.getMessageCopy()) + std::string(" for property ") + propname);
      }
    }
    ptr+=2;
  }
  fp->UpdateState();
  if (!fp->IsType("figure") && !fp->IsType("uicontrol")) {
    HandleFigure *fig = fp->GetParentFigure();
    fig->UpdateState();
    fig->Repaint();
  }
  return ArrayVector();
}

//!
//@Module GET Get Object Property
//@@Section HANDLE
//@@Usage
//This function allows you to retrieve the value associated
//with a property.  The syntax for its use is
//@[
//  value = get(handle,property)
//@]
//where @|property| is a string containing the name of the
//property, and @|value| is the value for that property. The
//type of the variable @|value| depends on the property being
//set.  See the help for the properties to see what values
//you can set.
//!
ArrayVector HGetFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() != 2)
    throw Exception("get doesn't handle all cases yet!");
  int handle = ArrayToInt32(arg[0]);
  std::string propname = ArrayToString(arg[1]);
  // Lookup the handle
  HandleObject *fp;
  if (handle >= HANDLE_OFFSET_OBJECT)
    fp = LookupHandleObject(handle);
  else
    fp = (HandleObject*) LookupHandleFigure(handle);
  // Use the address and property name to lookup the Get/Set handler
  return singleArrayVector(fp->LookupProperty(propname)->Get());
}

unsigned GenericConstructor(HandleObject* fp, const ArrayVector& arg,
			    bool autoParentGiven = true) {
  unsigned int handle = AssignHandleObject(fp);
  ArrayVector t(arg);
  while (t.size() >= 2) {
    std::string propname(ArrayToString(t[0]));
    if (propname == "autoparent") {
      std::string pval(ArrayToString(t[1]));
      autoParentGiven = (pval == "on");
    }	else {
      try {
	fp->LookupProperty(propname)->Set(t[1]);
      } catch (Exception &e) {
	throw Exception(std::string("Got error ") + std::string(e.getMessageCopy()) + std::string(" for property ") + propname);
      }
    }
    t.pop_front();
    t.pop_front();
  }
  if (autoParentGiven) {
    HandleFigure *fig = CurrentFig();
    unsigned current = fig->HandlePropertyLookup("currentaxes");
    if (current == 0) {
      ArrayVector arg2;
      HAxesFunction(0,arg2);
      current = fig->HandlePropertyLookup("currentaxes");
    }
    HandleAxis *axis = (HandleAxis*) LookupHandleObject(current);
    HPHandles *cp = (HPHandles*) axis->LookupProperty("children");
    std::vector<unsigned> children(cp->Data());
    children.push_back(handle);
    cp->Data(children);
    cp = (HPHandles*) fp->LookupProperty("parent");
    std::vector<unsigned> parent;
    parent.push_back(current);
    cp->Data(parent);
    axis->UpdateState();
  }
  fp->UpdateState();
  return handle;
}


//!
//@Module HLINE Create a line object
//@@Section HANDLE
//@@Usage
//Creates a line object and parents it to the current axis.  The
//syntax for its use is 
//@[
//  handle = hline(property,value,property,value,...)
//@]
//where @|property| and @|value| are set.  The handle ID for the
//resulting object is returned.  It is automatically added to
//the children of the current axis.
//!
ArrayVector HLineFunction(int nargout, const ArrayVector& arg) {
  return singleArrayVector(Array::uint32Constructor(GenericConstructor(new HandleLineSeries,arg)));
}

//!
//@Module HCONTOUR Create a contour object
//@@Section HANDLE
//@@Usage
//Creates a contour object and parents it to the current axis.  The
//syntax for its use is 
//@[
//  handle = hcontour(property,value,property,value,...)
//@]
//where @|property| and @|value| are set.  The handle ID for the
//resulting object is returned.  It is automatically added to
//the children of the current axis.
//!
ArrayVector HContourFunction(int nargout, const ArrayVector& arg) {
  return singleArrayVector(Array::uint32Constructor(GenericConstructor(new HandleContour,arg)));
}

//!
//@Module UICONTROL Create a UI Control object
//@@Section HANDLE
//@@Usage
//Creates a UI control object and parents it to the current figure.  The
//syntax for its use is
//@[
//  handle = uicontrol(property,value,property,value,...)
//@]
//where @|property| and @|value| are set.  The handle ID for the
//resulting object is returned.  It is automatically added to
//the children of the current figure.
//!
ArrayVector HUIControlFunction(int nargout, const ArrayVector& arg, Interpreter *eval) {
  HandleUIControl *o = new HandleUIControl;
  o->SetEvalEngine(eval);
  o->ConstructWidget(CurrentWindow());
  unsigned handleID = GenericConstructor(o,arg,false);
  // Parent the control to the current figure
  AddToCurrentFigChildren(handleID);
  HPHandles* cp = (HPHandles*) o->LookupProperty("parent");
  std::vector<unsigned> parent;
  parent.push_back(HcurrentFig+1);
  cp->Data(parent);
  return singleArrayVector(Array::uint32Constructor(handleID));
}

//!
//@Module HIMAGE Create a image object
//@@Section HANDLE
//@@Usage
//Creates a image object and parents it to the current axis.  The
//syntax for its use is 
//@[
//  handle = himage(property,value,property,value,...)
//@]
//where @|property| and @|value| are set.  The handle ID for the
//resulting object is returned.  It is automatically added to
//the children of the current axis.
//!
ArrayVector HImageFunction(int nargout, const ArrayVector& arg) {
  return singleArrayVector(Array::uint32Constructor(GenericConstructor(new HandleImage,arg)));
}

//!
//@Module HTEXT Create a text object
//@@Section HANDLE
//@@Usage
//Creates a text object and parents it to the current axis.  The
//syntax for its use is 
//@[
//  handle = htext(property,value,property,value,...)
//@]
//where @|property| and @|value| are set.  The handle ID for the
//resulting object is returned.  It is automatically added to
//the children of the current axis.
//!
ArrayVector HTextFunction(int nargout, const ArrayVector& arg) {
  return singleArrayVector(Array::uint32Constructor(GenericConstructor(new HandleText,arg)));
}

//!
//@Module HSURFACE Create a surface object
//@@Section HANDLE
//@@Usage
//Creates a surface object and parents it to the current axis.  The
//syntax for its use is 
//@[
//  handle = hsurface(property,value,property,value,...)
//@]
//where @|property| and @|value| are set.  The handle ID for the
//resulting object is returned.  It is automatically added to
//the children of the current axis.
//!
ArrayVector HSurfaceFunction(int nargout, const ArrayVector& arg) {
  return singleArrayVector(Array::uint32Constructor(GenericConstructor(new HandleSurface,arg)));
}

//!
//@Module FIGRAISE Raise a Figure Window
//@@Section HANDLE
//@@Usage
//Raises a figure window indicated by the figure number.  The syntax for
//its use is
//@[
//  figraise(fignum)
//@]
//where @|fignum| is the number of the figure to raise.  The figure will
//be raised to the top of the GUI stack (meaning that it we be visible).
//Note that this function does not cause @|fignum| to become the current
//figure, you must use the @|figure| command for that.
//!
ArrayVector FigRaiseFunction(int nargout, const ArrayVector& args) {
  if (args.size() == 0)
    CurrentWindow()->raise();
  else {
    int fignum = ArrayToInt32(args[0]);
    if ((fignum >= 1) && (fignum < MAX_FIGS+1)) {
      if (Hfigs[fignum-1])
	Hfigs[fignum-1]->raise();
      else {
	Hfigs[fignum-1] = new HandleWindow(fignum-1);
	Hfigs[fignum-1]->show();
	Hfigs[fignum-1]->raise();
      }
    }
  }
  return ArrayVector();
}

//!
//@Module FIGLOWER Lower a Figure Window
//@@Section HANDLE
//@@Usage
//Lowers a figure window indicated by the figure number.  The syntax for
//its use is
//@[
//  figlower(fignum)
//@]
//where @|fignum| is the number of the figure to lower.  The figure will
//be lowerd to the bottom of the GUI stack (meaning that it we be behind other
//windows).  Note that this function does not cause @|fignum| to 
//become the current  figure, you must use the @|figure| command for that.
//Similarly, if @|fignum| is the current figure, it will remain the current
//figure (even though the figure is now behind others).
//!
ArrayVector FigLowerFunction(int nargout, const ArrayVector& args) {
  if (args.size() == 0)
    CurrentWindow()->lower();
  else {
    int fignum = ArrayToInt32(args[0]);
    if ((fignum >= 1) && (fignum < MAX_FIGS+1)) {
      if (Hfigs[fignum-1])
	Hfigs[fignum-1]->lower();
      else {
	Hfigs[fignum-1] = new HandleWindow(fignum-1);
	Hfigs[fignum-1]->show();
	Hfigs[fignum-1]->lower();
      }
    }
  }
  return ArrayVector();
}

//!
//@Module GCF Get Current Figure
//@@Section HANDLE
//@@Usage
//Returns the figure number for the current figure (which is also its handle,
//and can be used to set properties of the current figure using @|set|).  
//The syntax for its use
//is
//@[
//  figure_number = gcf
//@]
//where @|figure_number| is the number of the active figure (also the handle of
//the figure).
//
//Note that figures have handles, just like axes, images, plots, etc.  However
//the handles for figures match the figure number (while handles for other 
//graphics objects tend to be large, somewhat arbitrary integers).  So, to 
//retrieve the colormap of the current figure, you could 
//use @|get(gcf,'colormap')|, or to obtain the colormap for figure 3, 
//use @|get(3,'colormap')|.
//!
ArrayVector HGCFFunction(int nargout, const ArrayVector& arg) {
  if (HcurrentFig == -1)
    NewFig();      
  return singleArrayVector(Array::uint32Constructor(HcurrentFig+1));
}



//!
//@Module GCA Get Current Axis
//@@Section HANDLE
//@@Usage
//Returns the handle for the current axis.  The syntax for its use
//is
//@[
//  handle = gca
//@]
//where @|handle| is the handle of the active axis.  All object
//creation functions will be children of this axis.
//!
ArrayVector HGCAFunction(int nargout, const ArrayVector& arg) {
  // Get the current figure...
  if (HcurrentFig == -1)
    NewFig();
  HandleFigure* fig = CurrentFig();
  unsigned current = fig->HandlePropertyLookup("currentaxes");
  if (current == 0) {
    ArrayVector arg2;
    HAxesFunction(0,arg2);
    current = fig->HandlePropertyLookup("currentaxes");
  }
  return singleArrayVector(Array::uint32Constructor(current));
}

//!
//@Module PVALID Validate Property Name
//@@Section HANDLE
//@@Usage
//This function checks to see if the given string is a valid
//property name for an object of the given type.  The syntax
//for its use is
//@[
//  b = pvalid(type,propertyname)
//@]
//where @|string| is a string that contains the name of a 
// valid graphics object type, and
//@|propertyname| is a string that contains the name of the
//property to test for.
//@@Example
//Here we test for some properties on an @|axes| object.
//@<
//pvalid('axes','type')
//pvalid('axes','children')
//pvalid('axes','foobar')
//@>
//!
ArrayVector HPropertyValidateFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() != 2)
    throw Exception("pvalid requires two arguments, an object type name and a property name");
  std::string objectname = ArrayToString(arg[0]);
  HandleObject *fp;
  if (objectname == "axes")
    fp = new HandleAxis;
  else if (objectname == "line")
    fp = new HandleLineSeries;
  else if (objectname == "text")
    fp = new HandleText;
  else
    throw Exception("Unrecognized object type name " + objectname);
  std::string propname = ArrayToString(arg[1]);
  bool isvalid;
  isvalid = true;
  try {
    fp->LookupProperty(propname);
  } catch (Exception& e) {
    isvalid = false;
  }
  delete fp;
  return singleArrayVector(Array::logicalConstructor(isvalid));
}

bool PrintBaseFigure(HandleWindow* g, std::string filename, 
		     std::string type) {
  bool retval;
  HPColor *color = (HPColor*) g->HFig()->LookupProperty("color");
  double cr, cg, cb;
  cr = color->At(0); cg = color->At(1); cb = color->At(2);
  g->HFig()->SetThreeVectorDefault("color",1,1,1);
  GfxEnableRepaint();
  g->UpdateState();
  while (g->isDirty())
    qApp->processEvents();
  if ((type == "PDF") || (type == "PS") || (type == "EPS")){
    QPrinter prnt;
    if (type == "PDF")
      prnt.setOutputFormat(QPrinter::PdfFormat);
    else
      prnt.setOutputFormat(QPrinter::PostScriptFormat);
    prnt.setOutputFileName(filename.c_str());
    QPainter pnt(&prnt);
    QTRenderEngine gc(&pnt,0,0,g->width(),g->height());
    g->HFig()->PaintMe(gc);
    retval = true;
  } else {
    // Binary print - use grabWidget
    QPixmap pxmap(QPixmap::grabWidget(g->GetQtWidget()));
    QImage img(pxmap.toImage());
    retval = img.save(filename.c_str(),type.c_str());
  }
  g->HFig()->SetThreeVectorDefault("color",cr,cg,cb);
  g->UpdateState();
  while (g->isDirty())
    qApp->processEvents();
  GfxDisableRepaint();
  return retval;
}
  
void CloseHelper(int fig) {
  if (fig == -1) return;
  if (Hfigs[fig] == NULL) return;
  Hfigs[fig]->hide();
  delete Hfigs[fig];
  Hfigs[fig] = NULL;
  if (HcurrentFig == fig)
    HcurrentFig = -1;
}

//!
//@Module CLOSE Close Figure Window
//@@Section HANDLE
//@@Usage
//Closes a figure window, either the currently active window, a 
//window with a specific handle, or all figure windows.  The general
//syntax for its use is
//@[
//   close(handle)
//@]
//in which case the figure window with the speicified @|handle| is
//closed.  Alternately, issuing the command with no argument
//@[
//   close
//@]
//is equivalent to closing the currently active figure window.  Finally
//the command
//@[
//   close('all')
//@]
//closes all figure windows currently open.
//!
ArrayVector HCloseFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() > 1)
    throw Exception("close takes at most one argument - either the string 'all' to close all figures, or a scalar integer indicating which figure is to be closed.");
  int action;
  if (arg.size() == 0) 
    action = 0;
  else {
    Array t(arg[0]);
    if (t.isString()) {
      string allflag = t.getContentsAsString();
      if (allflag == "all") 
	action = -1;
      else
	throw Exception("string argument to close function must be 'all'");
    } else {
      int handle = t.getContentsAsIntegerScalar();
      if (handle < 1)
	throw Exception("Invalid figure number argument to close function");
      action = handle;
    }
  }
  if (action == 0) {
    if (HcurrentFig != -1) 
      CloseHelper(HcurrentFig);
  } else if (action == -1) {
    for (int i=0;i<MAX_FIGS;i++)
      CloseHelper(i);
    HcurrentFig = -1;
  } else {
    if ((action < MAX_FIGS) && (action >= 1))
      CloseHelper(action-1);
  }
  return ArrayVector();
}

//!
//@Module COPY Copy Figure Window
//@@Section HANDLE
//@@Usage
//Copies the currently active figure window to the clipboard.
//The syntax for its use is:
//@[
//   copy
//@]
//The resulting figure is copied as a bitmap to the clipboard, 
//and can then be pasted into any suitable application.
//!
ArrayVector HCopyFunction(int nargout, const ArrayVector& arg) {
  if (HcurrentFig == -1)
    return ArrayVector();
  HandleWindow *f = Hfigs[HcurrentFig];
  // use grabWidget - doesnt work for openGL yet
  QClipboard *cb = QApplication::clipboard();
  cb->setPixmap(QPixmap::grabWidget(f));
  return ArrayVector();
}
  
std::string NormalizeImageExtension(std::string ext) {
  std::string upperext(ext);
  std::string lowerext(ext);
  std::transform(upperext.begin(),upperext.end(),upperext.begin(),
		 (int(*)(int))toupper);
  std::transform(lowerext.begin(),lowerext.end(),lowerext.begin(),
		 (int(*)(int))tolower);
  if (upperext == "JPG") return std::string("JPEG");
  if ((upperext == "PDF") || (upperext == "PS") || (upperext == "EPS")) return upperext;
  QList<QByteArray> formats(QImageWriter::supportedImageFormats());
  for (int i=0;i<formats.count();i++) {
    if (formats.at(i).data() == upperext) return upperext;
    if (formats.at(i).data() == lowerext) return lowerext;
  }
  return std::string();
}

std::string FormatListAsString() {
  std::string ret_text = "Supported Formats: ";
  QList<QByteArray> formats(QImageWriter::supportedImageFormats());
  for (int i=0;i<formats.count();i++)
    ret_text = ret_text + formats.at(i).data() + " ";
  return ret_text;
}

//!
//@Module PRINT Print a Figure To A File
//@@Section HANDLE
//@@Usage
//This function ``prints'' the currently active fig to a file.  The 
//generic syntax for its use is
//@[
//  print(filename)
//@]
//or, alternately,
//@[
//  print filename
//@]
//where @|filename| is the (string) filename of the destined file.  The current
//fig is then saved to the output file using a format that is determined
//by the extension of the filename.  The exact output formats may vary on
//different platforms, but generally speaking, the following extensions
//should be supported cross-platform:
//\begin{itemize}
//\item @|jpg|, @|jpeg|  --  JPEG file 
//\item @|pdf| -- Portable Document Format file
//\item @|png| -- Portable Net Graphics file
//\end{itemize}
//Postscript (PS, EPS) is supported on non-Mac-OSX Unix only.
//Note that only the fig is printed, not the window displaying
//the fig.  If you want something like that (essentially a window-capture)
//use a seperate utility or your operating system's built in screen
//capture ability.
//@@Example
//Here is a simple example of how the figures in this manual are generated.
//@<
//x = linspace(-1,1);
//y = cos(5*pi*x);
//plot(x,y,'r-');
//print('printfig1.jpg')
//print('printfig1.png')
//@>
//which creates two plots @|printfig1.png|, which is a Portable
//Net Graphics file, and @|printfig1.jpg| which is a JPEG file.
//@figure printfig1
//!
ArrayVector HPrintFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() != 1)
    throw Exception("print function takes a single, string argument");
  if (!(arg[0].isString()))
    throw Exception("print function takes a single, string argument");
  Array t(arg[0]);
  if (HcurrentFig == -1)
    return ArrayVector();
  HandleWindow *f = Hfigs[HcurrentFig];
  std::string outname(t.getContentsAsString());
  int pos = outname.rfind(".");
  if (pos < 0)
    throw Exception("print function argument must contain an extension - which is used to determine the format for the output");
  std::string original_extension(outname.substr(pos+1,outname.size()));
  std::string modified_extension = 
    NormalizeImageExtension(original_extension);
  if (modified_extension.empty())
    throw Exception(std::string("unsupported output format ") + 
		    original_extension + " for print.\n" + 
		    FormatListAsString());
  if (!PrintBaseFigure(f,outname,modified_extension))
    throw Exception("Printing failed!");
  return ArrayVector();
}

//!
//@Module HPOINT Get Point From Window
//@@Section HANDLE
//@@Usage
//This function waits for the user to click on the current figure
//window, and then returns the coordinates of that click.  The
//generic syntax for its use is
//@[
//  [x,y] = hpoint
//@]
//!
ArrayVector HPointFunction(int nargout, const ArrayVector& arg) {
  if (HcurrentFig == -1)
    return ArrayVector();
  HandleWindow *f = Hfigs[HcurrentFig];
  f->raise();
  f->activateWindow();
  f->setFocus(Qt::OtherFocusReason);
  int x, y;
  f->GetClick(x,y);
  Array retval(Array::doubleVectorConstructor(2));
  double *d_ip;
  d_ip = (double*) retval.getReadWriteDataPointer();
  d_ip[0] = (double) x;
  d_ip[1] = (double) y;
  return singleArrayVector(retval);
}

//!
//@Module IS2DVIEW Test Axes For 2D View
//@@Section HANDLE
//@@Usage
//This function returns @|true| if the current axes are in a
//2-D view, and false otherwise.  The generic syntax for its
//use is
//@[
//  y = is2dview(x)
//@]
//where @|x| is the handle of an axes object.
//!
ArrayVector HIs2DViewFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() == 0) throw Exception("is2DView expects a handle to axes");
  unsigned int handle = (unsigned int) ArrayToInt32(arg[0]);
  HandleObject* hp = LookupHandleObject(handle);
  if (!hp->IsType("axes"))
    throw Exception("single argument to axes function must be handle for an axes"); 
  HandleAxis *axis = (HandleAxis*) hp;
  return singleArrayVector(Array::logicalConstructor(axis->Is2DView()));
}

#if 0
class contour_point {
public:
  double x;
  double y;
  inline contour_point(double a, double b) : x(a), y(b) {};
};

inline bool operator==(const contour_point& p1, const contour_point& p2) {
  return ((p1.x == p2.x) && (p1.y == p2.y));
}

typedef QList<contour_point> cline;
typedef QList<cline> lineset;

inline cline Reverse(const cline& src) {
  cline ret;
  for (int i=src.size()-1;i>=0;i--)
    ret << src.at(i);
  return ret;
}

inline bool Connected(const cline& current, const cline& test) {
  return ((current.front() == test.front()) || 
	  (current.front() == test.back()) ||
	  (current.back() == test.front()) ||
	  (current.back() == test.back()));
}

inline void Join(cline& current, const cline& toadd) {
  if (current.front() == toadd.front())
    current = Reverse(toadd) + current;
  else if (current.front() == toadd.back())
    current = toadd + current;
  else if (current.back() == toadd.front())
    current += toadd;
  else if (current.back() == toadd.back())
    current += Reverse(toadd);
}

#define FOLD(x) MAP((x),row-1)
#define FNEW(x) MAP((x),row)
#define MAP(x,y) func[(y)+(x)*numy]
#define AINTER(a,b) ((val-(a))/((b)-(a)))
#define ALEFT(i,j) (((j)-1)+AINTER(FOLD((i)-1),FNEW((i)-1)))
#define TOP(i) (((i)-1)+AINTER(FNEW((i)-1),FNEW((i))))
#define BOT(i) (((i)-1)+AINTER(FOLD((i)-1),FOLD((i))))
#define RIGHT(i,j) (((j)-1)+AINTER(FOLD((i)),FNEW((i))))
#define DRAW(a,b,c,d) {allLines << (cline() << contour_point((double)a,(double)b) << contour_point((double)c,(double)d));}

ArrayVector ContourCFunction(int nargout, const ArrayVector& arg) {
  lineset allLines;
  lineset bundledLines;
  if (arg.size() < 2) throw Exception("contourc expects two arguments");
  double val = ArrayToDouble(arg[1]);
  if (!arg[0].is2D())
    throw Exception("First argument to contourc must be a 2D matrix");
  Array m(arg[0]);
  m.promoteType(FM_DOUBLE);
  const double *func = (const double *) m.getDataPointer();
  int outcnt = 0;
  int numy = m.rows();
  int numx = m.columns();
  for (int row=1;row<numy;row++)
    for (int col=1;col<numx;col++) {
      int l = 0;
      if (FOLD(col) >= val) l  = l + 1;
      if (FOLD(col-1) >= val) l = l + 2;
      if (FNEW(col) >= val) l = l + 4;
      if (FNEW(col-1) >= val) l = l + 8;
      switch (l) {
      case 1:
      case 14:
	DRAW(BOT(col),row-1,col,RIGHT(col,row));
	break;
      case 2:
      case 13:
	DRAW(col-1,ALEFT(col,row),BOT(col),row-1);
	break;
      case 3:
      case 12:
	DRAW(col-1,ALEFT(col,row),col,RIGHT(col,row));
	break;
      case 4:
      case 11:
	DRAW(TOP(col),row,col,RIGHT(col,row));
	break;
      case 5:
      case 10:
	DRAW(BOT(col),row-1,TOP(col),row);
	break;
      case 6:
      case 9:
	{
	  double x0 = AINTER(FOLD(col-1),FOLD(col));
	  double x1 = AINTER(FNEW(col-1),FNEW(col));
	  double y0 = AINTER(FOLD(col-1),FNEW(col-1));
	  double y1 = AINTER(FOLD(col),FNEW(col));
	  double y = (x0*(y1-y0)+y0)/(1.0-(x1-x0)*(y1-y0));
	  double x = y*(x1-x0) + x0;
	  double fx1 = MAP(col-1,row-1)+x*(MAP(col,row-1)-MAP(col-1,row-1));
	  double fx2 = MAP(col-1,row)+x*(MAP(col,row)-MAP(col-1,row));
	  double f = fx1 + y*(fx2-fx1);
	  if (f==val) {
	    DRAW(BOT(col),row-1,TOP(col),row);
	    DRAW(col-1,ALEFT(col,row),col,RIGHT(col,row));
	  } else if (((f > val) && (FNEW(col) > val)) || 
		     ((f < val) && (FNEW(col) < val))) {
	    DRAW(col-1,ALEFT(col,row),TOP(col),row);
	    DRAW(BOT(col),row-1,col,RIGHT(col,row));
	  } else {
	    DRAW(col-1,ALEFT(col,row),BOT(col),row-1);
	    DRAW(TOP(col),row,col,RIGHT(col,row));
	  }
	}
	break;
      case 7:
      case 8:
	DRAW(col-1,ALEFT(col,row),TOP(col),row);
	break;
      }
    }
  // Now we link the line segments into longer lines.
  int allcount = allLines.size();
  while (!allLines.empty()) {
    // Start a new line segment
    cline current(allLines.takeAt(0));
    bool lineGrown = true;
    while (lineGrown) {
      lineGrown = false;
      int i = 0;
      while (i<allLines.size()) {
	if (Connected(current,allLines.at(i))) {
	  Join(current,allLines.takeAt(i));
	  lineGrown = true;
	} else
	  i++;
      }
    }
    bundledLines << current;
  }
  int outcount = bundledLines.size() + 2*allcount + 1;
  Array out(Array::doubleMatrixConstructor(2,outcount));
  double *output = (double *) out.getReadWriteDataPointer();
  for (int i=0;i<bundledLines.size();i++) {
    *output++ = val;
    *output++ = bundledLines[i].size();
    cline bline(bundledLines[i]);
    for (int j=0;j<bline.size();j++) {
      *output++ = bline[j].x;
      *output++ = bline[j].y;
    }
  }
  return ArrayVector() << out;
}
#endif 

void LoadHandleGraphicsFunctions(Context* context) {
  context->addGfxFunction("is2dview",HIs2DViewFunction,1,1,"x",NULL);
  context->addGfxFunction("axes",HAxesFunction,-1,1,NULL);
  context->addGfxFunction("hline",HLineFunction,-1,1,NULL);
  context->addGfxFunction("htext",HTextFunction,-1,1,NULL);
  context->addGfxFunction("himage",HImageFunction,-1,1,NULL);
  context->addGfxFunction("hcontour",HContourFunction,-1,1,NULL);
  context->addGfxFunction("surface",HSurfaceFunction,-1,1,NULL);
  context->addGfxFunction("set",HSetFunction,-1,0,NULL);
  context->addGfxFunction("get",HGetFunction,2,1,"handle","propname",NULL);
  context->addGfxFunction("figure",HFigureFunction,1,1,"number",NULL);
  context->addGfxSpecialFunction("uicontrol",HUIControlFunction,-1,1,NULL);
  context->addGfxFunction("gca",HGCAFunction,0,1,NULL);
  context->addGfxFunction("gcf",HGCFFunction,0,1,NULL);
  context->addGfxFunction("pvalid",HPropertyValidateFunction,2,1,"type","property",NULL);
  context->addGfxFunction("print",HPrintFunction,-1,0,NULL);
  context->addGfxFunction("close",HCloseFunction,1,0,"handle",NULL);
  context->addGfxFunction("copy",HCopyFunction,0,0,NULL);
  context->addGfxFunction("hpoint",HPointFunction,0,1,NULL);
  context->addGfxFunction("drawnow",DrawNowFunction,0,0,NULL);
  context->addGfxFunction("figraise",FigRaiseFunction,1,0,"handle",NULL);
  context->addGfxFunction("figlower",FigLowerFunction,1,0,"handle",NULL);
  //  context->addFunction("contourc",ContourCFunction,2,1,"z","v",NULL);
  //  context->addSpecialFunction("demo",HDemoFunction,1,1,NULL);
  InitializeHandleGraphics();
};


syntax highlighted by Code2HTML, v. 0.9.1