ifndef SASS_AST_VALUES_H define SASS_AST_VALUES_H

// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. include “sass.hpp” include “ast.hpp”

namespace Sass {

//////////////////////////////////////////////////////////////////////
// Still just an expression, but with a to_string method
//////////////////////////////////////////////////////////////////////
class PreValue : public Expression {
public:
  PreValue(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);
  ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);
  virtual ~PreValue() { }
};

//////////////////////////////////////////////////////////////////////
// base class for values that support operations
//////////////////////////////////////////////////////////////////////
class Value : public PreValue {
public:
  Value(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);

  // Some obects are not meant to be compared
  // ToDo: maybe fallback to pointer comparison?
  virtual bool operator< (const Expression& rhs) const override = 0;
  virtual bool operator== (const Expression& rhs) const override = 0;

  // We can give some reasonable implementations by using
  // inverst operators on the specialized implementations
  virtual bool operator> (const Expression& rhs) const {
    return rhs < *this;
  }
  virtual bool operator!= (const Expression& rhs) const {
    return !(*this == rhs);
  }

  ATTACH_VIRTUAL_AST_OPERATIONS(Value);

};

///////////////////////////////////////////////////////////////////////
// Lists of values, both comma- and space-separated (distinguished by a
// type-tag.) Also used to represent variable-length argument lists.
///////////////////////////////////////////////////////////////////////
class List : public Value, public Vectorized<ExpressionObj> {
  void adjust_after_pushing(ExpressionObj e) override { is_expanded(false); }
private:
  ADD_PROPERTY(enum Sass_Separator, separator)
  ADD_PROPERTY(bool, is_arglist)
  ADD_PROPERTY(bool, is_bracketed)
  ADD_PROPERTY(bool, from_selector)
public:
  List(SourceSpan pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false);
  sass::string type() const override { return is_arglist_ ? "arglist" : "list"; }
  static sass::string type_name() { return "list"; }
  const char* sep_string(bool compressed = false) const {
    return separator() == SASS_SPACE ?
      " " : (compressed ? "," : ", ");
  }
  bool is_invisible() const override { return empty() && !is_bracketed(); }
  ExpressionObj value_at_index(size_t i);

  virtual size_t hash() const override;
  virtual size_t size() const;
  virtual void set_delayed(bool delayed) override;

  virtual bool operator< (const Expression& rhs) const override;
  virtual bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(List)
  ATTACH_CRTP_PERFORM_METHODS()
};

///////////////////////////////////////////////////////////////////////
// Key value paris.
///////////////////////////////////////////////////////////////////////
class Map : public Value, public Hashed<ExpressionObj, ExpressionObj, Map_Obj> {
  void adjust_after_pushing(std::pair<ExpressionObj, ExpressionObj> p) override { is_expanded(false); }
public:
  Map(SourceSpan pstate, size_t size = 0);
  sass::string type() const override { return "map"; }
  static sass::string type_name() { return "map"; }
  bool is_invisible() const override { return empty(); }
  List_Obj to_list(SourceSpan& pstate);

  virtual size_t hash() const override;

  virtual bool operator< (const Expression& rhs) const override;
  virtual bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(Map)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////////////////////////////////////////////////////////////////////
// Binary expressions. Represents logical, relational, and arithmetic
// operations. Templatized to avoid large switch statements and repetitive
// subclassing.
//////////////////////////////////////////////////////////////////////////
class Binary_Expression : public PreValue {
private:
  HASH_PROPERTY(Operand, op)
  HASH_PROPERTY(ExpressionObj, left)
  HASH_PROPERTY(ExpressionObj, right)
  mutable size_t hash_;
public:
  Binary_Expression(SourceSpan pstate,
                    Operand op, ExpressionObj lhs, ExpressionObj rhs);

  const sass::string type_name();
  const sass::string separator();
  bool is_left_interpolant(void) const override;
  bool is_right_interpolant(void) const override;
  bool has_interpolant() const override;

  virtual void set_delayed(bool delayed) override;

  virtual bool operator< (const Expression& rhs) const override;
  virtual bool operator==(const Expression& rhs) const override;

  virtual size_t hash() const override;
  enum Sass_OP optype() const { return op_.operand; }
  ATTACH_AST_OPERATIONS(Binary_Expression)
  ATTACH_CRTP_PERFORM_METHODS()
};

////////////////////////////////////////////////////
// Function reference.
////////////////////////////////////////////////////
class Function final : public Value {
public:
  ADD_PROPERTY(Definition_Obj, definition)
  ADD_PROPERTY(bool, is_css)
public:
  Function(SourceSpan pstate, Definition_Obj def, bool css);

  sass::string type() const override { return "function"; }
  static sass::string type_name() { return "function"; }
  bool is_invisible() const override { return true; }

