// file: $isip/class/mmedia/AnnotationGraph/angr_5.cc
// version: $Id: angr_05.cc 9136 2003-05-12 22:41:59Z huang $
//

// isip include files
//
#include "AnnotationGraph.h"

// method: clear
//
// arguments:
//   Integral::CMODE cmode: (input) clear mode
//
// return: logical error status
//
// this method clears the content of the current object
//
boolean AnnotationGraph::clear(Integral::CMODE cmode_a) {

  // declare local variables
  //
  boolean status = false;
  DoubleLinkedList<Anchor> ancrs(DstrBase::USER);
  DoubleLinkedList<Annotation> annos(DstrBase::USER);
  
  // delete the sequence of annotations
  //
  if (getAnnotationSet(annos)) {
    for (boolean more = annos.gotoFirst(); more; more = annos.gotoNext()) {
      deleteAnnotation(annos.getCurr());
    }
  }
  
  // delete the set of anchors
  //
  if (getAnchorSet(ancrs)) {
    for (boolean more = ancrs.gotoFirst(); more; more = ancrs.gotoNext()) {
      deleteAnchor(ancrs.getCurr());
    }
  }
  
  // clear the member data
  //
  status = id_d.clear(cmode_a);
  status &= type_d.clear(cmode_a);
  status &= index_d.clear(cmode_a);
  status &= annoseq_d.clear(cmode_a);
  
  // delete the identifiers
  //
  if (anchorids_d != (Identifier*)NULL) {
    delete anchorids_d;
    anchorids_d = (Identifier*)NULL;
  }
  if (annotationids_d != (Identifier*)NULL) {
    delete annotationids_d;
    annotationids_d = (Identifier*)NULL;
  }
   
  // return the logical status
  //
  return status;
}

// method: sofSize
//
// arguments: none
//
// return: logical error status
//
// method gets the size of the graph object
//
long AnnotationGraph::sofSize() const {

  // declare local variables
  //
  DoubleLinkedList<Pair<Long, Anchor> > ancrs;
  DoubleLinkedList<Triple<Long, Long, Annotation> > annos;

  // get the annotations and anchors associated with the graph
  //
  const_cast<AnnotationGraph*>(this)->get(ancrs, annos);

  // return the sof size
  //
  return ancrs.sofSize() + annos.sofSize() + id_d.sofSize() + type_d.sofSize();
}
			     
// method: get
//
// arguments:
//   DoubleLinkedList<Pair<Long, Anchor> >& ancrs: (output) list of anchors
//   DoubleLinkedList<Triple<Long, Long, Annotation> >& annos: (output) list
//   of annotations
//
// return: logical error status
//
// method get the annotations and anchors associated with the graph
//
boolean AnnotationGraph::get(DoubleLinkedList<Pair<Long, Anchor> >& ancrs_a,
		 DoubleLinkedList<Triple<Long, Long, Annotation> >& annos_a) {

  // declare local variables
  //
  Pair<Long, Anchor> ancr_pair;
  Triple<Long, Long, Annotation> anno_triple;
  DoubleLinkedList<Anchor> ancrs(DstrBase::USER);
  DoubleLinkedList<Annotation> annos(DstrBase::USER);
  
  // get the list of anchors
  //
  getAnchorSet(ancrs);
  
  // get the list of annotations
  //
  getAnnotationSeqByOffset(annos, 0.0, 0.0);

  // create a list of annotations and anchors that contain cross indexing
  // information in order for the annotations to reference the anchors
  //
  for (boolean more = ancrs.gotoFirst(); more; more = ancrs.gotoNext()) {

    // set the index of the anchor
    //
    ancr_pair.first().assign(ancrs.getPosition());

    // set the anchor object
    //
    ancr_pair.second().assign(*ancrs.getCurr());

    // inset the anchor pair into the list
    //
    ancrs_a.insert(&ancr_pair);
  }

  for (boolean more = annos.gotoFirst(); more; more = annos.gotoNext()) {

    // set the start index of the annotation
    //
    anno_triple.first().assign(DEF_INDEX);
    if (ancrs.find(annos.getCurr()->getStartAnchor())) {
      anno_triple.first().assign(ancrs.getPosition());
    }

    // set the end index of the annotation
    //    
    anno_triple.second().assign(DEF_INDEX);
    if (ancrs.find(annos.getCurr()->getEndAnchor())) {
      anno_triple.second().assign(ancrs.getPosition());
    }

    // set the annotation object
    //
    anno_triple.third().assign(*annos.getCurr());

    // insert the annotation pair into the list
    //
    annos_a.insert(&anno_triple);
  }

  // exit gracefully
  //
  return true;
}

