#!/usr/bin/env python
"""
Script to extract a self-consistent tree of XML files.
"""
import xml.sax
import os
import shutil
from sys import argv,exit
import re

class Collector:
    def __init__(self,curr_dir="."):
        self.curr_dir = curr_dir
        self.collection = set()
    def collect(self,publicId,systemId):
        if systemId:
            # search for environment variables
            m = re.search("\$([a-zA-Z0-9_]*)",systemId)
            if m:
                n = m.groups()[0]
                if not n in os.environ:
                    raise RuntimeError("Cannot find environment variable %s"%n)
                systemId = systemId.replace("$%s"%n,os.environ[n])
            else:
                systemId = os.path.normpath(os.path.join(self.curr_dir,systemId))
            self.collection.add(systemId)
        else:
            self.collection.add(publicId)
    def chdir(self,d):
        if d[0] == '/':
            self.curr_dir = d
        else:
            self.curr_dir = os.path.normpath(os.path.join(self.curr_dir,d))
    
class MyEntityResolver(xml.sax.handler.EntityResolver):
    def __init__(self,collector):
        self.collector = collector
    def resolveEntity(self,publicId,systemId):
        #print (publicId,systemId)
        self.collector.collect(publicId,systemId)
        return xml.sax.handler.EntityResolver.resolveEntity(self,publicId,systemId)

class MyContentHandler(xml.sax.handler.ContentHandler):
    def __init__(self,collector):
        xml.sax.handler.ContentHandler.__init__(self)
        self.collector = collector
    def startElement(self,name,attr):
        if 'href' in attr.getNames():
            f = attr.getValue('href')
            p = f.find("#")
            if (p>=0) :
                f = f[0:p]
            if f:
                self.collector.collect(None,f)

class MyDTDHandler(xml.sax.handler.DTDHandler):
    def notationDecl(self,name, publicId, systemId):
        print "MyDTDHandler.notationDecl",name, publicId, systemId
        return xml.sax.handler.DTDHandler.notationDecl(self,name, publicId, systemId)
    def unparsedEntityDecl( self, name, publicId, systemId, ndata):
        print "MyDTDHandler.unparsedEntityDecl",name, publicId, systemId, ndata
        return xml.sax.handler.DTDHandler.unparsedEntityDecl(self,name, publicId, systemId, ndata)


def find_file(dir,name):
    name = os.path.basename(name)
    for p,d,f in os.walk(dir):
        if name in f:
            return os.path.join(p,name)

if __name__ == "__main__":

    if len(argv) < 4:
        print "Usage: %s source_dir dest_dir entry_point"%os.path.basename(argv[0])
        print "Note that the entry point has to be relative to the source directory."
        exit(1)

    source = os.path.normpath(argv[1])
    destination = os.path.normpath(argv[2])
    entry_point = os.path.normpath(os.path.join(source,argv[3]))
    
    reader = xml.sax.make_parser()

    ERcoll = Collector()
    er = MyEntityResolver(ERcoll)

    CHcoll = Collector()
    ch = MyContentHandler(CHcoll)

    reader.setEntityResolver(er)
    reader.setContentHandler(ch)

    to_parse = set()
    parsed = set()
    next_to_parse = set([entry_point])
    #next_to_parse = set(["Det/XmlDDDB/v30r12/DDDB/Magnet/geometry.xml"])
    while next_to_parse:
        # we still have files to parse

        to_parse = next_to_parse # I loop over these
    
        CHcoll.collection = set() # I collect here those still to parse
    
        for f in to_parse:
            if f.find("CONDITIONS_PATH") >= 0 : continue # this does not bring us anywhere
            # prepare to parse file
            CHcoll.curr_dir = ERcoll.curr_dir = os.path.dirname(f)
            
            parsed.add(f)
            
            # parse
            print "parsing %s"%f
            reader.parse(f)
            
        next_to_parse = CHcoll.collection


    all_files = parsed
    all_files.update(ERcoll.collection)

    for f in all_files:
        #if f.endswith(".dtd"):
        #    f = os.path.join(src,"DDDB","DTD",os.path.basename(f))
        orig = f
        if not os.path.exists(f):
            orig = find_file(source,f)
            
        destfile = orig.replace(source,destination)
        
        d = os.path.dirname(destfile)
        if not os.path.exists(d):
            os.makedirs(d)

        shutil.copy2(orig,destfile)
        print "%s -> %s"%(orig,destfile)
