"""Base Transitland Entity."""
import json

import mzgeohash

import geom
import util
import errors

class Entity(object):
  """A Transitland Entity."""  
  # OnestopID prefix.
  onestop_type = None

  def __init__(self, **data):
    """Set name, Onestop ID, and geometry."""
    if 'onestop_id' in data:
      data['onestopId'] = data.pop('onestop_id')
    data['tags'] = data.get('tags') or {}
    self.data = data
    self.parents = set()
    self.children = set()

  # Basic ID, name, geometry...    
  def name(self):
    """A reasonable display name for the entity."""
    return self.data.get('name')
  
  def id(self):
    """Alias to onestop()"""
    return self.onestop()

  def onestop(self):
    """Return the Onestop ID for this entity."""
    return self.data.get('onestopId') or self.make_onestop()
    
  def make_onestop(self):
    # Check maximum length.
    onestop = '%s-%s-%s'%(
      self.onestop_type, 
      self.geohash()[:util.GEOHASH_LENGTH], 
      self.mangle(self.name())
    )
    return onestop[:util.ONESTOP_LENGTH]

  def mangle(self, s):
    """Mangle a string into a Onestop component."""
    s = s.lower()
    for a,b in util.REPLACE_CHAR:
      s = a.sub(b,s)
    s = s.strip() 
    return s

  # Entity geometry.
  def geohash(self):
    """Return the geohash for this entity."""
    raise NotImplementedError

  def geometry(self):
    """Return a GeoJSON-type geometry for this entity."""
    # TODO: Right now, this has to be supplied by GTFS Entity...
    return self.data.get('geometry')

  def point(self):
    """Return a point for this entity."""
    raise NotImplementedError
  
  def bbox(self):
    """Return a bounding box for this entity."""
    c = self.point()
    return [c[0], c[1], c[0], c[1]]
  
  # Load from JSON.
  @classmethod
  def from_json(cls, data):
    return cls(**data)

  def json(self):
    """Return a GeoJSON representation of this entity."""
    raise NotImplementedError
  
  # Tags and identifiers
  def tags(self):
    return self.data['tags']

  def tag(self, key):
    return self.data['tags'].get(key)

  def identifiers(self):
    return self.data.get('identifiers') or []

  def set_tag(self, key, value):
    self.add_tags({key:value})

  def add_tags(self, tags):
    self.data['tags'].update(tags)

  def add_identifier(self, identifier):
    """Add GTFS data to the set of identifiers."""
    if not self.data.get('identifiers'):
      self.data['identifiers'] = []
    if identifier in self.data['identifiers']:
      raise errors.ExistingIdentifierError(
        "Identifier already present: %s"%identifier
      )
    self.data['identifiers'].append(identifier)
  
  # Rename
  def merge(self, item, ignore_existing=True):
    """Merge data into identifiers."""
    for identifier in item.identifiers():
      try:
        self.add_identifier(identifier)
      except errors.ExistingIdentifierError, e:
        if ignore_existing:
          pass
        else:
          raise
    # merge name and geometry.
    if 'name' in item.data:
      self.data['name'] = item.data['name']
    if 'geometry' in item.data:
      self.data['geometry'] = item.data['geometry']
    # merge tags
    for k,v in item.tags().items():
      self.set_tag(k,v)
    # merge relations
    relkeys = ['serves', 'servedBy', 'operatedBy']
    for relkey in relkeys:
      if (relkey in self.data) or (relkey in item.data):
        a = set(self.data.get(relkey, []))
        b = set(item.data.get(relkey, []))
        self.data[relkey] = sorted(a | b)

  # Graph.
  def pclink(self, parent, child):
    """Create a parent-child relationship."""
    parent.children.add(child)
    child.parents.add(parent)

  # ... children
  def add_child(self, child):
    """Add a child relationship."""
    self.pclink(self, child)

  # ... parents
  def add_parent(self, parent):
    """Add a parent relationship."""
    self.pclink(parent, self)  
    
@@ -807,20 +807,16 @@
 estop()%0A
-    
 %0A  def m
@@ -827,26 +827,135 @@
 onestop(self
+, geohash=None, name=None):%0A    geohash = geohash or self.geohash()%0A    name = self.mangle(name or self.name()
 )
-:
 %0A    # Check
@@ -1030,30 +1030,23 @@
  %0A      
-self.
 geohash
-()
 %5B:util.G
@@ -1072,32 +1072,12 @@
     
-self.mangle(self.
 name
-())
 %0A   
@@ -1821,24 +1821,333 @@
 %5D, c%5B1%5D%5D%0A  %0A
+  # Load from GTFS entity.%0A  @classmethod%0A  def from_gtfs(cls, gtfs_entity, feedid, **kw):%0A    entity = cls(%0A      name=gtfs_entity.name(),%0A      geometry=gtfs_entity.geometry(),%0A      **kw%0A    )%0A    entity.add_identifier(gtfs_entity.feedid(feedid))%0A    entity.add_tags_gtfs(gtfs_entity)%0A    return entity%0A  %0A
   # Load fro
@@ -2665,16 +2665,66 @@
 (tags)%0A%0A
+  def add_tags_gtfs(self, gtfs_entity):%0A    pass%0A%0A
   def ad
@@ -2901,16 +2901,20 @@
 ntifier 
+not 
 in self.
@@ -2940,110 +2940,8 @@
 :%0A  
-    raise errors.ExistingIdentifierError(%0A        %22Identifier already present: %25s%22%25identifier%0A      )%0A
     
@@ -3024,30 +3024,8 @@
 item
-, ignore_existing=True
 ):%0A 
@@ -3108,21 +3108,8 @@
 ():%0A
-      try:%0A  
     
@@ -3146,129 +3146,8 @@
 er)%0A
-      except errors.ExistingIdentifierError, e:%0A        if ignore_existing:%0A          pass%0A        else:%0A          raise%0A
     

Add arguments to make_onestop; add from_gtfs method; ignore dup identifiers