// method: set
//
// arguments:
//   DoubleLinkedList<Pair<Long, Anchor> >& ancrs: (input) list of anchors
//   DoubleLinkedList<Triple<Long, Long, Annotation> >& annos: (input) list
//   of annotations
//
// return: logical error status
//
// method get the annotations and anchors associated with the graph
//
boolean AnnotationGraph::set(DoubleLinkedList<Pair<Long, Anchor> >& ancrs_a,
		 DoubleLinkedList<Triple<Long, Long, Annotation> >& annos_a) {

  // declare local variables
  //
  long index;
  long start_index;
  long end_index;  

  String tmpstr;
  Anchor anchor;
  String anchorid;
  Annotation annotation;
  Annotation* annotation1;  
  String annotationid;
  Vector<String> keys;
  
  long num_anchors = ancrs_a.length();
  Anchor* start_anchor = (Anchor*)NULL;
  Anchor* end_anchor = (Anchor*)NULL;
  Anchor* anchors[num_anchors];
  
  // insert the anchors into the graph
  //
  for (boolean more=ancrs_a.gotoFirst(); more; more=ancrs_a.gotoNext()) {

    // get the anchor parameters
    //
    index = ancrs_a.getCurr()->first();
    anchor = ancrs_a.getCurr()->second();
    tmpstr = anchor.getUnit();

    // create the anchor object using the parameters
    //
    if (anchor.getAnchored()) {
      anchorid = createAnchor(id_d, anchor.getOffset(), tmpstr);
    }
    else {
      anchorid = createAnchor(id_d, tmpstr);
    }
    
    anchors[index] = getAnchorById(anchorid);
  }

  // insert the annotations into the graph
  //
  for (boolean more=annos_a.gotoFirst(); more; more=annos_a.gotoNext()) {

    // get the annotation parameters
    //
    start_anchor = (Anchor*)NULL;
    end_anchor = (Anchor*)NULL;
    start_index = annos_a.getCurr()->first();
    end_index = annos_a.getCurr()->second();
    annotation = annos_a.getCurr()->third();
    tmpstr = annotation.getType();

    // get the start and end anchors
    //
    if (start_index != DEF_INDEX) {
      start_anchor = anchors[start_index];
    }
    if (end_index != DEF_INDEX) {
      end_anchor = anchors[end_index];
    }

    long channel_index = annotation.getChannel();
    
    // create the annotation object using the parameters
    //
    annotationid.assign(annotation.getId());
    annotationid = createAnnotation(id_d, start_anchor,
				    end_anchor, tmpstr, channel_index);
    annotation1 = getById(annotationid);
    
    // set the feature map for the annotation
    //
    annotation1->setFeatureMap(annotation.getFeatureMap());

    // get the features corresponding to the annotation
    //
    annotation.getFeatureNames(keys);

    // add the features to the annotation index
    //
    for (long i=0; i<keys.length(); i++) {
      tmpstr.assign(annotation.getFeature(keys(i)));
      index_d.addFeature(annotation1, keys(i), tmpstr);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: eq
//
// arguments:
//   const AnnotationGraph& arg: (input) annotation graph
//
// return: logical error status
//
// method determines if the input annotation graph is equal to the current
//
boolean AnnotationGraph::eq(const AnnotationGraph& arg_a) const {

  // declare local variables
  //
  boolean status = false;
  DoubleLinkedList<Pair<Long, Anchor> > ancrs;
  DoubleLinkedList<Triple<Long, Long, Annotation> > annos;
  DoubleLinkedList<Pair<Long, Anchor> > ancrs1;
  DoubleLinkedList<Triple<Long, Long, Annotation> > annos1;
  
  // get the structure of the current graph
  //
  const_cast<AnnotationGraph*>(this)->get(ancrs, annos);
  
  // get the structure of the input graph
  //
  const_cast<AnnotationGraph&>(arg_a).get(ancrs1, annos1);

  // determine if the two graph structures are equal
  //
  status = ancrs.eq(ancrs1);
  status &= annos.eq(annos1);

  // return the logical status
  //
  return status;
}

// method: assign
//
// arguments:
//   const AnnotationGraph& arg: (input) annotation graph
//
// return: logical error status
//
// method assigns the input annotation graph to the current one
//
boolean AnnotationGraph::assign(const AnnotationGraph& arg_a) {

  // declare local variables
  //
  boolean status = false;

  String anchor(DEF_ANCHOR);
  String annotation(DEF_ANNOTATION);
  
  DoubleLinkedList<Pair<Long, Anchor> > ancrs;  
  DoubleLinkedList<Triple<Long, Long, Annotation> > annos;
  
  // clear the current contents of the graph
  //
  this->clear();
  
  // assign the member data
  //
  status = id_d.assign(arg_a.id_d);
  status &= type_d.assign(arg_a.type_d);
  
  // initialize the identifiers if needed
  //
  if (anchorids_d == (Identifier*)NULL) {
    anchorids_d = new Identifier(id_d, anchor);
  }
  if (annotationids_d == (Identifier*)NULL) {
    annotationids_d = new Identifier(id_d, annotation);    
  }  

  // get the annotations and anchors associated with the input graph
  //
  status &= const_cast<AnnotationGraph&>(arg_a).get(ancrs, annos);
  
  // initialize the annotations and anchors
  //
  status &= set(ancrs, annos);
  
  // return the logical status
  //
  return status;  
}
				    
// method: setFeature
//
// arguments:
//   String& id_a: (input) annotation id
//   String& fname_a: (input) feature name
//   String& fvalue_a: (input) feature value
//
// return: logical error status
//
// method sets the feature value of the annotation
//
boolean AnnotationGraph::setFeature(String& id_a, String& fname_a,
				    String& fvalue_a) {

  // insert the feature value pair to the feature map of the annotation
  //
  return setFeature(getById(id_a), fname_a, fvalue_a);
}

// method: existsFeature
//
// arguments:
//   String& id_a: (input) annotation id
//   String& fname_a: (input) feature name
//
// return: logical error status
//
// method to test if a feature exists in the annotation
//
boolean AnnotationGraph::existsFeature(String& id_a, String& fname_a) {

  // declare local variables
  //
  boolean status = false;
  Annotation* anno = (Annotation*)NULL;

  // get the annotation corresponding to the id
  //
  anno = getById(id_a);
  
  // determine if the feature name exists
  //
  if (anno != (Annotation*)NULL) {
    status = anno->existsFeature(fname_a);
  }

  // return the logical status
  //
  return status;
}

// method: deleteFeature
//
// arguments:
//   String& id_a: (input) annotation id
//   String& fname_a: (input) feature name
//
// return: logical error status
//
// method that deletes the specified feature from the annotation
//
boolean AnnotationGraph::deleteFeature(String& id_a, String& fname_a) {

  // declare local variables
  //
  boolean status = false;  
  Annotation* anno = (Annotation*)NULL;

  // get the annotation corresponding to the id
  //
  anno = getById(id_a);
  
  // delete the feature from the indeces
  //
  if (anno != (Annotation*)NULL) {
    status = index_d.deleteFeature(anno, fname_a);
    status &= anno->deleteFeature(fname_a);
  }
  
  // return the logical status
  //
  return status;  
}

// method: getFeature
//
// arguments:
//   String& id_a: (input) annotation id
//   String& fname_a: (input) feature name
//
// return: value of the corresponding feature name
//
// method that gets the value of specified feature in the annotation
//
String AnnotationGraph::getFeature(String& id_a, String& fname_a) {

  // declare local variables
  //
  String fvalue;
  Annotation* anno = (Annotation*)NULL;

  // get the annotation corresponding to the id
  //
  anno = getById(id_a);

  // retrieve the feature value
  //
  if (anno != (Annotation*)NULL) {
    fvalue.assign(anno->getFeature(fname_a));
  }

  // return the feature value
  //
  return fvalue;
}

// method: getFeatureNames
//
// arguments:
//   String& id_a: (input) annotation id
//   Vector<String>& features: (output) vector of feature names
//
// return: logical error status
//
// method that gets the value of specified feature in the annotation
//
boolean AnnotationGraph::getFeatureNames(String& id_a,
					 Vector<String>& features_a) {

  // declare local variables
  //
  boolean status = false;  
  Annotation* anno = (Annotation*)NULL;

  // get the annotation corresponding to the id
  //
  anno = getById(id_a);

  // retrieve the feature names
  //
  if (anno != (Annotation*)NULL) {
    status = anno->getFeatureNames(features_a);
  }
    
  // return the logical status
  //
  return status;
}

// method: unsetFeatures
//
// arguments:
//   String& id_a: (input) annotation id
//
// return: logical error status
//
// method to unset all features in the annotation
//
boolean AnnotationGraph::unsetFeatures(String& id_a) {

  // declare local variables
  //
  String null;
  Vector<String> names;
  Annotation* anno = (Annotation*)NULL;

  // get the annotation corresponding to the id
  //
  anno = getById(id_a);
  
  // get the set of feature names
  //
  if (anno != (Annotation*)NULL) {
    if (!anno->getFeatureNames(names)) {
      return Error::handle(name(), L"getFeature", Error::ARG, __FILE__, __LINE__);
    }
  }

  // loop over all feature names and unset their values
  //
  null.assign(String::EMPTY);
  for (int i=0; i < names.length(); i++) {
    setFeature(anno, names(i), null);
  }

  // exit gracefully
  //
  return true;  
}

// method: setFeature
//
// arguments:
//   Annotation* anno: (input) annotation
//   const String& fname_a: (input) feature name
//   const String& fvalue_a: (input) feature value
//
// return: logical error status
//
// method to set the specified feature of the annotation to this value
//
boolean AnnotationGraph::setFeature(Annotation* anno_a, String& fname_a,
				    String& fvalue_a) {

  // declare local variables
  //
  boolean status = false;
  
  // delete the previous feature from the annotation indices
  //
  status = index_d.deleteFeature(anno_a, fname_a);
  if (anno_a->existsFeature(fname_a)) {
    status &= anno_a->deleteFeature(fname_a);
  }
  
  // insert the feature value pair into the indices
  //
  status &= anno_a->setFeature(fname_a, fvalue_a);
  status &= index_d.addFeature(anno_a, fname_a, fvalue_a);
  
  // return the logical status
  //
  return status;
}

// method: getAnnotationSetByFeature
//
// arguments:
//   const String& fname_a: (input) feature name
//   const String& fvalue_a: (input) feature value
//   DoubleLinkedList<Annotation>& annos_a: (input) annotation list
//
// return: logical error status
//
// method to set the specified feature of the annotation to this value
//
boolean AnnotationGraph::getAnnotationSetByFeature(const String& fname_a,
				       const String& fvalue_a,
				       DoubleLinkedList<Annotation>& annos_a) {

  // declare local variables
  //
  String index;
  
  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);

  // generate the hash index
  //
  index.assign(fname_a);
  index.concat(fvalue_a);
  
  // get the list of annotations corresponding to the feature-value pair
  //
  if (index_d.by_feature_d.get(index) != (DoubleLinkedList<Annotation>*)NULL) {
    annos_a.assign(*index_d.by_feature_d.get(index));
  }

  // return error
  //
  else {
    return Error::handle(name(), L"null pointer - no annotation for this feature and the value", Error::ARG, __FILE__, __LINE__);
  }    
  
  // exit gracefully
  //
  return true;  
}

// method: addAnchor
//
// arguments:
//   Anchor* ancr: (input) anchor to be added
//
// return: logical error status
//
// method to add an anchor to the graph
//
boolean AnnotationGraph::addAnchor(Anchor* ancr_a) {


  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // insert the anchor id and anchor pair to the anchor mapping
  //
  anchorids_d->addAnchorRef(ancr_a->getId(), ancr_a);
  
  // insert the anchor to the anchor set
  //
  if (!index_d.ancrset_d.contains(ancr_a)) {
    index_d.addAnchor(ancr_a);
  }
    
  // exit gracefully
  //
  return true;
}

// method: deleteAnchor
//
// arguments:
//   Anchor* ancr: (input) anchor to be deleted
//
// return: logical error status
//
// method to delete an anchor from the graph
//
boolean AnnotationGraph::deleteAnchor(Anchor* ancr_a) {

  // declare local variables
  //
  HashKey<Anchor> hashkey;


  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // make sure that the anchor has no incoming or outgoing arcs
  //
  hashkey.assign(ancr_a);
  if ((index_d.incoming_d.get(hashkey) ==
       (DoubleLinkedList<Annotation>*)NULL) &&
      (index_d.outgoing_d.get(hashkey) ==
       (DoubleLinkedList<Annotation>*)NULL)) {
    
    // remove the anchor for the identifier list
    //
    anchorids_d->deleteAnchorRef(ancr_a->getId());
    index_d.deleteAnchor(ancr_a);
    
    // reclaim the id from the anchor issuer
    //
    anchorids_d->reclaimId(ancr_a->getId());
    
    // delete the anchor
    //
    delete ancr_a;
    
    // exit gracefully
    //
    return true;
  }

  
  // exit gracefully
  //
  return false;
}

// method: setAnchorOffset
//
// arguments:
//   Anchor* ancr: (input) anchor whose offset is to be changed
//   float offset: (input) anchor offset
//
// return: logical error status
//
// method that sets an anchors offset to the specified value
//
boolean AnnotationGraph::setAnchorOffset(Anchor* ancr_a, float offset_a) {

  // delete anchor from indexes
  //
  index_d.deleteAnchor(ancr_a);

  // update the anchor
  //
  ancr_a->setOffset(offset_a);

  // add new anchor to indexes
  //
  index_d.addAnchor(ancr_a);      
  
  // exit gracefully
  //
  return true;  
}

// method: splitAnchor
//
// arguments:
//   Anchor* ancr: (input) anchor to be split
//
// return: the new anchor is returned
//
// method that splits an anchor a in two, creating a new anchor a'
// having the same offset as the original one. anchor a has all the
// incoming annotations, while anchor a' has all the outgoing annotations.
// the new anchor a' is returned.
//
Anchor* AnnotationGraph::splitAnchor(Anchor* ancr_a) {

  // declare local variables
  //
  String newid;
  Anchor* ancr = (Anchor*)NULL;
  DoubleLinkedList<Annotation> annos(DstrBase::USER);


  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // generate a new id for the anchor
  //
  newid.assign(anchorids_d->registerId());
  
  // clone the anchor
  //
  ancr = new Anchor(newid, ancr_a);  
  addAnchor(ancr);

  // for each outgoing arc from ancr_a, change its start node to ancr
  //
  getOutgoingAnnotationSet(ancr_a, annos);
  for (boolean more = annos.gotoFirst(); more; more = annos.gotoNext()) {
    setStartAnchor(annos.getCurr(), ancr);
  }
  
  // return the new anchor
  //
  return ancr;  
}

// method: add
//
// arguments:
//   Annotation* anno: (input) annotation to be added
//
// return: logical error status
//
// method that adds a new annotation to the graph
//
boolean AnnotationGraph::add(Annotation* anno_a) {


  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // add annotation to the annotation set
  //
  annoseq_d.insert(anno_a);

  // update index and the identifier issuer
  //
  index_d.add(anno_a);
  annotationids_d->addAnnotationRef(anno_a->getId(), anno_a);
  
  // exit gracefully
  //
  return true;  
}

// method: createAnnotation
//
// arguments:
//   String& id_a: (input) annotation id
//   Anchor* anchor1_a: (input) start anchor
//   Anchor* anchor2_a: (input) end anchor
//   String& type_a:  (input) annotation type
//   long channel; (input) channel
//
// return: annotation id
//
// method that create a new annotation
//
String AnnotationGraph::createAnnotation(String& id_a, Anchor* anchor1_a,
					 Anchor* anchor2_a, String& type_a,
					 long channel_a) {
  
  // declare local variables
  //
  String newid;
  Annotation* anno = (Annotation*)NULL;

  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  String nspace(annotationids_d->getNamespace(id_a));
  if (nspace.eq(String::EMPTY)) {
    nspace.assign(id_a);
  }
      
  // when the is the same as the graphs id
  //
  if (id_a.eq(id_a)) {
    newid.assign(annotationids_d->registerId());
    anno = new Annotation(newid, anchor1_a, anchor2_a, type_a, channel_a);
  }

  // when the namespace is the same as the graphs id
  //
  else if (nspace.eq(id_a)) {
    newid.assign(annotationids_d->registerId(id_a));
    anno = new Annotation(newid, anchor1_a, anchor2_a, type_a, channel_a);
  }

  // the id is not an annotation graph or a valid annotation id
  //
  else {
    return Error::handle(name(), L"createAnnotation", Error::ARG, __FILE__, __LINE__);

  }

  // add the annotation to the graph
  //
  add(anno);
  
  // return the annotation id
  //
  return anno->getId();
}

// method: createAnchor
//
// arguments:
//   String& id: (input) anchor id
//   float offset: (input) anchor offset
//   String& unit: (input) unit of the offset
//
// return: anchor id
//
// method that creates and anchor with specified offset and unit
//
String AnnotationGraph::createAnchor(String& id_a, String& unit_a) {

  // declare local variables
  //
  String newid;
  Anchor* ancr = (Anchor*)NULL;

  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  String nspace(anchorids_d->getNamespace(id_a));
  if (nspace.eq(String::EMPTY)) {
    nspace.assign(id_a);
  }
      
  // when the id is the same as the graphs id
  //
  if (id_a.eq(id_d)) {
    newid.assign(anchorids_d->registerId());
    ancr = new Anchor(newid, unit_a);
  }

  // when the name space is the same as the graphs id
  //
  else if (nspace.eq(id_a)) {
    newid.assign(anchorids_d->registerId(id_a));
    ancr = new Anchor(newid, unit_a);
  }
  
  // the id is not an annotation graph or a valid anchor id
  //
  else {
    return Error::handle(name(), L"createAnchor", Error::ARG, __FILE__, __LINE__);
  }

  // add the anchor to the graph
  //
  addAnchor(ancr);
  
  // return the anchor id
  //
  return ancr->getId();
}

// method: createAnchor
//
// arguments:
//   String& id: (input) anchor id
//   float offset: (input) anchor offset
//   String& unit: (input) unit of the offset
//
// return: anchor id
//
// method that creates and anchor with specified offset and unit
//
String AnnotationGraph::createAnchor(String& id_a, float offset_a,
				     String& unit_a) {

  // declare local variables
  //
  String newid;
  Anchor* ancr = (Anchor*)NULL;

  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  String nspace(anchorids_d->getNamespace(id_a));
  if (nspace.eq(String::EMPTY)) {
    nspace.assign(id_a);
  }
      
  // when the id is the same as the graphs id
  //
  if (id_a.eq(id_d)) {
    newid.assign(anchorids_d->registerId());
    ancr = new Anchor(newid, offset_a, unit_a);
  }

  // when the name space is the same as the graphs id
  //
  else if (nspace.eq(id_a)) {
    newid.assign(anchorids_d->registerId(id_a));
    ancr = new Anchor(newid, offset_a, unit_a);
  }
  
  // the id is not an annotation graph or a valid anchor id
  //
  else {
    return Error::handle(name(), L"createAnchor", Error::ARG, __FILE__, __LINE__);
  }

  // add the anchor to the graph
  //
  addAnchor(ancr);
  
  // return the anchor id
  //
  return ancr->getId();
}


// method: getAnchorOffset
//
// arguments:
//   Anchor* ancr: (input) graph anchor
//
// return: anchor offset
//
// method that returns the offset of the specified anchor
//
float AnnotationGraph::getAnchorOffset(Anchor* ancr_a) {

  // declare local variables
  //
  float offset = 0.0;

  // determine the offset of the anchor
  //
  if ((ancr_a != (Anchor*)NULL) && (ancr_a->getAnchored())) {
    offset = ancr_a->getOffset();
  }
  
  // return the offset
  //
  return offset;
}

// method: copyAnnotation
//
// arguments:
//   const Annotation* anno: (input) graph anchor
//
// return: annotation copy
//
// method that makes a copy of the specified annotation
//
Annotation* AnnotationGraph::copyAnnotation(const Annotation* anno_a) {

  // declare local variables
  //
  String newid;
  Annotation* annotation = (Annotation*)NULL;

  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // clone the annotation
  //
  newid.assign(annotationids_d->registerId());
  annotation = new Annotation(newid, anno_a);

  // add the cloned annotation to the graph
  //
  add(annotation);
  
  // return the annotation copy
  //
  return annotation;
}

// method: splitAnnotation
//
// arguments:
//   Annotation* anno: (input) annotation to split
//
// return: split annotation
//
// method that split an annotation a in two creating a new annotation a'
// having the same label data as the original one. the two annotations a,
// a' connect head-to-tail at a new anchor. the new annotation and anchor
// have identifiers taken from the specified identifier spaces. the new
// anchor is unanchored, i.e. has no offset.
//
Annotation* AnnotationGraph::splitAnnotation(Annotation* anno_a) {

  // dclare local variables
  //
  String newid;
  String newunit;  
  Anchor* endancr = (Anchor*)NULL;
  Annotation* newanno = (Annotation*)NULL;
  Anchor* newancr = (Anchor*)NULL;

  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // get the end anchor of the annotation
  //
  endancr = anno_a->getEndAnchor();

  // clone the annotation
  //
  newanno = copyAnnotation(anno_a);

  // create a new anchor positioned between the split annotations
  //
  newunit.assign(endancr->getUnit());
  newid.assign(anchorids_d->registerId());
  newancr = new Anchor(newid, newunit);

  // make anno_a end at this anchor
  //
  setEndAnchor(anno_a, newancr);

  // make newanno start at this anchor
  //
  setStartAnchor(newanno, newancr);

  // return the new annotation
  //
  return newanno;
}

// method: nSplitAnnotation
//
// arguments:
//   Annotation* anno: (input) annotation to split
//   long num_split: number of splits
//   DoubleLinkedList<Annotation> annos: (output) annotation list
//
// return: split annotations
//
// method that split an annotation to n annotations. aversion of split
// which does the split operation n-1 times, i.e. splits the original
// annotation into n annotations.
//
boolean AnnotationGraph::nSplitAnnotation(Annotation* anno_a,
				       long num_split_a,
				       DoubleLinkedList<Annotation>& annos_a) {

  // declare local variables
  //
  Annotation* anno = anno_a;

  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);
  
  // insert the into the list
  //
  if (anno != (Annotation*)NULL) {
    annos_a.insert(anno);
  }

  // split the annotation as per the number specified
  //
  for (int i=num_split_a; i > 1; i--) {
    if (anno != (Annotation*)NULL) {
      anno = splitAnnotation(anno);
      annos_a.insert(anno);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: deleteAnnotation
//
// arguments:
//   Annotation* anno: (input) annotation to delete
//
// return: logical error status
//
// method that deletes the annotation from the graph
//
boolean AnnotationGraph::deleteAnnotation(Annotation* anno_a) {

  // check for null pointer exceptions
  //
  if (anchorids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }  
  if (annotationids_d == (Identifier*)NULL) {
    return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
  }
  
  // determine if the annotation is present in the graph
  //
  if (getById(anno_a->getId()) != (Annotation*)NULL) {

    // delete the annotation from the indices and identifier issuer
    //
    annotationids_d->deleteAnnotationRef(anno_a->getId());
    index_d.deleteAnnotation(anno_a);

    // remove the annotation from the sequence and reclaim the identifier
    //
    if (annoseq_d.find(anno_a)) {
      annoseq_d.remove();
    }
    annotationids_d->reclaimId(anno_a->getId());

    // delete the annotation
    //
    delete anno_a;

    // exit gracefully
    //
    return true;       
  }
  
  // exit gracefully
  //
  return false;   
}

// method: setStartAnchor
//
// arguments:
//   Annotation* anno: (input) graph annotation
//   Anchor* ancr: (input) start anchor
//
// return: logical error status
//
// method that sets the start anchor of an annotation to the input anchor
//
boolean AnnotationGraph::setStartAnchor(Annotation* anno_a, Anchor* ancr_a) {

  // delete the annotation from the indices
  //
  index_d.deleteAnnotation(anno_a);

  // set the start anchor of the annotation
  //
  anno_a->setStartAnchor(ancr_a);

  // add the annotation to the indices
  //
  index_d.add(anno_a);
  
  // exit gracefully
  //
  return true;   
}

// method: setEndAnchor
//
// arguments:
//   Annotation* anno: (input) graph annotation
//   Anchor* ancr: (input) end anchor
//
// return: logical error status
//
// method that sets the end anchor of an annotation to the specified anchor
//
boolean AnnotationGraph::setEndAnchor(Annotation* anno_a, Anchor* ancr_a) {

  // delete the annotation from the indices
  //
  index_d.deleteAnnotation(anno_a);

  // set the end anchor of the annotation
  //
  anno_a->setEndAnchor(ancr_a);

  // add the annotation to the indices
  //
  index_d.add(anno_a);
  
  // exit gracefully
  //
  return true;   
}

// method: unsetAnchorOffset
//
// arguments:
//   Anchor* ancr: (input) graph anchor
//
// return: logical error status
//
// method that unsets the offset of the specified anchor
//
boolean AnnotationGraph::unsetAnchorOffset(Anchor* ancr_a) {

  // delete anchor from indices
  //
  index_d.deleteAnchor(ancr_a);

  // update the anchor offset
  //
  ancr_a->unsetOffset();

  // add new anchor to indices
  //
  index_d.addAnchor(ancr_a);
  
  // exit gracefully
  //
  return true;   
}

// method: getAnchorSet
//
// arguments:
//   DoubleLinkedList<Anchor>& ancrs: (output) anchors list
//
// return: graph anchor set
//
// metod that gets the set of anchors, sorted by offsets
//
boolean AnnotationGraph::getAnchorSet(DoubleLinkedList<Anchor>& ancrs_a) {

  // clear the list to start with
  //
  ancrs_a.clear();
  ancrs_a.setAllocationMode(DstrBase::USER);
  
  // assign the anchor set to the input list
  //
  ancrs_a.assign(index_d.ancrset_d);
  
  // exit gracefully
  //
  return true;   
}

// method: getById
//
// arguments:
//   const String& id: (input) annotation id
//
// return: annotation
//
// method that gets the annotation reference by its id
//
Annotation* AnnotationGraph::getById(const String& id_a) {

  // declare local variables
  //
  Annotation* annotation = (Annotation*)NULL;

  // check if the annotation exists
  //
  if(index_d.existsAnnotation(id_a)) {
    annotation = index_d.anno_by_id_d.get(id_a);
  }
  
  // return the annotation
  //
  return annotation;
}

// method:
//
// arguments:
//   const String& id: (input) anchor id
//
// return: anchor
//
// method that gets the anchor reference by its id
//
Anchor* AnnotationGraph::getAnchorById(const String& id_a) {

  // declare local variables
  //
  Anchor* anchor = (Anchor*)NULL;

  // check if the anchor exists
  //
  if(index_d.existsAnchor(id_a)) {
    anchor = index_d.ancr_by_id_d.get(id_a);
  }
  
  // return the annotation
  //
  return anchor;
}

// method: getAnchorSetByOffset
//
// arguments:
//   float offset: (input) anchor offset
//   DoubleLinkedList<Anchor> ancrs: (output) anchors list
//   double esp: (input) epsilon value 
//
// return: anchor set
//
// method that gets anchors with the specified offset
//
boolean AnnotationGraph::getAnchorSetByOffset(float offset_a,
					    DoubleLinkedList<Anchor>& ancrs_a,
					    double eps_a) {

  // d eclare local variables
  //
  double delta;  
  Float offset;
  DoubleLinkedList<Anchor> ancrset(DstrBase::USER);

  // clear the list to start with
  //
  ancrs_a.clear();
  ancrs_a.setAllocationMode(DstrBase::USER);
  
  // epsilon should not be a negative number
  //
  eps_a = eps_a > 0.0 ? eps_a : -eps_a;

  // when the specified epsilon is zero
  //
  if (eps_a == 0.0) {
    offset.assign(offset_a);
    if (index_d.ancrset_by_offset_d.get(offset) !=
	(DoubleLinkedList<Anchor>*)NULL) {
      ancrs_a.assign(*(index_d.ancrset_by_offset_d.get(offset)));
    }
  }

  // when the specified epsilon is greater than zero
  //  
  else {
    
    // get the list of anchors
    //
    getAnchorSet(ancrset);
    
    // loop over all anchors in the set
    //
    for(boolean more = ancrset.gotoFirst(); more; more = ancrset.gotoNext()) {
      
      // determine the variance between the current and input offsets
      //
      delta = ancrset.getCurr()->getOffset() - offset_a;
      delta = delta > 0.0 ? delta : -delta;
      
      // insert the anchor into the list if the variance is within epsilon
      //
      if (delta <= eps_a) {
	ancrs_a.insert(ancrset.getCurr());
      }
    }
  }
  
  // exit gracefully
  //
  return true; 
}

// method: getAnnotationSetByType
//
// arguments:
//   const String& type: (input) annotation type
//   DoubleLinkedList<Annotation>& annos: (output) annotation list
//
// return: annotation set
//
// method that gets the annotations using the type
//
boolean AnnotationGraph::getAnnotationSetByType(const String& type_a,
				      DoubleLinkedList<Annotation>& annos_a) {


  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);

  DoubleLinkedList<Annotation>* list;
  
  // assign the annotation set
  //
  if (index_d.anno_by_type_d.get(type_a) !=
      (DoubleLinkedList<Annotation>*)NULL) {
    list = index_d.anno_by_type_d.get(type_a);
    annos_a.assign(*(index_d.anno_by_type_d.get(type_a)));
  }
  
  // exit gracefully
  //
  return true; 
}

// method: getAnnotationSetByOffset
//
// arguments:
//   float offset: (input) annotation offset
//   DoubleLinkedList<Annotation>& annos: (input) annotation list
//
// return: annotation set
//
// method that gets the annotations that overlap a particular time offset.
// gets all annotations whose start anchor offset is smaller than or
// equal to the given offset AND end anchor offset is greater than 
// or equal to the given offet
//
boolean AnnotationGraph::getAnnotationSetByOffset(float offset_a,
				     DoubleLinkedList<Annotation>& annos_a) {

  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);
  
  // get the annotation set by offset
  //
  index_d.getByOffset(offset_a, annos_a);
  
  // exit gracefully
  //
  return true; 
}

// method: getIncomingAnnotationSet
//
// arguments:
//   Anchor* ancr: (input) graph anchor
//   DoubleLinkedList<Annotation>& annos: (input) annotation list
//
// return: annotation set
//
// method that gets the incoming annotations to the specified node
//
boolean AnnotationGraph::getIncomingAnnotationSet(Anchor* ancr_a,
				      DoubleLinkedList<Annotation>& annos_a) {

  // define local variables
  //
  HashKey<Anchor> hashkey;

  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);
  
  // get the incoming annotation set
  //
  hashkey.assign(ancr_a);
  if (index_d.incoming_d.get(hashkey) != (DoubleLinkedList<Annotation>*)NULL) {
    annos_a.assign(*(index_d.incoming_d.get(hashkey)));
  }
  
  // exit gracefully
  //
  return true;   
}

// method: getOutgoingAnnotationSet
//
// arguments:
//   Anchor* ancr: (input) graph anchor
//   DoubleLinkedList<Annotation>& annos: (input) annotation list
//
// return: annotation set
//
// method that gets the outgoing annotations from the specified node
//
boolean AnnotationGraph::getOutgoingAnnotationSet(Anchor* ancr_a,
			    DoubleLinkedList<Annotation>& annos_a) {

  // define local variables
  //
  HashKey<Anchor> hashkey;

  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);
  
  // get the outgoing annotation set
  //
  hashkey.assign(ancr_a);
  if (index_d.outgoing_d.get(hashkey) != (DoubleLinkedList<Annotation>*)NULL) {
    annos_a.assign(*(index_d.outgoing_d.get(hashkey)));
  }
    
  // exit gracefully
  //
  return true;   
}