  sass::string name();

  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(Function)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////////////
// Function calls.
//////////////////
class Function_Call final : public PreValue {
  HASH_CONSTREF(String_Obj, sname)
  HASH_PROPERTY(Arguments_Obj, arguments)
  HASH_PROPERTY(Function_Obj, func)
  ADD_PROPERTY(bool, via_call)
  ADD_PROPERTY(void*, cookie)
  mutable size_t hash_;
public:
  Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, void* cookie);
  Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Function_Obj func);
  Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args);

  Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, void* cookie);
  Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, Function_Obj func);
  Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args);

  sass::string name() const;
  bool is_css();

  bool operator==(const Expression& rhs) const override;

  size_t hash() const override;

  ATTACH_AST_OPERATIONS(Function_Call)
  ATTACH_CRTP_PERFORM_METHODS()
};

///////////////////////
// Variable references.
///////////////////////
class Variable final : public PreValue {
  ADD_CONSTREF(sass::string, name)
public:
  Variable(SourceSpan pstate, sass::string n);
  virtual bool operator==(const Expression& rhs) const override;
  virtual size_t hash() const override;
  ATTACH_AST_OPERATIONS(Variable)
  ATTACH_CRTP_PERFORM_METHODS()
};

////////////////////////////////////////////////
// Numbers, percentages, dimensions, and colors.
////////////////////////////////////////////////
class Number final : public Value, public Units {
  HASH_PROPERTY(double, value)
  ADD_PROPERTY(bool, zero)
  mutable size_t hash_;
public:
  Number(SourceSpan pstate, double val, sass::string u = "", bool zero = true);

  bool zero() { return zero_; }

  sass::string type() const override { return "number"; }
  static sass::string type_name() { return "number"; }

  // cancel out unnecessary units
  // result will be in input units
  void reduce();

  // normalize units to defaults
  // needed to compare two numbers
  void normalize();

  size_t hash() const override;

  bool operator< (const Number& rhs) const;
  bool operator== (const Number& rhs) const;
  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;
  ATTACH_AST_OPERATIONS(Number)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////
// Colors.
//////////
class Color : public Value {
  ADD_CONSTREF(sass::string, disp)
  HASH_PROPERTY(double, a)
protected:
  mutable size_t hash_;
public:
  Color(SourceSpan pstate, double a = 1, const sass::string disp = "");

  sass::string type() const override { return "color"; }
  static sass::string type_name() { return "color"; }

  virtual size_t hash() const override = 0;

  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;

  virtual Color_RGBA* copyAsRGBA() const = 0;
  virtual Color_RGBA* toRGBA() = 0;

  virtual Color_HSLA* copyAsHSLA() const = 0;
  virtual Color_HSLA* toHSLA() = 0;

  ATTACH_VIRTUAL_AST_OPERATIONS(Color)
};

//////////
// Colors.
//////////
class Color_RGBA final : public Color {
  HASH_PROPERTY(double, r)
  HASH_PROPERTY(double, g)
  HASH_PROPERTY(double, b)
public:
  Color_RGBA(SourceSpan pstate, double r, double g, double b, double a = 1, const sass::string disp = "");

  sass::string type() const override { return "color"; }
  static sass::string type_name() { return "color"; }

  size_t hash() const override;

  Color_RGBA* copyAsRGBA() const override;
  Color_RGBA* toRGBA() override { return this; }

  Color_HSLA* copyAsHSLA() const override;
  Color_HSLA* toHSLA() override { return copyAsHSLA(); }

  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(Color_RGBA)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////
// Colors.
//////////
class Color_HSLA final : public Color {
  HASH_PROPERTY(double, h)
  HASH_PROPERTY(double, s)
  HASH_PROPERTY(double, l)
public:
  Color_HSLA(SourceSpan pstate, double h, double s, double l, double a = 1, const sass::string disp = "");

  sass::string type() const override { return "color"; }
  static sass::string type_name() { return "color"; }

  size_t hash() const override;

  Color_RGBA* copyAsRGBA() const override;
  Color_RGBA* toRGBA() override { return copyAsRGBA(); }

  Color_HSLA* copyAsHSLA() const override;
  Color_HSLA* toHSLA() override { return this; }

  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(Color_HSLA)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////////////////////////
// Errors from Sass_Values.
//////////////////////////////
class Custom_Error final : public Value {
  ADD_CONSTREF(sass::string, message)
public:
  Custom_Error(SourceSpan pstate, sass::string msg);
  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;
  ATTACH_AST_OPERATIONS(Custom_Error)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////////////////////////
// Warnings from Sass_Values.
//////////////////////////////
class Custom_Warning final : public Value {
  ADD_CONSTREF(sass::string, message)
public:
  Custom_Warning(SourceSpan pstate, sass::string msg);
  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;
  ATTACH_AST_OPERATIONS(Custom_Warning)
  ATTACH_CRTP_PERFORM_METHODS()
};

////////////
// Booleans.
////////////
class Boolean final : public Value {
  HASH_PROPERTY(bool, value)
  mutable size_t hash_;
public:
  Boolean(SourceSpan pstate, bool val);
  operator bool() override { return value_; }

