import tempfile
import os
import os.path
import sys
import re
import math
import json
import ast
import datetime
import subprocess
import pprint

import checkmate.lib.analysis.languages.python.ast_parser as parser
from checkmate.lib.stats.mapreduce import mapreduce
import checkmate.settings.stats as statistics_settings
from checkmate.lib.git.repository import Repository

def analyze_file(repository,commit_sha,path,aggregators = statistics_settings.python_node_aggregators):
    if not re.match(".*(\.py|\.pyw)$",path):
        return None
    file_details = repository.get_file_details(commit_sha,path)
    file_content = repository.get_file_content(commit_sha,path)
    try:
        (visitor,fileNode) = parser.analyze_string(file_content,path)
    except SyntaxError:
        print "Syntax error in file: %s %s" % (path,commit_sha)
        return None
    tree = fileNode.to_dict()
    file_stats = mapreduce(get_nodes_list(tree),aggregators)
    file_stats['average_number_of_characters'] = file_stats['number_of_characters'] = len(file_content)
    file_stats['average_number_of_lines'] = file_stats['number_of_lines'] = len(file_content.split("\n"))
    file_revision = {'commit_sha' : commit_sha,'stats':file_stats}
    file_revision.update(file_details)
    return file_revision

def get_nodes_list(root_node):
    nodes = [root_node]
    for child in root_node['children']:
        nodes.extend(get_nodes_list(child))
    return nodes

def analyze_files_in_commit(repository,commit_sha,reference_commit):
    submodules = repository.get_submodules()
    new_file_revisions = []
    unchanged = False
    if reference_commit:
        diffs = repository.get_diffs(reference_commit['sha'],commit_sha)
        for diff in diffs:
            if not diff[1] in submodules:
                filename = diff[1]
                if diff[0] == 'A' or diff[0] == 'M':
                    unchanged = False
                    file_revision = analyze_file(repository,commit_sha,filename)
                    if file_revision:
                        new_file_revisions.append(file_revision)
                        reference_commit['file_revisions'][filename] = file_revision
                        reference_commit['filenames'][filename] = file_revision['sha']
                elif diff[0] == 'R':
                    raise Exception("renamed!")
                elif diff[0] == 'D':
                    if filename in reference_commit['filenames']:
                        unchanged = False
                        del reference_commit['filenames'][filename],reference_commit['file_revisions'][filename]
    else:
        reference_commit = {'sha' : commit_sha,'file_revisions' : {},'filenames' : {} }
        unchanged = False
        files_in_commit = repository.get_files_in_commit(commit_sha)
        for filename in files_in_commit:
            file_revision = analyze_file(repository,commit_sha,filename)
            if file_revision:
                new_file_revisions.append(file_revision)
                reference_commit['file_revisions'][filename] = file_revision
                reference_commit['filenames'][filename] = file_revision['sha']
    reference_commit['sha'] = commit_sha
    return new_file_revisions,unchanged,reference_commit

def analyze_repository(repository,writer,commits = None):
    if not commits:
        commits = repository.get_commits()
    last_stat_commit_sha = None
    reference_commit = None
    file_revision_ids = {}
    with writer:
        ordered_commits = list(sorted(commits,key = lambda commit : commit['author_date']))        
        for commit in ordered_commits:
            new_file_revisions,unchanged,reference_commit = analyze_files_in_commit(repository,commit['sha'],reference_commit)
            commit['reference_commit_sha'] = reference_commit['sha'] if reference_commit else None
            commit['tree'] = dict(zip([str(x) for x in reference_commit['filenames'].values()],reference_commit['filenames'].keys()))
            if not unchanged:
                commit_stats = mapreduce(reference_commit['file_revisions'].values(),statistics_settings.file_aggregators+statistics_settings.python_file_aggregators,group_results_by_key = False)
                commit_stats['n_changed_files'] = len(reference_commit['file_revisions'])
                commit_stats['n_files'] = len(reference_commit['filenames'])
                last_stat_commit_sha = commit['sha']
            else:
                commit_stats = {'look_at' : last_stat_commit_sha}
            for file_revision in new_file_revisions:
                file_revision_ids[file_revision['sha']] = writer.write('file_revision',file_revision)
            commit['file_revisions'] = [file_revision_ids[sha] for sha in commit['tree'].keys()]
            commit_id = writer.write('commit',commit)
            commit_stats['commit'] = commit_id
            commit_stats['commit_sha'] = commit['sha']
            writer.write('commit_stats',commit_stats)
            print commit['committer_date'],len(new_file_revisions),len(reference_commit['file_revisions'])
