from zope.interface import Interface
from zope.interface.interfaces import IInterface
import zope.schema

# Keys for tagged values on interfaces

FILENAME_KEY    = 'plone.supermodel.filename'    # absolute file name of model file
SCHEMA_NAME_KEY = 'plone.supermodel.schemaname'  # name of schema that was loaded from the model
FIELDSETS_KEY   = 'plone.supermodel.fieldsets'   # list of fieldsets

# The namespace for the default supermodel schema/field parser

XML_NAMESPACE = u"http://namespaces.plone.org/supermodel/schema"

class IModel(Interface):
    """Describes a model as generated by this library
    """
    
    schema = zope.schema.InterfaceField(title=u"Default schema",
                                        readonly=True)

    schemata = zope.schema.Dict(title=u"Schemata",
                                key_type=zope.schema.TextLine(title=u"Schema name",
                                        description=u"Default schema is under the key u''."),
                                value_type=zope.schema.Object(title=u"Schema interface",
                                        schema=IInterface))

class IFieldset(Interface):
    """Describes a grouping of fields in the schema
    """
    
    __name__ = zope.schema.TextLine(title=u"Fieldset name")
    
    label = zope.schema.TextLine(title=u"Label")
    
    description = zope.schema.TextLine(title=u"Long description", required=False)
    
    fields = zope.schema.List(title=u"Field names",
                              value_type=zope.schema.TextLine(title=u"Field name"))

class IXMLToSchema(Interface):
    """Functionality to parse an XML representation of a schema and return
    an interface representation with zope.schema fields.
    
    A file can be parsed purely for a schema. This allows syntax like:
    
        schema = xml_schema('schema.xml')
        
    If a file contains multiple schemata, you can load them all using:
    
        model = load_file('schema.xml')
    """
    
    def xml_schema(filename, schema=u"", policy=u""):
        """Given a filename relative to the current module, return an
        interface representing the schema contained in that file. If there
        are multiple <schema /> blocks, return the unnamed one, unless 
        a name is supplied, in which case the 'name' attribute of the schema
        will be matched to the schema name.
        
        The policy argument can be used to pick a different parsing policy.
        Policies must be registered as named utilities providing
        ISchemaPolicy.
        
        Raises a KeyError if the schema cannot be found.
        Raises an IOError if the file cannot be opened.
        """
    
    def load_file(filename, reload=False, policy=u""):
        """Return an IModel as contained in the given XML file, which is read
        relative to the current module (unless it is an absolute path).
        
        If reload is True, reload a schema even if it's cached. If policy
        is given, it can be used to select a custom schema parsing policy.
        Policies must be registered as named utilities providing
        ISchemaPolicy.
        """
    
    def load_string(model, policy=u""):
        """Load a model from a string rather than a file.
        """
    
    def serialize_schema(schema, name=u""):
        """Return an XML string representing the given schema interface. This
        is a convenience method around the serialize_model() method, below.
        """

    def serialize_model(model):
        """Return an XML string representing the given model, as returned by
        the load_file() or load_string() method.
        """

class ISchemaPolicy(Interface):
    """A utility that provides some basic attributes of the generated
    schemata. Provide a custom one to make policy decisions about where
    generated schemata live, what bases they have and how they are named.
    """

    def module(schema_name, tree):
        """Return the module name to use.
        """
        
    def bases(schema_name, tree):
        """Return the bases to use.
        """
        
    def name(schema_name, tree):
        """Return the schema name to use
        """
        
class IFieldExportImportHandler(Interface):
    """Named utilities corresponding to node names should be registered for
    this interface. They will be called upon to build a schema fields out of
    DOM ndoes.
    """
    
    def read(node):
        """Read a field from the node and return a new instance
        """
        
    def write(field, field_name, field_type, element_name='field'):
        """Create and return a new node representing the given field
        """
        
class ISchemaMetadataHandler(Interface):
    """A third party application can register named utilities providing this
    interface. For each schema that is parsed in a model, the read() method
    will be called.
    """
    
    namespace = zope.schema.URI(title=u"XML namespace used by this handler", required=False)
    prefix = zope.schema.ASCII(title=u"Preferred XML schema namespace for serialisation", required=False)
    
    def read(schema_node, schema):
        """Called once the schema in the given <schema /> node has been
        read. schema is the schema interface that was read.
        """

    def write(schema_node, schema):
        """Write the metadata contained in the given schema, to the
        schema_node. The node will already exist and be populated with
        standard data.
        """

class IFieldMetadataHandler(Interface):
    """A third party application can register named utilities providing this
    interface. For each field that is parsed in a schema, the read() method
    will be called.
    """
    
    namespace = zope.schema.URI(title=u"XML namespace used by this handler", required=False)
    prefix = zope.schema.ASCII(title=u"Preferred XML schema namespace for serialisation", required=False)
    
    def read(field_node, schema, field):
        """Called once the field in the given <field /> node has been
        read. field is the field instance that was read. schema is the schema
        it is a part of.
        """
    
    def write(field_node, schema, field):
        """Write the metadata for the field in the given schema to the
        field_node. The node will already exist and be populated with
        standard data.
        """

class IFieldNameExtractor(Interface):
    """Adapter to determine the canonical name of a field
    """
    
    def __call__():
        """Return the name of the adapted field
        """
class IToUnicode(Interface):
    """Reciprocal to IToUnicode. Adapting a field to this interface allows
    a string representation to be extracted.
    """
    
    def toUnicode(value):
        """Convert the field value to a unicode string.
        """