// method: getNearestOffset
//
// arguments:
//   float offset: (input) offset target
//
// return: nearest offset
//
// method that gets the nearest used offset to the specified offset
//
float AnnotationGraph::getNearestOffset(float offset_a) {

  // get the nearest offset
  //
  return index_d.getNearestOffset(offset_a);
}

// method: getAnnotationSeqByOffset
//
// arguments:
//  DoubleLinkedList<Annotation>& annos: (output) annotation list
//  float begin: (input) start offset
//  float end: (input) end offset
//
// return: annotation set
//
// method that get all annotations with its start anchor offset in between
// the specified values. gets all annotations with its start anchor offset
// in between the specified values. if both values are 0, return all
// annotations in the graph
//
boolean AnnotationGraph::getAnnotationSeqByOffset(DoubleLinkedList<Annotation>& annos_a, float begin_a, float end_a) {

  // declate local variables
  //
  Annotation* anno;
  
  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);
  
  // determine if the annotation sequence is not empty
  //
  if (!annoseq_d.isEmpty()) {

    // when the begin and end times are zero return the annotation sequence
    //
    if ((begin_a == 0.0) && (end_a == 0.0)) {
      annos_a.assign(annoseq_d);
    }

    // determine which annotations in the sequence lie in the specified range
    //
    else {
      
      // loop over all annotations in the sequence
      //
      for (boolean more = annoseq_d.gotoFirst(); more;
	   more = annoseq_d.gotoNext()) {
	
	// get the current annotation
	//
	anno = annoseq_d.getCurr();
	
	// check if the annotation start anchor is within bounds
	//
	if (anno->getStartAnchor()->getOffset() >= begin_a) {

	  // check if the annotation start anchor is within bounds
	  //
	  if (anno->getStartAnchor()->getOffset() <= end_a) {	  

	    // insert the annotation into the list
	    //
	    annos_a.insert(anno);
	  }
	}
      }
    }
  }
  
  // exit gracefully
  //
  return true;   
}

