/*
//
// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//				
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/

#include "opennurbs.h"

#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined 
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif

//////////////////////////////////////////////////////////////////////////
//
// ON_ComponentStatus bits
//

#define SELECTED_BIT (0x01U)
#define SELECTED_PERSISTENT_BIT (0x02U)
#define SELECTED_MASK   (SELECTED_BIT | SELECTED_PERSISTENT_BIT)
#define HIGHLIGHTED_BIT (0x04U)
#define LOCKED_BIT     (0x08U)
#define HIDDEN_BIT     (0x10U)

// A mark is a tool used in a wide variety of ways by calculations. 
// Its value is unpredictable outside the scope of a specific code block. 
// High quality calculations will save and restore mark state, 
// but this is not always the case.
// This state is not saved in archives.
#define RUNTIME_MARK_BIT (0x20U)


#define DELETED_BIT    (0x40U)

#define DAMAGED_BIT    (0x80U)

// Do NOT include RUNTIME_MARK_BIT in ALL_MASK
#define ALL_MASK (SELECTED_MASK|HIGHLIGHTED_BIT|LOCKED_BIT|HIDDEN_BIT|DELETED_BIT|DAMAGED_BIT)

ON_ComponentState ON_ComponentStateFromUnsigned(
  unsigned int state_as_unsigned
  )
{
  switch (state_as_unsigned)
  {
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Unset);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Clear);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotSelected);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Selected);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::SelectedPersistent);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotHighlighted);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Highlighted);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotHidden);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Hidden);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotLocked);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Locked);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotDamaged);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Damaged);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotDeleted);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Deleted);
  }

  return ON_ComponentState::Unset;
}


//enum STATE_FILTER : unsigned int
//{
//  selected = 0,
//  highlighted = 1,
//  hidden = 2,
//  locked = 4,
//  damaged = 8
//};

ON_ComponentStatus::ON_ComponentStatus(
  ON_ComponentState state
  )
{
  switch ( state)
  {
  case ON_ComponentState::Selected:
    m_status_flags = SELECTED_BIT;
    break;
  case ON_ComponentState::SelectedPersistent:
    m_status_flags = SELECTED_BIT | SELECTED_PERSISTENT_BIT;
    break;
  case ON_ComponentState::Highlighted:
    m_status_flags = HIGHLIGHTED_BIT;
    break;
  case ON_ComponentState::Hidden:
    m_status_flags = HIDDEN_BIT;
    break;
  case ON_ComponentState::Locked:
    m_status_flags = LOCKED_BIT;
    break;
  case ON_ComponentState::RuntimeMarkSet:
    m_status_flags = RUNTIME_MARK_BIT;
    break;
  case ON_ComponentState::Deleted:
    m_status_flags = DELETED_BIT;
    break;
  case ON_ComponentState::Damaged:
    m_status_flags = DAMAGED_BIT;
    break;
  default:
    m_status_flags = 0U;
    break;
  }
}

bool ON_ComponentStatus::operator==(ON_ComponentStatus b)
{
  return (m_status_flags&ALL_MASK) == (b.m_status_flags&ALL_MASK);
}

bool ON_ComponentStatus::operator!=(ON_ComponentStatus b)
{
  return (m_status_flags&ALL_MASK) != (b.m_status_flags&ALL_MASK);
}

const ON_ComponentStatus ON_ComponentStatus::LogicalAnd(ON_ComponentStatus lhs, ON_ComponentStatus rhs)
{
  ON_ComponentStatus x;
  x.m_status_flags = (lhs.m_status_flags & rhs.m_status_flags);
  return x;
}

const ON_ComponentStatus ON_ComponentStatus::LogicalOr(ON_ComponentStatus lhs, ON_ComponentStatus rhs)
{
  ON_ComponentStatus x;
  x.m_status_flags = (lhs.m_status_flags | rhs.m_status_flags);
  return x;
}

bool ON_ComponentStatus::StatusCheck(
  ON_ComponentStatus candidate,
  ON_ComponentStatus status_pass,
  ON_ComponentStatus status_fail
)
{
  if (0 != ON_ComponentStatus::LogicalAnd(candidate, status_pass).m_status_flags)
    return true;
  if (0 != ON_ComponentStatus::LogicalAnd(candidate, status_fail).m_status_flags)
    return false;
  if (0 == status_fail.m_status_flags)
    return true;
  if (0 != status_pass.m_status_flags)
    return false;
  return true;
}

bool ON_ComponentStatus::IsClear() const
{
  return (0 == (m_status_flags&ALL_MASK));
}

bool ON_ComponentStatus::IsNotClear() const
{
  return (0 != (m_status_flags&ALL_MASK));
}

unsigned int ON_ComponentStatus::SetStatus(
  ON_ComponentStatus status_to_copy
  )
{
  unsigned char s1 = (status_to_copy.m_status_flags&ALL_MASK);
  if ( 0 == (SELECTED_BIT & s1) )
    s1 &= ~SELECTED_PERSISTENT_BIT;
  if (s1 != (m_status_flags&ALL_MASK))
  {
    const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT);
    m_status_flags = (s1|mark);
    return 1;
  }
  return 0;
}

unsigned int ON_ComponentStatus::SetStates(
  ON_ComponentStatus states_to_set
  )
{
  unsigned char s1 = (m_status_flags&ALL_MASK);
  unsigned char mask = (ALL_MASK & states_to_set.m_status_flags);

  if (0 == (SELECTED_BIT & mask))
  {
    // no changes to s1's selected status
    mask &= ~SELECTED_PERSISTENT_BIT;
  }
  else
  {
    // states_to_set specifies setting selected or selected_persistent.
    //
    // Clear the persistent bit on s1.
    // If s1's SELECTED_PERSISTENT_BIT it is supposed to remain set, 
    // then mask's SELECTED_PERSISTENT_BIT is set and the 
    // s1 bit will get set by the s1 |= mask line below.
    s1 &= ~SELECTED_PERSISTENT_BIT;
  }

  s1 |= mask;
  if (s1 != (m_status_flags&ALL_MASK))
  {
    const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT);
    m_status_flags = (s1|mark);
    return 1;
  }
  return 0;
}

unsigned int ON_ComponentStatus::ClearStates(
  ON_ComponentStatus states_to_clear
  )
{
  unsigned char s1 = (m_status_flags&ALL_MASK);

  unsigned char mask = ~(ALL_MASK & states_to_clear.m_status_flags);

  switch (SELECTED_MASK & mask)
  {
  case 0: 
    // states_to_clear.IsSelectedPersistent() is true.
    // This means all selection states must be cleared from s1.
    // We are in this case because 0 = (SELECTED_MASK & mask)
    // so the s1 &= mask line below will remove any set selection
    // states from s1.
    break;

  case SELECTED_PERSISTENT_BIT:
    // states_to_clear.IsSelected() is true.
    // states_to_clear.IsSelectedPersistent() is false.
    // That is SELECTED_BIT = (SELECTED_MASK & states_to_clear.m_status_flags)
    // If s1 is selected persistent, it must stay selected.
    // If s1 is selected but not persistent, its selection state must be cleared.
    if (SELECTED_MASK == (SELECTED_MASK & s1))
    {
      // s1 is selected persistent and must stay that way
      mask |= SELECTED_MASK;
    }
    else
    {
      // s1 is not selected persistent and must end up not selected.
      mask &= ~SELECTED_MASK;
    }
    break;

  default:
    // states_to_clear.IsSelected() is false.
    // No changes to s1's selection state.
    mask |= SELECTED_MASK;
  }

  s1 &= mask;
  if (s1 != (m_status_flags & ALL_MASK))
  {
    // If input was selected and highlighted,
    // and output is not selected,
    // and hightlight and not explictily cleared,
    // then preserve highlight sync by auto-clearing highlight.
    if ( 0 == (SELECTED_MASK & s1)
      && 0 != (SELECTED_MASK & m_status_flags)
      && 0 != (HIGHLIGHTED_BIT & m_status_flags)
      && 0 != (HIGHLIGHTED_BIT & s1)
      )
    { 
      // Input was selected and hightlighted,
      // output is not selected.
      // Clear highlight automatically.
      s1 &= ~HIGHLIGHTED_BIT;
    }

    const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT);
    m_status_flags = (s1|mark);
    return 1;
  }

  return 0;
}


ON_ComponentState ON_ComponentStatus::SelectedState() const
{
  switch ((m_status_flags & SELECTED_MASK))
  {
  case 0U:
    return ON_ComponentState::NotSelected;

  case SELECTED_BIT:
    return ON_ComponentState::Selected;

  case (SELECTED_BIT|SELECTED_PERSISTENT_BIT):
    return ON_ComponentState::SelectedPersistent;
  }

  // error
  return ON_ComponentState::NotSelected;
}

unsigned int ON_ComponentStatus::SetSelectedState(
    bool bSelectedState,
    bool bPersistent,
    bool bSynchronizeHighlight
  )
{
  if (bSelectedState)
  {
    return 
      bPersistent
      ? SetSelectedState(ON_ComponentState::SelectedPersistent, bSynchronizeHighlight)
      : SetSelectedState(ON_ComponentState::Selected, bSynchronizeHighlight)
      ;
  }
  return SetSelectedState(ON_ComponentState::NotSelected, bSynchronizeHighlight);
}

unsigned int ON_ComponentStatus::SetSelectedState(
  ON_ComponentState selected_state,
  bool bSynchronizeHighlight
  )
{
  bool bChanged = false;
  switch (selected_state)
  {
  case ON_ComponentState::NotSelected:
    if ( 0 != ClearStates(ON_ComponentStatus::Selected) )
      bChanged = true;
    if ( bSynchronizeHighlight && 0 != ClearStates(ON_ComponentStatus::Highlighted) )
      bChanged = true;
    break;

  case ON_ComponentState::Selected:
    if ( 0 != SetStates(ON_ComponentStatus::Selected) )
      bChanged = true;
    if ( bSynchronizeHighlight && 0 != SetStates(ON_ComponentStatus::Highlighted) )
      bChanged = true;
    break;

  case ON_ComponentState::SelectedPersistent:
    if ( 0 != SetStates(ON_ComponentStatus::SelectedPersistent) )
      bChanged = true;
    if ( bSynchronizeHighlight && 0 != SetStates(ON_ComponentStatus::Highlighted) )
      bChanged = true;
    break;
  }

  return bChanged ? 1U : 0U;
}

bool ON_ComponentStatus::IsSelected() const
{
  return 0 != (m_status_flags & SELECTED_BIT);
}

bool ON_ComponentStatus::IsSelectedPersistent() const
{
  return ( (SELECTED_BIT | SELECTED_PERSISTENT_BIT) == (m_status_flags & SELECTED_MASK) );
}

unsigned int ON_ComponentStatus::SetHighlightedState(
  bool bIsHighlighted
  )
{
  return bIsHighlighted ? SetStates(ON_ComponentStatus::Highlighted) : ClearStates(ON_ComponentStatus::Highlighted);
}

bool ON_ComponentStatus::IsHighlighted() const
{
  return 0 != (m_status_flags & HIGHLIGHTED_BIT);
}

bool ON_ComponentStatus::RuntimeMark() const
{
  return ( 0 != (m_status_flags & RUNTIME_MARK_BIT) );

}

unsigned int ON_ComponentStatus::SetRuntimeMark(
  bool bRuntimeMark
)
{
  return bRuntimeMark ? SetRuntimeMark() : ClearRuntimeMark();
}

unsigned int ON_ComponentStatus::SetRuntimeMark()
{
  const unsigned char c = (m_status_flags | RUNTIME_MARK_BIT);
  if (c != m_status_flags)
  {
    m_status_flags = c;
    return 1;
  }
  return 0;
}

unsigned int ON_ComponentStatus::ClearRuntimeMark()
{
  const unsigned char c = (m_status_flags & ~RUNTIME_MARK_BIT);
  if (c != m_status_flags)
  {
    m_status_flags = c;
    return 1;
  }
  return 0;
}


unsigned int ON_ComponentStatus::SetHiddenState(
  bool bIsHidden
  )
{
  return bIsHidden ? SetStates(ON_ComponentStatus::Hidden) : ClearStates(ON_ComponentStatus::Hidden);
}

bool ON_ComponentStatus::IsHidden() const
{
  return 0 != (m_status_flags & HIDDEN_BIT);
}

unsigned int ON_ComponentStatus::SetLockedState(
  bool bIsLocked
  )
{
  return bIsLocked ? SetStates(ON_ComponentStatus::Locked) : ClearStates(ON_ComponentStatus::Locked);
}

bool ON_ComponentStatus::IsLocked() const
{
  return 0 != (m_status_flags & LOCKED_BIT);
}

unsigned int ON_ComponentStatus::SetDeletedState(
  bool bIsDeleted
  )
{
  return bIsDeleted ? SetStates(ON_ComponentStatus::Deleted) : ClearStates(ON_ComponentStatus::Deleted);
}

bool ON_ComponentStatus::IsDeleted() const
{
  return 0 != (m_status_flags & DELETED_BIT);
}



unsigned int ON_ComponentStatus::SetDamagedState(
  bool bIsDamaged
  )
{
  return bIsDamaged ? SetStates(ON_ComponentStatus::Damaged) : ClearStates(ON_ComponentStatus::Damaged);
}

bool ON_ComponentStatus::IsDamaged() const
{
  return 0 != (m_status_flags & DAMAGED_BIT);
}

bool ON_ComponentStatus::operator==(const ON_ComponentStatus& other) const
{
  return m_status_flags == other.m_status_flags;
}

bool ON_ComponentStatus::operator!=(const ON_ComponentStatus& other) const
{
  return m_status_flags != other.m_status_flags;
}

bool ON_ComponentStatus::AllEqualStates(
  ON_ComponentStatus states_filter,
  ON_ComponentStatus comparand
  ) const
{
  unsigned char mask = (states_filter.m_status_flags & ALL_MASK);
  mask &= ~SELECTED_PERSISTENT_BIT;
  if (0 == mask)
    return false;
  
  unsigned char s1 = mask & m_status_flags;
  unsigned char s2 = mask & comparand.m_status_flags;

  return (s1 == s2);
}

bool ON_ComponentStatus::SomeEqualStates(
  ON_ComponentStatus states_filter,
  ON_ComponentStatus comparand
  ) const
{
  unsigned char mask = (states_filter.m_status_flags & ALL_MASK);
  mask &= ~SELECTED_PERSISTENT_BIT;
  if (0 == mask)
    return false;

  unsigned char s1 = mask & m_status_flags;
  unsigned char s2 = mask & comparand.m_status_flags;

  if (0 != (s1&s2))
    return true; // some set states are equal

  s1 = mask & ~s1;
  s2 = mask & ~s2;
  if (0 != (s1&s2))
    return true; // some clear states are equal

  return false;
}


bool ON_ComponentStatus::NoEqualStates(
  ON_ComponentStatus states_filter,
  ON_ComponentStatus comparand
  ) const
{
  unsigned char mask = (states_filter.m_status_flags & ALL_MASK);
  mask &= ~SELECTED_PERSISTENT_BIT;
  if (0 == mask)
    return false;

  unsigned char s1 = mask & m_status_flags;
  unsigned char s2 = mask & comparand.m_status_flags;

  return (mask == (s1 ^ s2));
}

bool ON_AggregateComponentStatus::ClearAllStates()
{
  if (m_current <= 1)
  {
    unsigned char c = m_current;
    unsigned int n = m_component_count;
    *this = ON_AggregateComponentStatus::Empty;
    m_current = c;
    n = m_component_count;
    return true;
  }
  return false;
}

bool ON_AggregateComponentStatus::ClearAggregateStatus(
  ON_ComponentStatus states_to_clear
  )
{
  if (states_to_clear.m_status_flags == ON_ComponentStatus::AllSet.m_status_flags || 0 == m_component_count)
    return ClearAllStates();

  if (1 == m_current)
  {
    m_aggregate_status.ClearStates(states_to_clear);
    const unsigned char s1 = m_aggregate_status.m_status_flags;
    if (0 == (SELECTED_BIT & s1))
    {
      m_selected_count = 0;
      m_selected_persistent_count = 0;
    }
    else if (0 == (SELECTED_PERSISTENT_BIT & s1))
    {
      m_selected_count -= m_selected_persistent_count;
      m_selected_persistent_count = 0;
    }
    if (0 == (HIGHLIGHTED_BIT & s1))
      m_highlighted_count = 0;
    if (0 == (LOCKED_BIT & s1))
      m_locked_count = 0;
    if (0 == (HIDDEN_BIT & s1))
      m_hidden_count = 0;
    if (0 == (DAMAGED_BIT & s1))
      m_damaged_count = 0;
    return true;
  }

  return false;
}

bool ON_AggregateComponentStatus::IsEmpty() const
{
  return (0 == m_current);
}
bool ON_AggregateComponentStatus::IsCurrent() const
{
  return (1 == m_current);
}

void ON_AggregateComponentStatus::MarkAsNotCurrent()
{
  if (2 != m_current)
  {
    *this = ON_AggregateComponentStatus::Empty;
    m_current = 2;
  }
}

ON_ComponentStatus ON_AggregateComponentStatus::AggregateStatus() const
{
  return m_aggregate_status;
}

unsigned int ON_AggregateComponentStatus::ComponentCount() const
{
  return m_component_count;
}

unsigned int ON_AggregateComponentStatus::SelectedCount() const
{
  return m_selected_count;
}

unsigned int ON_AggregateComponentStatus::SelectedPersistentCount() const
{
  return m_selected_persistent_count;
}

unsigned int ON_AggregateComponentStatus::HighlightedCount() const
{
  return m_highlighted_count;
}

unsigned int ON_AggregateComponentStatus::HiddenCount() const
{
  return m_hidden_count;
}

unsigned int ON_AggregateComponentStatus::LockedCount() const
{
  return m_locked_count;
}
unsigned int ON_AggregateComponentStatus::DamagedCount() const
{
  return m_locked_count;
}

bool ON_AggregateComponentStatus::Add(
  const ON_AggregateComponentStatus& aggregate_status
  )
{
  if (0 == m_current )
  {
    *this = aggregate_status;
    return m_current <= 1;
  }

  if ( m_current >= 2 )
    return false;

  if ( 0 == aggregate_status.m_current )
    return true;

  if ( aggregate_status.m_current >= 2 )
  {
    MarkAsNotCurrent();
    return false;
  }

  m_component_count += aggregate_status.m_component_count;
  
  const unsigned char s1 = aggregate_status.m_aggregate_status.m_status_flags;
  if ( 0 == s1 )
    return true;

  if (0 != (SELECTED_BIT & s1))
  {
    m_selected_count += aggregate_status.m_selected_count;
    m_selected_persistent_count += aggregate_status.m_selected_persistent_count;
  }
  if ( 0 != (HIGHLIGHTED_BIT & s1) )
    m_highlighted_count += aggregate_status.m_highlighted_count;
  if ( 0 != (LOCKED_BIT & s1) )
    m_locked_count += aggregate_status.m_locked_count;
  if ( 0 != (HIDDEN_BIT & s1) )
    m_hidden_count += aggregate_status.m_hidden_count;
  if ( 0 != (DAMAGED_BIT & s1) )
    m_damaged_count += aggregate_status.m_damaged_count;

  m_aggregate_status.m_status_flags |= s1;

  return true;
}

bool ON_AggregateComponentStatus::Add(
  ON_ComponentStatus component_status
  )
{
  if ( 0 == m_current )
    m_current = 1;
  else if ( 1 != m_current )
    return false;

  m_component_count++;

  const unsigned char s1 = component_status.m_status_flags;
  if ( 0 == s1 )
    return true;

  if (0 != (SELECTED_BIT & s1))
  {
    m_selected_count++;
    if (0 != (SELECTED_PERSISTENT_BIT & s1))
      m_selected_persistent_count++;
  }
  if ( 0 != (HIGHLIGHTED_BIT & s1) )
    m_highlighted_count++;
  if ( 0 != (LOCKED_BIT & s1) )
    m_locked_count++;
  if ( 0 != (HIDDEN_BIT & s1) )
    m_hidden_count++;
  if ( 0 != (DAMAGED_BIT & s1) )
    m_damaged_count++;

  m_aggregate_status.m_status_flags |= s1;

  return true;
}


/////////////////////////////////////////////////////////////////
//
// ON_Object component status virtual interface
//
//
unsigned int ON_Object::ClearAllComponentStates() const
{
  return ClearComponentStates(ON_ComponentStatus::AllSet);
}

// virtual
unsigned int ON_Object::ClearComponentStates(
  ON_ComponentStatus states_to_clear
  ) const
{
  return 0U;
}

//virtual
unsigned int ON_Object::GetComponentsWithSetStates(
  ON_ComponentStatus states_filter,
  bool bAllEqualStates,
  ON_SimpleArray< ON_COMPONENT_INDEX >& components
  ) const
{
  components.SetCount(0);
  return 0U;
}
  

// virtual
unsigned int ON_Object::SetComponentStates(
  ON_COMPONENT_INDEX component_index,
  ON_ComponentStatus states_to_set
  ) const
{
  return 0U;
}

// virtual
unsigned int ON_Object::ClearComponentStates(
  ON_COMPONENT_INDEX component_index,
  ON_ComponentStatus states_to_clear
  ) const
{
  return 0U;
}
  
// virtual
unsigned int ON_Object::SetComponentStatus(
  ON_COMPONENT_INDEX component_index,
  ON_ComponentStatus status_to_copy
  ) const
{
  return 0U;
}

// virtual
ON_AggregateComponentStatus ON_Object::AggregateComponentStatus() const
{
  return ON_AggregateComponentStatus::Empty;
}

// virtual
void ON_Object::MarkAggregateComponentStatusAsNotCurrent() const
{
  return;
}

// virtual
bool ON_Object::DeleteComponents(
  const ON_COMPONENT_INDEX* ci_list,
  size_t ci_count
  )
{
  return false;
}
