/*
 * 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 "HandleProperty.hpp"
#include "HandleList.hpp"
#include "HandleObject.hpp"
#include "HandleCommands.hpp"
#include "Core.hpp"

static const char *auto_manual_dict[3] = {"auto","manual",0};
static const char *on_off_dict[3] = {"on","off",0};
static const char *on_inactive_off_dict[4] = {"on","active","off",0};
static const char *font_angle_dict[4] = {"normal","italic","oblique",0};
static const char *font_units_dict[6] = {"points","normalized","inches",
					 "centimeters","pixels",0};
static const char *font_weight_dict[5] = {"normal","bold","light","demi",0};
static const char *line_style_dict[6] = {"-","--",":","-.","none",0};
static const char *top_bottom_dict[3] = {"top","bottom",0};
static const char *left_right_dict[3] = {"left","right",0};
static const char *normal_reverse_dict[3] = {"normal","reverse",0};
static const char *linear_log_dict[3] = {"linear","log",0};
static const char *next_plot_dict[4] = {"add","replace","replacechildren",0};
static const char *projection_mode_dict[3] = {"orthographic","perspective",0};
static const char *in_out_dict[3] = {"in","out",0};
static const char *units_dict[8] = {"inches","centimeters","normalized",
				    "points","pixels","characters","data",0};
static const char *position_dict[3] = {"outerposition","position",0};
static const char *horiz_dict[4] = {"left","center","right",0};
static const char *vert_dict[6] = {"top","cap","middle","baseline","bottom",0};
static const char *symb_dict[19] = {"+","o","*",".","x","square","s","diamond","d","^","v",">","<","pentagram","p","hexagram","h","none",0};
static const char *mapmode_dict[4] = {"none","direct","scaled",0};
static const char *datamapmode_dict[3] = {"scaled","direct",0};
static const char *rowcols_dict[4] = {"both","rows","cols",0};
static const char *autoflat_dict[5] = {"none","auto","flat","colorspec",0};
static const char *lightingmode_dict[5] = {"none","flat","gouraud","phong",0};
static const char *interpmode_dict[5] = {"none","flat","interp","colorspec",0};
static const char *facealpha_dict[5] = {"flat","interp","texturemap","scalar",0};
static const char *backface_dict[4] = {"unlit","lit","reverselit",0};
static const char *edgealpha_dict[4] = {"flat","interp","scalar",0};
static const char *render_dict[3] = {"painters","opengl",0};
static const char *widget_dict[11] = {"pushbutton","toggle","radiobutton","checkbox",
				      "edit","text","slider","frame","listbox","popupmenu",0};
static const char *autocolor_dict[4] = {"none","auto","colorspec",0};
  
HPAutoManual::HPAutoManual() : HPConstrainedString(auto_manual_dict) {}
HPOnOff::HPOnOff() : HPConstrainedString(on_off_dict) {}
HPOnOffInactive::HPOnOffInactive() : HPConstrainedString(on_inactive_off_dict) {}
HPFontAngle::HPFontAngle() : HPConstrainedString(font_angle_dict) {}
HPFontUnits::HPFontUnits() : HPConstrainedString(font_units_dict) {}
HPFontWeight::HPFontWeight() : HPConstrainedString(font_weight_dict) {}
HPLineStyle::HPLineStyle() : HPConstrainedString(line_style_dict) {}
HPTopBottom::HPTopBottom() : HPConstrainedString(top_bottom_dict) {}
HPLeftRight::HPLeftRight() : HPConstrainedString(left_right_dict) {}
HPNormalReverse::HPNormalReverse() : HPConstrainedString(normal_reverse_dict) {}
HPLinearLog::HPLinearLog() : HPConstrainedString(linear_log_dict) {}
HPNextPlotMode::HPNextPlotMode() : HPConstrainedString(next_plot_dict) {}
HPProjectionMode::HPProjectionMode() : HPConstrainedString(projection_mode_dict) {}
HPInOut::HPInOut() : HPConstrainedString(in_out_dict) {}
HPUnits::HPUnits() : HPConstrainedString(units_dict) {}
HPPosition::HPPosition() : HPConstrainedString(position_dict) {}
HPAlignHoriz::HPAlignHoriz() : HPConstrainedString(horiz_dict) {}
HPAlignVert::HPAlignVert() : HPConstrainedString(vert_dict) {}
HPSymbol::HPSymbol() : HPConstrainedString(symb_dict) {}
HPMappingMode::HPMappingMode() : HPConstrainedString(mapmode_dict) {}
HPDataMappingMode::HPDataMappingMode() : HPConstrainedString(datamapmode_dict) {}
HPLineStyleOrder::HPLineStyleOrder() : HPConstrainedStringSet(line_style_dict) {}
HPRowColumns::HPRowColumns() : HPConstrainedString(rowcols_dict) {}
HPLightingMode::HPLightingMode() : HPConstrainedString(lightingmode_dict) {}
HPAutoFlatColor::HPAutoFlatColor() : HPConstrainedStringColor(autoflat_dict) {}
HPAutoColor::HPAutoColor() : HPConstrainedStringColor(autocolor_dict) {}
HPColorInterp::HPColorInterp() : HPConstrainedStringColor(interpmode_dict) {}
HPFaceAlpha::HPFaceAlpha() : HPConstrainedStringScalar(facealpha_dict) {}
HPBackFaceLighting::HPBackFaceLighting() : HPConstrainedString(backface_dict) {}
HPWidgetString::HPWidgetString() : HPConstrainedString(widget_dict) {}
HPEdgeAlpha::HPEdgeAlpha() : HPConstrainedStringScalar(edgealpha_dict) {}
HPRenderMode::HPRenderMode() : HPConstrainedString(render_dict) {}

HPHandles::HPHandles() {
}
  
Array HPHandles::Get() {
  Array ret(Array::uint32VectorConstructor(data.size()));
  unsigned *dp = (unsigned*) ret.getReadWriteDataPointer();
  for (int i=0;i<data.size();i++)
    dp[i] = data[i];
  return ret;
}

void HPHandles::Set(Array arg) {
  if (arg.isEmpty()) {
    data.clear();
    HandleProperty::Set(arg);
    return;
  }
  if (!arg.isReal())
    throw Exception("expecting handle for property");
  arg.promoteType(FM_UINT32);
  const unsigned *dp = (const unsigned*) arg.getDataPointer();
  // make sure they are all valid handles
  for (int i=0;i<arg.getLength();i++) 
    ValidateHandle(dp[i]);
  data.clear();
  for (int i=0;i<arg.getLength();i++) 
    data.push_back(dp[i]);
  HandleProperty::Set(arg); 
}

Array HPString::Get() {
  return Array::stringConstructor(data);
}

void HPString::Set(Array arg) {
  HandleProperty::Set(arg);
  if (!arg.isString())
    throw Exception("Expecting a string for property ");
  data = ArrayToString(arg);
}
  
Array HPVector::Get() {
  Array ret(Array::doubleVectorConstructor(data.size()));    
  double *dp = (double*) ret.getReadWriteDataPointer();
  for (int i=0;i<data.size();i++)
    dp[i] = data[i];
  return ret;
}
  
void HPVector::Set(Array num) {
  HandleProperty::Set(num);
  num.promoteType(FM_DOUBLE);
  const double *dp = (const double*) num.getDataPointer();
  data.clear();
  for (int i=0;i<num.getLength();i++)
    data.push_back(dp[i]);
}
  
void HPFixedVector::Set(Array num) {
  HandleProperty::Set(num);
  num.promoteType(FM_DOUBLE);
  const double *dp = (const double*) num.getDataPointer();
  data.clear();
  for (int i=0;i<qMin(m_len,(unsigned int)num.getLength());i++)
    data.push_back(dp[i]);
}

double& HPVector::At(int ndx) {
  return data[ndx];
}

double& HPVector::operator[](int ndx) {
  return data[ndx];
}

void HPColorVector::Set(Array arg) {
  HandleProperty::Set(arg);
  arg.promoteType(FM_DOUBLE);
  if ((!arg.is2D()) || (arg.getDimensionLength(1) != 3))
    throw Exception("Expect an m x 3 matrix for color orders");
  const double *dp = (const double *) arg.getDataPointer();
  int n = arg.getLength();
  for (int i=0;i<n;i++) 
    if ((dp[i] < 0) || (dp[i] > 1.0))
      throw Exception("Color vector must be between 0 and 1");
  data.clear();
  int rows = n/3;
  for (int i=0;i<rows;i++) 
    for (int j=0;j<3;j++)
      data.push_back(dp[i+j*rows]);
}

Array HPColorVector::Get() {
  int count = data.size();
  int rows = count/3;
  double *rp = (double*) Array::allocateArray(FM_DOUBLE,count);
  for (int i=0;i<rows;i++)
    for (int j=0;j<3;j++)
      rp[i+j*rows] = data[3*i+j];
  return Array::Array(FM_DOUBLE,Dimensions(rows,3),rp);
}

//!
//@Module COLORSPEC Color Property Description
//@@Section HANDLE
//@@Usage
//There are a number of ways of specifying a color value for
//a color-based property.  Examples include line colors, 
//marker colors, and the like.  One option is to specify
//color as an RGB triplet
//@[
//   set(h,'color',[r,g,b])
//@]
//where @|r,g,b| are between @[0,1]@.  Alternately, you can
//use color names to specify a color.
//\begin{itemize}
//  \item @|'none'| - No color.
//  \item @|'y','yellow'| - The color @[1,1,0]@ in RGB space.
//  \item @|'m','magenta'| - The color @[1,0,1]@ in RGB space.
//  \item @|'c','cyan'| - The color @[0,1,1]@ in RGB space.
//  \item @|'r','red'| - The color @[1,0,0]@ in RGB space.
//  \item @|'g','green'| - The color @[0,1,0]@ in RGB space.
//  \item @|'b','blue'| - The color @[0,0,1]@ in RGB space.
//  \item @|'w','white'| - The color @[1,1,1]@ in RGB space.
//  \item @|'k','black'| - The color @[0,0,0]@ in RGB space.
//\end{itemize}
//!
bool ParseColorSpec(Array arg, std::vector<double> &data) {
  if (arg.isString()) {
    string cp = arg.getContentsAsString();
    if (cp=="none") {
      data.clear(); data.push_back(-1); 
      data.push_back(-1); data.push_back(-1);
    } else if ((cp=="y") || (cp=="yellow")) {
      data.clear(); data.push_back(.75); 
      data.push_back(.75); data.push_back(0);
    } else if ((cp=="m") || (cp=="magenta")) {
      data.clear(); data.push_back(.75); 
      data.push_back(0); data.push_back(.75);
    } else if ((cp=="c") || (cp=="cyan")) {
      data.clear(); data.push_back(0); 
      data.push_back(.75); data.push_back(.75);
    } else if ((cp=="r") || (cp=="red")) {
      data.clear(); data.push_back(1); 
      data.push_back(0); data.push_back(0);
    } else if ((cp=="g") || (cp=="green")) {
      data.clear(); data.push_back(0); 
      data.push_back(.5); data.push_back(0);
    } else if ((cp=="b") || (cp=="blue")) {
      data.clear(); data.push_back(0); 
      data.push_back(0); data.push_back(1);
    } else if ((cp=="w") || (cp=="white")) {
      data.clear(); data.push_back(1); 
      data.push_back(1); data.push_back(1);
    } else if ((cp=="k") || (cp=="black")) {
      data.clear(); data.push_back(0); 
      data.push_back(0); data.push_back(0);
    } else
      return false;
  } else {
    if (arg.getLength() != 3)
      return false;
    arg.promoteType(FM_DOUBLE);
    const double *dp = (const double*) arg.getDataPointer();
    if (((dp[0] < 0) || (dp[0] > 1)) ||
	((dp[1] < 0) || (dp[1] > 1)) ||
	((dp[2] < 0) || (dp[2] > 1)))
      return false;
    data.clear();
    data.push_back(dp[0]);
    data.push_back(dp[1]);
    data.push_back(dp[2]);
  }
  return true;
}

void HPColor::Set(Array arg) {
  HandleProperty::Set(arg);
  if (!ParseColorSpec(arg,data))
    throw Exception("Expecting a color spec: either a color name or a 3-vector or RGB values");
}
  
Array HPColor::Get() {
  if (data[0] == -1)
    return Array::stringConstructor("none");
  else
    return HPVector::Get();
}
  
Array HPStringSet::Get() {
  if (data.size() == 0)
    return Array::stringConstructor("");
  std::string retval;
  for (unsigned i=0;i<data.size()-1;i++) {
    retval.append(data[i]);
    retval.append("|");
  }
  retval.append(data.back());
  return Array::stringConstructor(retval);
}

void HPStringSet::Set(Array arg) {
  HandleProperty::Set(arg);
  if (!arg.isString()) 
    throw Exception("expecting a '|'-delimited list of strings for property argument");
  std::string args(ArrayToString(arg));
  data.clear();
  Tokenize(args,data,"|");
}

double HPConstrainedStringScalar::Scalar() {
  return scalar;
}

void HPConstrainedStringScalar::Scalar(double scal) {
  scalar = scal;
}

void HPConstrainedStringScalar::Set(Array arg) {
  HandleProperty::Set(arg);
  if (arg.isString())
    HPConstrainedString::Set(arg);
  else
    scalar = ArrayToDouble(arg);
}

std::vector<double> HPConstrainedStringColor::ColorSpec() {
  return colorspec;
}

void HPConstrainedStringColor::ColorSpec(std::vector<double> col) {
  colorspec = col;
}

void HPConstrainedStringColor::ColorSpec(double r, double g, double b) {
  std::vector<double> data;
  data.push_back(r);
  data.push_back(g);
  data.push_back(b);
  colorspec = data;
}

void HPConstrainedStringColor::Set(Array arg) {
  HandleProperty::Set(arg);
  if (!ParseColorSpec(arg,colorspec)) {
    HPConstrainedString::Set(arg);
  } else {
    data = "colorspec";
  }
  if (colorspec.size() > 1 && (colorspec[0] == -1))
    data = "none";
}

Array HPConstrainedStringColor::Get() {
  if (!Is("colorspec"))
    return HPConstrainedString::Get();
  Array ret(Array::doubleVectorConstructor(3));
  double *dp = (double*) ret.getReadWriteDataPointer();
  dp[0] = colorspec[0];
  dp[1] = colorspec[1];
  dp[2] = colorspec[2];
  return ret;
}

Array HPConstrainedStringScalar::Get() {
  if (!Is("scalar"))
    return HPConstrainedString::Get();
  return Array::doubleConstructor(scalar);
}

void HPConstrainedString::Set(Array arg) {
  HandleProperty::Set(arg);
  if (!arg.isString())
    throw Exception("expecting a string for property");
  std::string tst(ArrayToString(arg));
  if (find(m_dictionary.begin(),m_dictionary.end(),tst) == m_dictionary.end())
    throw Exception("illegal selection for property");
  HPString::Set(arg);
}

void HPConstrainedStringSet::Set(Array arg) {
  HandleProperty::Set(arg);
  HPStringSet::Set(arg);
  // Validate the result
  for (int i=0;i<data.size();i++)
    if (find(m_dictionary.begin(),m_dictionary.end(),data[i]) == m_dictionary.end())
      throw Exception("illegal selection for property");
}

void HPScalar::Value(double x) {
  At(0) = x;
}


syntax highlighted by Code2HTML, v. 0.9.1