// method: getAnnotationByOffset
//
// arguments:
//   float offset: (input) annotation offset
//
// return: annotation
//
// method that gets one of the annotations which overlap a particular time
// offset. same as getByOffset except that getAnnotationByOffset returns only
// one qualified annotation while getByOffset returns all of them
//
Annotation* AnnotationGraph::getAnnotationByOffset(float offset_a) {

  // get the annotation by offset
  //
  return index_d.getAnnotationByOffset(offset_a);
}

// method: getAnchorSetNearestOffset
//
// arguments:
//   float offset: (input) anchor offset
//   DoubleLinkedList<Anchor>& ancrs: (output) anchots list
//
// return: anchor set
//
// method that gets all anchors whose offset is the nearest to the
// specified offset
//
boolean AnnotationGraph::getAnchorSetNearestOffset(float offset_a,
				       DoubleLinkedList<Anchor>& ancrs_a) {

  // determine the nearest offset
  //
  float offset = getNearestOffset(offset_a);

  // get the anchor set corresponding to the nearest offset
  //
  getAnchorSetByOffset(offset, ancrs_a);
 
  // exit gracefully
  //
  return true;   
}

// method: getAnnotationSet
//
// arguments:
//   DoubleLinkedList<Annotation>& annos_a: (output) annotation list
//
// return: logical error status
//
// method to get all the annotations in the graph
//
boolean AnnotationGraph::getAnnotationSet(DoubleLinkedList<Annotation>&
					  annos_a) {

  // declare local variables
  //
  String index;
  
  // clear the list to start with
  //
  annos_a.clear();
  annos_a.setAllocationMode(DstrBase::USER);

  // get the list of annotations
  //
  annos_a.assign(annoseq_d);
  
  // exit gracefully
  //
  return true;  
}

