''' Usage
    python rearrange_models.py inspected_models.py ordered_models.py
'''

import re
from cStringIO import StringIO

class DjangoModelsArrangement:
    def __init__(self):
        self.output = StringIO()
        self.classes = {}
        self.already_written = set()

    def get_classes(self, input):
        # This regular expression will divide the input as
        # [file_header, class_declaration1, class_body1, class_declaration2, class_body2, ...]
        parsed_classes = re.split(r'(class\s+.*?\(models\.Model\):)', input)
        self.header = parsed_classes[0]
        return [i+j for i, j in zip(parsed_classes[1::2], parsed_classes[2::2])]
    
    def parse_class(self, class_def):
        lines = class_def.split('\n')
        class_decl, class_body = lines[0], lines[1:]
        class_name = re.findall(r'class\s+(.*?)\(models\.Model\):', class_decl)[0]
        referenced_set = set()
        for line in lines:
            if 'ForeignKey' in line:
                referenced_class = re.findall(r'ForeignKey\((.*?)(?:,|\))', line)[0]
                if referenced_class and referenced_class != "'self'":
                    referenced_set.add(referenced_class)
        self.classes[class_name] = (referenced_set, class_def)
    
    def write_class(self, class_):
        if class_ not in self.already_written:
            self.already_written.add(class_)
            referenced_set, class_def = self.classes[class_]
            if referenced_set:
                for referenced_class in referenced_set:
                    self.write_class(referenced_class)
            self.output.write(class_def)
            
                
    def arrange(self, source_file, dest_file):
        input = open(source_file).read()
        classes = self.get_classes(input)
        for class_ in classes:
            self.parse_class(class_)
        for class_ in self.classes:
            self.write_class(class_)
        output_data = self.output.getvalue()
        open(dest_file, 'w').write(self.header + output_data)
        
if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print __doc__
        sys.exit(1)
    DjangoModelsArrangement().arrange(sys.argv[1], sys.argv[2])
