/* id.cc */

#include "root.h"
#include "component.h"
#include "signal_change.h"
#include "node.h"
#include "gui_hook.h"
#include "element.h"

int diff_lbl( cstring src_lbl, cstring other ) {
  int spos = ( src_lbl[0] == ID_SEPARATOR );
  int opos = ( other[0] == ID_SEPARATOR );

  int send = src_lbl.pos( ID_SEP_STRING, spos );
  int oend = other.pos( ID_SEP_STRING, opos );
  while( send >= 0 && oend >= 0 ) {
    if( src_lbl( spos, send - 1 ) != other( opos, oend - 1 ) ) return spos;
    
    spos = send + 1;
    opos = oend + 1;

    send = src_lbl.pos( ID_SEP_STRING, spos );
    oend = other.pos( ID_SEP_STRING, opos );
  }
  return spos;
}

dc_label::dc_label( void ) { 
  globalTag = tag_error;
  parentId = nil;
  temporary = true;
  localLabel = "";
  visible = true;
  //  connected = false;
  hook = nil;
  templated = nil;
}

dc_label::~dc_label( void ) {
  if( templated ) root.templated_list.erase( templated );

  /* used to identify first call to ~dc_label so that signal_death only called
     on first call rather than being called by all of a dc_node's childrens'
     destructors */
  static bool sigdel = true;
  bool lclsig = sigdel;

  dc_node *old_parent;
  string old_label;
  tag old_tag;
  if( !temporary ) {
    if( lclsig ) sigdel = false;
    old_parent = parentId;
    old_label = localLabel;
    old_tag = get_tag();
  }
  
  dc_trace( TRACE_MANY ) {
    if( temporary ) {
      cerr << "dc_label::~dc_label -- deleting temporary " << type_string() 
	   << "\n";
    } else {
      cerr << "dc_label::~dc_label -- deleting " << full_type() << "\n";
    }
  }
  
  /* remove from component and parent list */
  if( !temporary ) {
    if( old_parent )
      old_parent->remove_child( old_label );
    else 
      root.label_list.del( old_label );

    root.tag_list.del( globalTag );
    if( lclsig ) {
      sigdel = true;
      signal_death( old_tag, old_label, old_parent );
    }
  }
  if( hook ) delete( hook );
}

bool dc_label::set_label( cstring new_label ) {
  if( new_label.length() == 0 ) return true;

  if( new_label.pos( ID_SEPARATOR ) != -1 ) {
    dc_trace( TRACE_WARNING ) {
      cerr << "dc_label::set_label -- cannot set local label of " << full_type()
	   << " to " << new_label << " since delimeter '" << ID_SEPARATOR
	   << "' appears in new_label\n";
    }
    return true;
  }

  string old_label = localLabel;
  bool initializing = is_temporary();
  if( initializing ) {
    /* if temprary make permanent, allocate tag, and add to global lists */
    if( root.label_list.lookup( new_label ) != nil ) {
      dc_trace( TRACE_WARNING ) {
	cerr << "dc_label::set_label -- label \"" << new_label
	     << "\" already exists at root\n";
      }
      return true;
    }
    localLabel = new_label;
    root.label_list.insert( new_label, this );
    globalTag = new_tag();
    root.tag_list.insert( globalTag, this );
    temporary = false;
    // connected = true;
  } else {
    dictionary<string, dc_label *> *dic_to_change;
    if( parentId == nil ) {
      dic_to_change = &root.label_list;
    } else {
      dic_to_change = &( parentId->child_list );
    }

    /* check for label conflict */
    if( dic_to_change->lookup( new_label ) != nil ) {
      dc_trace( TRACE_WARNING ) {
	cerr << "dc_label::set_label -- label \"" << new_label
	     << "\" already exists or new_label == old_label\n";
      }
      return true;
    }
    
    //    if( connected ) {
    dic_item di = dic_to_change->lookup( old_label );
    if( di == nil ) {
      /* insert and warn if old copy not found */
      dc_trace( TRACE_WARNING ) {
	cerr << "dc_label::set_label -- old label \"" << localLabel
	     << "\" not found\n";
      }
      dic_to_change->insert( new_label, this );
    } else {
      dic_to_change->del_item( di );
      dic_to_change->insert( new_label, this );
    }
//     } else {
//       dic_to_change->insert( new_label, this );
//       root.tag_list.insert( globalTag, this );
//     }
    localLabel = new_label;
  }

  dc_trace( TRACE_ALL ) {
    cerr << "dc_label::set_label -- label set to \"" << localLabel << "\"\n";
  }

  signal_hchange( this, !initializing, old_label, false, nil, initializing );

  return false;
}
  