// method: deleteAnnotationSetByFeature
//
// arguments:
//   const String& fname_a: (input) feature name
//   const String& fvalue_a: (input) feature value
//
// return: logical error status
//
// method to delete the annotations and the corresponding redundant
// anchors with the feature-value pair
//
boolean AnnotationGraph::deleteAnnotationSetByFeature(const String& fname_a,
						      const String& fvalue_a) {
  
  // declare local variables
  //
  DoubleLinkedList<Annotation> annos(DstrBase::USER);
  Annotation* anno = (Annotation*)NULL;
  
  // get the list of annotations corresponding to the feature-value pair
  //
  if (!getAnnotationSetByFeature(fname_a, fvalue_a, annos)) {
    return Error::handle(name(), L"no annotation for this feature and the value", Error::ARG, __FILE__, __LINE__);
  }
  
  // loop over the list of annotations and delete them
  //
  for (boolean more = annos.gotoFirst(); more; more = annos.gotoNext()) {

    // get the annotation
    //
    anno = annos.getCurr();

    // check for null annotation
    //
    if (anno == (Annotation*)NULL) {
      return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
    }

    // delete the annotation
    //
    if (!deleteAnnotation(anno)) {
      return Error::handle(name(), L"can't delete this Annotation", Error::ARG, __FILE__, __LINE__);
    }
  }
  
  // get all the anchors and purge the redundant anchors
  //
  if (!purgeAnchors()) {
    return Error::handle(name(), L"can't purge the Anchors", Error::ARG, __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;  
}

// method: purgeAnchors
//
// arguments:
//   none
//
// return: logical error status
//
//  method to purge all the redundant anchors in the graph
//
boolean AnnotationGraph::purgeAnchors() {

  // local variables
  //
  DoubleLinkedList<Anchor> ancrs(DstrBase::USER);
  Anchor* ancr = (Anchor*)NULL;
  DoubleLinkedList<Annotation> in_annos(DstrBase::USER);
  DoubleLinkedList<Annotation> out_annos(DstrBase::USER);

  // get all the anchors  
  //  
  if (!getAnchorSet(ancrs)) {
    return Error::handle(name(), L"no Anchors in this AnnotationGraph", Error::ARG, __FILE__, __LINE__);
  }

  // delete the redundant anchors
  //
  for (boolean morea = ancrs.gotoFirst(); morea; morea = ancrs.gotoNext()) {

    // get the anchor
    //
    ancr = ancrs.getCurr();
    
    // check for null anchor
    //
    if (ancr == (Anchor*)NULL) {
      return Error::handle(name(), L"null pointer", Error::ARG, __FILE__, __LINE__);
    }

    // clear the memory
    //
    in_annos.clear();
    out_annos.clear();
    
    // get the incoming and the outgoing annotations at this anchor
    // 
    if (!getIncomingAnnotationSet(ancr, in_annos)) {
      return Error::handle(name(), L"no annotation in this AnnotationGraph", Error::ARG, __FILE__, __LINE__);
    }
    
    if (!getOutgoingAnnotationSet(ancr, out_annos)) {
      return Error::handle(name(), L"no annotation in this AnnotationGraph", Error::ARG, __FILE__, __LINE__);
    }

    // delete this anchor if both the incoming and the outgoig
    // annotations are zero
    //
    if ((in_annos.length() == (long)0) && (out_annos.length() == (long)0)) {
      
      // delete the annotation
      //
      if (!deleteAnchor(ancr)) {
	return Error::handle(name(), L"can't delete this Anchor", Error::ARG, __FILE__, __LINE__);
      }
    }
  }    
  
  // exit gracefully
  //
  return true;  
}
