/* set.cc */

#include "set.h"
#include "id_lookup.h"
#include "root.h"
#include "pattern_match.h"
#include "display.h"

void dc_all_set::exec( void op( dc_label * ) ) {
  dic_item di;
  forall_items( di, root.tag_list ) {
    dc_label *id = root.tag_list.inf( di );
    op( id );
  }
}

void dc_all_set::exec( void op( dc_label *, void * ), void *vp ) {
  dic_item di;
  forall_items( di, root.tag_list ) {
    dc_label *id = root.tag_list.inf( di );
    op( id, vp );
  }
}

void dc_finite_set::exec( void op( dc_label * ) ) {
  tag T;
  forall( T, S ) {
    op( lookup( T ) );
  }
}

void dc_finite_set::exec( void op( dc_label *, void * ), void *vp ) {
  tag T;
  forall( T, S ) {
    op( lookup( T ), vp );
  }
}

dc_set_atom *dc_finite_set::duplicate( dc_label * ) {
  dc_finite_set *fset = new dc_finite_set;
  fset->S = S;
  return ( dc_set_atom * )fset;
}

inline void add_to_set( dc_label *id, void *s ) {
  dc_trace( TRACE_MANY ) {
    cerr << "adding to set -- " << id->label() << "\n";
  }
  ( ( dc_desc_set * )s )->pr_add( *id );
}

dc_desc_set::dc_desc_set( const string &K, user_type U, cstring o_str, 
			  dc_label *o ) {
  origin = o;
  origin_str = o_str;
  desc_origin = nil;
  u_type = U;
  key = K;
}

bool desc_crit( dc_label *id, void *s ) {
  return pattern_match( id->local_label(), ( ( dc_desc_set * )s )->key );
}

bool all_crit( dc_label *, void * ) {
  return true;
}

int dc_desc_set::rehash( void ) {
  if( ( desc_origin = search( origin_str, origin ) ) == nil 
      && origin_str != ID_SEP_STRING ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "dc_desc_set::rehash -- error finding origin \"" << origin_str
	   << "\"\n";
    }
    return 1;
  }

  pr_clear();
  if( u_type != Undefined_Type )
    if( !key.length() ) 
      for_type_match( all_crit, desc_origin, u_type, add_to_set, ( void * )this, false );
    else 
      for_type_match( desc_crit, desc_origin, u_type, add_to_set, ( void * )this, false );
  else
    if( !key.length() )
      for_desc_match( all_crit, desc_origin, add_to_set, ( void * )this, false );
    else
      for_desc_match( desc_crit, desc_origin, add_to_set, ( void * )this, false );
 
  /* if none get default */
  if( !size() && key.length() ) {
    dc_node *default_origin = ( dc_node * ) ( ( origin == nil ) ? nil :
      ( ( origin->type() == Node_t ) ? origin : origin->get_parent() ) );
    dc_default *deflt = get_default( key, default_origin );
    
    if( deflt ) {
      pr_add( *deflt );
    }
  }
  return 0;
}

dc_set_atom *dc_desc_set::duplicate( dc_label *origin ) {
  dc_desc_set *dset = new dc_desc_set( key, u_type, origin_str, origin );
  dset->origin_str = origin_str;

  return ( dc_set_atom * )dset;
}

int dc_label_set::rehash( void ) {
  int nerrors = 0;
  string label;
  forall( label, lbl_list ) {
    dc_label *id = search( label, origin );
    if( id ) {
      pr_add( *id );
    } else {
      dc_trace( TRACE_ERROR ) {
	cerr << "dc_label_set::rehash -- failed to locate \"" << label 
	     << "\"\n";
      }
      nerrors++;
    }
  }
return nerrors;
}

dc_set_atom *dc_label_set::duplicate( dc_label *o ) {
  dc_label_set *dupe = new dc_label_set;
  dupe->origin = o;
  dupe->lbl_list = lbl_list;
  return ( dc_set_atom * )dupe;
}

/* dc_set members */
static list<dc_set *> set_list;

dc_set::dc_set( void ) {
  global_loc = set_list.append( this );
}

dc_set::dc_set( cstring label, dc_node *parent ) {
  global_loc = set_list.append( this );
  set_both( label, parent );
}

dc_set::~dc_set( void ) {
  clear();

  set_list.del_item( global_loc );
}

bool dc_set::remove_set( int n ) {
  if( n >=0 && n < sets.size() ) {
    sets.del_item( sets[n] );
    return false;
  }
  return true;
}

bool dc_set::remove_exception( int n ) {
  if( n >=0 && n < exceptions.size() ) {
    exceptions.del_item( exceptions[n] );
    return false;
  }
  return true;
}

void dc_set::clear( void ) {
  sets.clear();
  exceptions.clear();
  members.clear();
}

int dc_set::rehash( void ) {
  int nerrors = 0;
  members.clear();
  
  dc_finite_set *fset;
  forall( fset, sets ) {
    nerrors += fset->rehash();
    members += *fset;
  }

  set<tag> all_exceptions;
  forall( fset, exceptions ) {
    nerrors += fset->rehash();
    all_exceptions += *fset;
  }

  members -= all_exceptions;
  return nerrors;
}

void dc_set::forall_members( void op( dc_label * ) ) {
  tag T;
  forall( T, members ) {
    dc_label *id = lookup( T );
    op( id );
  }
}

void dc_set::forall_members( void op( dc_label *, void * ), void *vp ) {
  tag T;
  forall( T, members ) {
    dc_label *id = lookup( T );
    if( id ) op( id, vp );
  }
}

dc_set &dc_set::operator=( const dc_set &S ) {
  sets = S.sets;
  exceptions = S.exceptions;
  members = S.members;
  return *this;
}

bool dc_set::operator==( const dc_set &S ) {
  return members == S.members;
}

bool dc_set::operator!=( const dc_set &S ) {
  return members != S.members;
}

bool dc_set::operator<=( const dc_set &S ) {
  return members <= S.members;
}

bool dc_set::operator>=( const dc_set &S ) {
  return members >= S.members;
}

bool dc_set::operator<( const dc_set &S ) {
  return members < S.members;
}

bool dc_set::operator>( const dc_set &S ) {
  return members > S.members;
}

dc_label *dc_set::duplicate( dc_node *parent ) const {
  dc_set *dupe = new dc_set;

  if( !is_temporary() ) {
    if( dupe->set_both( local_label(), parent ) ) {
      delete( dupe );
      return nil;
    }
  }

  dupe->set_visible( is_visible() );

  dc_finite_set *fset;
  forall( fset, sets ) {
    dc_finite_set *dupe_set = ( dc_finite_set * )fset->duplicate( parent );
    if( dupe_set == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "dc_set::duplicate -- failed to duplicate part of set\n";
      }
      delete( dupe );
      return nil;
    }
    dupe->sets.append( dupe_set );
  }

  forall( fset, exceptions ) {
    dc_finite_set *dupe_set = ( dc_finite_set * )fset->duplicate( parent );
    if( dupe_set == nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "dc_set::duplicate -- failed to duplicate part of exception\n";
      }
      delete( dupe );
      return nil;
    }
    dupe->exceptions.append( dupe_set );
  }

  return ( dc_label * )dupe;
}

int rehash_sets( void ) {
  int nerrors = 0;

  dc_set *s;
  forall( s, set_list ) {
    nerrors += s->rehash();
  }

  return nerrors;
}