bool dc_label::set_parent( dc_node *new_parent ) {
  dc_node *old_parent = parentId;

  if( is_temporary() ) {
    dc_trace( TRACE_WARNING ) {
      cerr << "dc_label::set_parent -- cannot set parent of temporary\n";
    }
    return true;
  } 

  /* check for conflict */
  if( new_parent ) {
    if( new_parent->add_child( this ) ) {
      return true;
    }
    parentId = new_parent;
  } else {
    /* add to root.label_list */
    if( root.label_list.lookup( localLabel ) != nil ) {
      dc_trace( TRACE_WARNING ) {
	cerr << "dc_label::set_parent -- label \"" << localLabel 
	     << "\" already exists at root\n";
      }
      return true;
    }
    
    root.label_list.insert( localLabel, this );
    parentId = nil;
  }
  if( old_parent == nil ) {
    /* remove from root.label_list */
    root.label_list.del( localLabel );
  } else {
    /* remove from parent's child_list */
    old_parent->remove_child( localLabel );
  }
  
  dc_trace( TRACE_ALL ) {
    cerr << "dc_label::set_parent -- after parent set " << full_type() << "\n";
  }
  signal_hchange( this, false, "", true, old_parent, false );

  return false;
}

bool dc_label::set_both( cstring new_label, dc_node *new_parent ) {
  /* calling with args "", nil in a constructor means keep it temporary,
     but in all other cases null label is illegal */
  if( !new_label.length() )
    return ( !temporary || ( new_parent != nil ) );

  if( new_label.pos( ID_SEPARATOR ) != -1 ) {
    dc_trace( TRACE_WARNING ) {
      cerr << "dc_label::set_both -- cannot set local label of " << full_type()
	   << " to " << new_label << " since delimeter '" << ID_SEPARATOR
	   << "' appears in new_label\n";
    }
    return true;
  }
  
  /* if new_label exists at new_parent exit */
  if( new_parent == nil ) {
    if( root.label_list.lookup( new_label ) != nil ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "dc_label::set_both -- label \"" << new_label
	     << "\" already exists at root\n";
	return true;
      }
    }
  } else if( new_parent->child_exists( new_label ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "dc_label::set_both -- label \"" << new_label
	   << "\" already exists as child of " << new_parent->full_type() 
	   << "\n";
    }
    return true;
  }
  
  bool initializing = is_temporary();
  dc_node *old_parent = parentId;
  string old_label = localLabel;

  localLabel = new_label;
  /* set label and add to new parent */
  if( new_parent == nil ) {
    parentId = nil;
    root.label_list.insert( new_label, this );
  } else {
    if( new_parent->add_child( this ) ) { /* will set parentId */
      dc_trace( TRACE_WARNING ) {
	cerr << "dc_label::set_both -- could not add label \"" << new_label
	     << "\" to " << new_parent->full_type() << "\n";
      }
      return true;
    }
    parentId = new_parent;
  }

  if( initializing ) {
    /* assign tag and add to tag list */
    globalTag = new_tag();
    root.tag_list.insert( globalTag, this );
    temporary = false;
  } else {
    if( old_parent == nil ) { /* remove from root */
      root.label_list.del( old_label );
    } else { /* remove from parent */
      old_parent->remove_child( old_label );
    }
  }
  
  dc_trace( TRACE_ALL ) {
    cerr << "dc_label::set_both -- after parent set " << full_type() << "\n";
  }

  signal_hchange( this, !initializing, old_label, !initializing, old_parent,
		  initializing );

  return false;
}

inline void dc_label::set_visible( const bool v = true ) { 
  visible = v; signal_visible( this ); 
}

string dc_label::label( void ) const {
  return( parentId ? ( parentId->label() + ID_SEP_STRING + localLabel ) : 
	  localLabel );
}

string dc_label::full_type( void ) const {
  if( this == nil ) return "";
  if( temporary ) return type_string();

  string ft = type_string() + ":\"" + label() + "\"";
  switch( sub_type() ) {
  case Component_t : case Modification_t : case GA_t :
    ft += ( ( dc_component * )this )->buffer_info().to_string();
    break;
  case Element_t :
    ft += ( ( dc_element * )this )->buffer_info().to_string();
    break;
  default :;
  }
  return ft;
}

void dc_label::set_templated( bool ti ) {
  if( ti ) {
    if( templated == nil ) {
      templated = root.templated_list.push( this );
    }
  } else {
    if( templated ) {
      root.templated_list.erase( templated );
      templated = nil;
    }
  }
}

string dc_label::parent_label( void ) const {
  if( parentId == nil ) {
    return ID_SEP_STRING;
  } else {
    return parentId->label();
  }
}

ostream &dc_label::display( ostream &stream = cout ) {
  return stream << full_type();
}
