// Filename: multiTransition.T
// Created by:  drose (23Mar00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////

template<class Property, class NameClass>
TypeHandle MultiTransition<Property, NameClass>::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
MultiTransition<Property, NameClass>::
MultiTransition() {
  _default_dir = TD_identity;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::Copy Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
MultiTransition<Property, NameClass>::
MultiTransition(const MultiTransition &copy) :
  NodeTransition(copy),
  _properties(copy._properties),
  _default_dir(copy._default_dir)
{
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::Copy Assignment Operator
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
operator = (const MultiTransition &copy) {
  NodeTransition::operator = (copy);
  _properties = copy._properties;
  _default_dir = copy._default_dir;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::clear
//       Access: Public
//  Description: Removes all properties from the set, and resets it to
//               its initial state.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
clear() {
  _properties.clear();
  state_changed();
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::is_identity
//       Access: Public
//  Description: Returns true if this transition does not have any
//               effect.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
bool MultiTransition<Property, NameClass>::
is_identity() const {
  if (_default_dir != TD_identity) {
    return false;
  }

  Properties::const_iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second != TD_identity) {
      return false;
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::set_default_dir
//       Access: Public
//  Description: Sets the default direction on the MultiTransition.
//               This controls whether the MultiTransition applies
//               itself by completely replacing everything that went
//               before, or by adding to whatever has gone before.
//
//               When this is set to either TD_on or TD_off, all
//               properties which are not explicitly mentioned are
//               assumed to be 'on' or 'off' transitions,
//               respectively; when it is set to TD_identity, the
//               default, all properties which are not explicitly
//               mentions are assumed to be identity transitions and
//               are left unchanged.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
INLINE void MultiTransition<Property, NameClass>::
set_default_dir(TransitionDirection dir) {
  _default_dir = dir;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::get_default_dir
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
INLINE TransitionDirection MultiTransition<Property, NameClass>::
get_default_dir() const {
  return _default_dir;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::set_identity
//       Access: Public
//  Description: Changes the indicated property to an identity
//               transition: this property will not be changed by the
//               application of the MultiTransition.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
set_identity(const Property &property) {
  Properties::iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).first == property) {
      (*pi).second = TD_identity;
      state_changed();
      return;
    }
  }

  // It wasn't already on the list; append it.
  _properties.push_back(Element(property, TD_identity));

  // And now we need to re-sort the list.
  sort(_properties.begin(), _properties.end(), SortByFirstOfPair());
  state_changed();
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::set_on
//       Access: Public
//  Description: Changes the indicated property to an 'on' transition:
//               this property will be explicitly turned on by the
//               application of the MultiTransition.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
set_on(const Property &property) {
  Properties::iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).first == property) {
      (*pi).second = TD_on;
      state_changed();
      return;
    }
  }

  // It wasn't already on the list; append it.
  _properties.push_back(Element(property, TD_on));

  // And now we need to re-sort the list.
  sort(_properties.begin(), _properties.end(), SortByFirstOfPair());
  state_changed();
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::set_off
//       Access: Public
//  Description: Changes the indicated property to an 'off' transition:
//               this property will be explicitly turned off by the
//               application of the MultiTransition.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
set_off(const Property &property) {
  Properties::iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).first == property) {
      (*pi).second = TD_off;
      state_changed();
      return;
    }
  }

  // It wasn't already on the list; append it.
  _properties.push_back(Element(property, TD_off));

  // And now we need to re-sort the list.
  sort(_properties.begin(), _properties.end(), SortByFirstOfPair());
  state_changed();
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::is_identity
//       Access: Public
//  Description: Returns true if this property has been indicated as
//               an identity transition, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
bool MultiTransition<Property, NameClass>::
is_identity(const Property &property) const {
  Properties::const_iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).first == property) {
      return (*pi).second == TD_identity;
    }
  }

  // The property is not mentioned.  It's implicitly identity only if
  // the default_dir flag is identity.
  return (_default_dir == TD_identity);
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::is_on
//       Access: Public
//  Description: Returns true if this property has been indicated as
//               an 'on' transition, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
bool MultiTransition<Property, NameClass>::
is_on(const Property &property) const {
  Properties::const_iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).first == property) {
      return (*pi).second == TD_on;
    }
  }

  // The property is not mentioned.  It's implicitly on only if
  // the default_dir flag is on.
  return (_default_dir == TD_on);
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::is_on
//       Access: Public
//  Description: Returns true if this property has been indicated as
//               an 'off' transition, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
bool MultiTransition<Property, NameClass>::
is_off(const Property &property) const {
  Properties::const_iterator pi;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).first == property) {
      return (*pi).second == TD_off;
    }
  }

  // The property is not mentioned.  It's implicitly off only if
  // the default_dir flag is off.
  return (_default_dir == TD_off);
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::make_identity
//       Access: Public, Virtual
//  Description: This is a pure virtual function and normally would
//               not need a body, except that VC++ doesn't seem happy
//               about instantiating the template otherwise.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
NodeTransition *MultiTransition<Property, NameClass>::
make_identity() const {
  return NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::compose
//       Access: Public, Virtual
//  Description: Returns a new transition that corresponds to the
//               composition of this transition with the second
//               transition (which must be of an equivalent type).
//               This may return the same pointer as either source
//               transition.  Applying the transition returned from
//               this function to an attribute attribute will produce
//               the same effect as applying each transition
//               separately.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
NodeTransition *MultiTransition<Property, NameClass>::
compose(const NodeTransition *other) const {
  const MultiTransition<Property, NameClass> *ot;
  DCAST_INTO_R(ot, other, NULL);

  if (ot->_priority < _priority) {
    // The other transition too low-priority; the result is unchanged.
    return (MultiTransition<Property, NameClass> *)this;

  } else if (ot->_priority > _priority) {
    // The other priority is higher: the result is the same as the other.
    return (MultiTransition<Property, NameClass> *)ot;

  } else if (ot->_default_dir != TD_identity) {
    // The other transition is a complete-world transition: the result
    // is the same as other.
    return (MultiTransition<Property, NameClass> *)ot;

  } else if (is_identity()) {
    // We're identity: the result is the same as other.
    return (MultiTransition<Property, NameClass> *)ot;

  } else if (ot->is_identity()) {
    // The other transition is identity: it has no effect.
    return (MultiTransition<Property, NameClass> *)this;

  } else {
    // Neither of us is identity.  Build and return a new list.
    MultiTransition<Property, NameClass> *result;
    DCAST_INTO_R(result, make_identity(), NULL);
    result->_default_dir = _default_dir;

    dmap_compose(_properties.begin(), _properties.end(),
                 ot->_properties.begin(), ot->_properties.end(),
                 back_inserter(result->_properties));

    return result;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::invert
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
NodeTransition *MultiTransition<Property, NameClass>::
invert() const {
  if (is_identity()) {
    return (MultiTransition<Property, NameClass> *)this;
  }

  MultiTransition<Property, NameClass> *result;
  DCAST_INTO_R(result, make_identity(), NULL);

  result->_default_dir = TD_identity;

  dmap_invert(_properties.begin(), _properties.end(),
              back_inserter(result->_properties),
              (Element *)NULL);

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
output(ostream &out) const {
  Properties::const_iterator pi;

  bool any_identity = false;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second == TD_identity) {
      if (!any_identity) {
        out << " identity(";
        any_identity = true;
      } else {
        out << ", ";
      }
      output_property(out, (*pi).first);
    }
  }
  if (any_identity) {
    out << ")";
  }

  bool any_on = false;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second == TD_on) {
      if (!any_on) {
        out << " on(";
        any_on = true;
      } else {
        out << ", ";
      }
      output_property(out, (*pi).first);
    }
  }
  if (any_on) {
    out << ")";
  }

  bool any_off = false;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second == TD_off) {
      if (!any_off) {
        out << " off(";
        any_off = true;
      } else {
        out << ", ";
      }
      output_property(out, (*pi).first);
    }
  }
  if (any_off) {
    out << ")";
  }
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::write
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
write(ostream &out, int indent_level) const {
  Properties::const_iterator pi;

  bool any_identity = false;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second == TD_identity) {
      if (!any_identity) {
        indent(out, indent_level) << "identity:\n";
        any_identity = true;
      }
      write_property(out, (*pi).first, indent_level + 2);
    }
  }

  bool any_on = false;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second == TD_on) {
      if (!any_on) {
        indent(out, indent_level) << "on:\n";
        any_on = true;
      }
      write_property(out, (*pi).first, indent_level + 2);
    }
  }

  bool any_off = false;
  for (pi = _properties.begin(); pi != _properties.end(); ++pi) {
    if ((*pi).second == TD_off) {
      if (!any_off) {
        indent(out, indent_level) << "off:\n";
        any_off = true;
      }
      write_property(out, (*pi).first, indent_level + 2);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::internal_compare_to
//       Access: Protected, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
int MultiTransition<Property, NameClass>::
internal_compare_to(const NodeTransition *other) const {
  const MultiTransition<Property, NameClass> *ot;
  DCAST_INTO_R(ot, other, false);

  if (_default_dir != ot->_default_dir) {
    return (int)_default_dir - (int)ot->_default_dir;
  }

  return dmap_compare(_properties.begin(), _properties.end(),
                      ot->_properties.begin(), ot->_properties.end());
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::output_property
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
output_property(ostream &, const Property &) const {
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::write_property
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
void MultiTransition<Property, NameClass>::
write_property(ostream &, const Property &, int) const {
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::size
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
INLINE MultiTransition<Property, NameClass>::size_type MultiTransition<Property, NameClass>::
size() const {
  return _properties.size();
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::begin
//       Access: Public
//  Description: Returns an iterator that begins the sequence of
//               properties in the transition.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
INLINE MultiTransition<Property, NameClass>::const_iterator MultiTransition<Property, NameClass>::
begin() const {
  return _properties.begin();
}

////////////////////////////////////////////////////////////////////
//     Function: MultiTransition::end
//       Access: Public
//  Description: Returns an iterator that marks the end of the
//               sequence of properties in the transition.
////////////////////////////////////////////////////////////////////
template<class Property, class NameClass>
INLINE MultiTransition<Property, NameClass>::const_iterator MultiTransition<Property, NameClass>::
end() const {
  return _properties.end();
}