  sass::string type() const override { return "bool"; }
  static sass::string type_name() { return "bool"; }

  size_t hash() const override;

  bool is_false() override { return !value_; }

  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(Boolean)
  ATTACH_CRTP_PERFORM_METHODS()
};

////////////////////////////////////////////////////////////////////////
// Abstract base class for Sass string values. Includes interpolated and
// "flat" strings.
////////////////////////////////////////////////////////////////////////
class String : public Value {
public:
  String(SourceSpan pstate, bool delayed = false);
  static sass::string type_name() { return "string"; }
  virtual ~String() = 0;
  virtual void rtrim() = 0;
  virtual bool operator<(const Expression& rhs) const override {
    return this->to_string() < rhs.to_string();
  };
  virtual bool operator==(const Expression& rhs) const override {
    return this->to_string() == rhs.to_string();
  };
  ATTACH_VIRTUAL_AST_OPERATIONS(String);
  ATTACH_CRTP_PERFORM_METHODS()
};
inline String::~String() { };

///////////////////////////////////////////////////////////////////////
// Interpolated strings. Meant to be reduced to flat strings during the
// evaluation phase.
///////////////////////////////////////////////////////////////////////
class String_Schema final : public String, public Vectorized<PreValueObj> {
  ADD_PROPERTY(bool, css)
  mutable size_t hash_;
public:
  String_Schema(SourceSpan pstate, size_t size = 0, bool css = true);

  sass::string type() const override { return "string"; }
  static sass::string type_name() { return "string"; }

  bool is_left_interpolant(void) const override;
  bool is_right_interpolant(void) const override;

  bool has_interpolants();
  void rtrim() override;
  size_t hash() const override;
  virtual void set_delayed(bool delayed) override;

  bool operator< (const Expression& rhs) const override;
  bool operator==(const Expression& rhs) const override;
  ATTACH_AST_OPERATIONS(String_Schema)
  ATTACH_CRTP_PERFORM_METHODS()
};

////////////////////////////////////////////////////////
// Flat strings -- the lowest level of raw textual data.
////////////////////////////////////////////////////////
class String_Constant : public String {
  ADD_PROPERTY(char, quote_mark)
  HASH_CONSTREF(sass::string, value)
protected:
  mutable size_t hash_;
public:
  String_Constant(SourceSpan pstate, sass::string val, bool css = true);
  String_Constant(SourceSpan pstate, const char* beg, bool css = true);
  String_Constant(SourceSpan pstate, const char* beg, const char* end, bool css = true);
  String_Constant(SourceSpan pstate, const Token& tok, bool css = true);
  sass::string type() const override { return "string"; }
  static sass::string type_name() { return "string"; }
  bool is_invisible() const override;
  virtual void rtrim() override;
  size_t hash() const override;
  bool operator< (const Expression& rhs) const override;
  bool operator==(const Expression& rhs) const override;
  // quotes are forced on inspection
  virtual sass::string inspect() const override;
  ATTACH_AST_OPERATIONS(String_Constant)
  ATTACH_CRTP_PERFORM_METHODS()
};

////////////////////////////////////////////////////////
// Possibly quoted string (unquote on instantiation)
////////////////////////////////////////////////////////
class String_Quoted final : public String_Constant {
public:
  String_Quoted(SourceSpan pstate, sass::string val, char q = 0,
    bool keep_utf8_escapes = false, bool skip_unquoting = false,
    bool strict_unquoting = true, bool css = true);
  bool operator< (const Expression& rhs) const override;
  bool operator==(const Expression& rhs) const override;
  // quotes are forced on inspection
  sass::string inspect() const override;
  ATTACH_AST_OPERATIONS(String_Quoted)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////////////
// The null value.
//////////////////
class Null final : public Value {
public:
  Null(SourceSpan pstate);
  sass::string type() const override { return "null"; }
  static sass::string type_name() { return "null"; }
  bool is_invisible() const override { return true; }
  operator bool() override { return false; }
  bool is_false() override { return true; }

  size_t hash() const override;

  bool operator< (const Expression& rhs) const override;
  bool operator== (const Expression& rhs) const override;

  ATTACH_AST_OPERATIONS(Null)
  ATTACH_CRTP_PERFORM_METHODS()
};

//////////////////////////////////
// The Parent Reference Expression.
//////////////////////////////////
class Parent_Reference final : public Value {
public:
  Parent_Reference(SourceSpan pstate);
  sass::string type() const override { return "parent"; }
  static sass::string type_name() { return "parent"; }
  bool operator< (const Expression& rhs) const override {
    return false; // they are always equal
  }
  bool operator==(const Expression& rhs) const override {
    return true; // they are always equal
  };
  ATTACH_AST_OPERATIONS(Parent_Reference)
  ATTACH_CRTP_PERFORM_METHODS()
};

}

endif