import subprocess
import os
import tempfile
import logging
import geogig
from collections import defaultdict
from feature import Feature
from tree import Tree
from commit import Commit
import datetime
from diff import Diffentry, ATTRIBUTE_DIFF_MODIFIED
from connector import Connector
from commitish import Commitish
from geogigexception import GeoGigException, GeoGigConflictException, UnconfiguredUserException
from geometry import Geometry
from copy import deepcopy
from geogigexception import GeoGigException

def _run(command, addcolor = True):         
    command = ['geogig'] + command
    if addcolor:
        command.extend(["--color", "never"])
    commandstr = " ".join(command)
    if os.name != 'nt':
        command = commandstr
    output = []   
    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, 
                            stdin=subprocess.PIPE,stderr=subprocess.STDOUT, universal_newlines=True)
    for line in iter(proc.stdout.readline, ""):        
        line = line.strip("\n")
        output.append(line)            
    proc.wait()
    returncode = proc.returncode  
    if returncode:                        
        logging.error("Error running " + commandstr + "\n" + " ".join(output))
        raise GeoGigException(output)
    logging.info("Executed " + commandstr + "\n" + " ".join(output[:5]))      
    return output

    
class CLIConnector(Connector):
    ''' A connector that calls the CLI version of geogig and parses CLI output'''
    
    def __init__(self):
        self.commandslog = []        

    def setRepository(self, repo):
        self.repo = repo                     

    def createdat(self):
        return datetime.datetime.fromtimestamp(os.stat(os.path.join(self.repo.url, ".geogig")).st_ctime)
    
    @staticmethod
    def clone(url, dest, username = None, password = None):        
        commands = ['clone', url, dest]
        if username is not None and password is not None:
            commands.extend(["--username", username, "--password", password])
        _run(commands)    

    def geogigversion(self):
        output = self.run(["--version"])
        return output[0].split(":")[1].strip()    
                
    def run(self, command):   
        os.chdir(self.repo.url)         
        self.commandslog.append(" ".join(command))
        return _run(command)        

    def revparse(self, rev):
        commands = ['rev-parse', rev]
        output = self.run(commands)
        id = output[0].strip()
        if l
en(id) != 40:
            raise GeoGigException("Cannot resolve the provided reference")        
        return id
    
    def head(se
lf):
        self.checkisrepo()
        headfile = os.path.join(self.repo.url, '.geogig', 'HEAD')
        f = open(headfile)
        line = f.readline()
        f.close()
        ref = line.strip().split()[-1]
        if ref.startswith("refs/heads/"):
            ref = ref[len("refs/heads/"):]
        return Commitish(self.repo, ref)           

    def isrebasing(self):
        self.checkisrepo()
        headfile = os.path.join(self.repo.url, '.geogig', 'ORIG_HEAD')
        branchfile =  os.path.join(self.repo.url, '.geogig', 'rebase-apply', 'branch')
        return os.path.exists(headfile) and os.path.exists(branchfile) 
    
    def ismerging(self):
        self.checkisrepo()
        headfile = os.path.join(self.repo.url, '.geogig', 'ORIG_HEAD')
        mergeheadfile = os.path.join(self.repo.url, '.geogig', 'MERGE_HEAD')        
        return os.path.exists(headfile) and os.path.exists(mergeheadfile)    
        
    def mergemessage(self):
        msgfile = headfile = os.path.join(self.repo.url, '.geogig', 'MERGE_MSG')
        if os.path.exists(msgfile):
            with open(msgfile) as f:
                lines = f.readlines()
            return "".join(lines)
        else:            
            return "" 
        
    def checkisrepo(self):
        if not os.path.exists(os.path.join(self.repo.url, '.geogig')):
            raise GeoGigException("Not a valid GeoGig repository: " + self.repo.url)
        
    
    def children(self, ref = geogig.HEAD, path = None, recursive = False):
        children = []    
        if path is None:
            fullref = ref
        else:
            fullref = ref + ':' + path 
        commands = ['ls-tree', fullref, "-v"]
        if recursive:
            commands.append("-r")
        output = self.run(commands)    
        for line in output:
            if line != '':                
                tokens = line.split(" ")
                if tokens[1] == "feature":
                    children.append(Feature(self.repo, ref, tokens[3]))
                elif tokens[1] == "tree":
                    try:
                       size = int(tokens[5])
                    except:
                       size = None
                    children.append(Tree(self.repo, ref, tokens[3], size))
        return children   
    
    def commitFromString(self, lines):                
        message = False
        messagetext = []
        parents = None
        commitid = None
        for line in lines:
            tokens = line.split(' ')
            if message:
                if line.startswith("\t") or line.startswith(" "):
                    messagetext.append(line.strip())
                else:
                    message = False            
            else:                
                if tokens[0] == 'commit':
                    commitid = tokens[1]
                if tokens[0] == 'tree':
                    tree = tokens[1]                    
                if tokens[0] == 'parent':                    
                    if len(tokens) > 1:
                        parents = [t for t in tokens[1:] if t != ""]                    
                elif tokens[0] == 'author':
                    author = " ".join(tokens[1:-3])
                    authordate = datetime.datetime.fromtimestamp((int(tokens[-2]) - int(tokens[-1]))//1000)                
                elif tokens[0] == 'committer':
                    committer = tokens[1]
                    committerdate = datetime.datetime.fromtimestamp((int(tokens[-2]) - int(tokens[-1]))//1000)
                elif tokens[0] == 'message':
                    message = True                
            
        if commitid is not None:            
            c = Commit(self.repo, commitid, tree, parents, "\n".join(messagetext), author, authordate, committer, committerdate)
            return c
        else:
            return None

    def addremote(self, name, url, username, password):
        if username and password:
            commands = ["remote", "add", "-u", username, "--password", password, name, url]
        else:
            commands = ["remote", "add", name, url]
        self.run(commands)
        
    def removeremote(self, name):
        commands = ["remote", "rm", name]
        self.run(commands)     
        
    def remotes(self):
        commands = ["remote", "list", "-v"]
        output = self.run(commands)
        remotes = {}        
        for line in output:
            tokens = line.split()
            if len(tokens) != 3:
                continue
            if tokens[0] not in remotes:
                remotes[tokens[0]] =  tokens[1]                
        return remotes        
        
    def log(self, tip, sincecommit = None, until = None, since = None, path = None, n = None):
        commits = []             
        param = tip if sincecommit is None else (sincecommit + ".." + tip)   
        commands = ['rev-list', param]        
        if path:
            if isinstance(path, list):
                commands.append("-p")
                commands.extend(path)
            else:
                commands.extend(["-p", path])
        if until is not None:
            commands.extend(["--until", until])
        if since is not None:
            commands.extend(["--since", since])            
        if n is not None:
            commands.extend(["-n", str(n)])            
        try:
            output = self.run(commands)
        except GeoGigException, e:
            if "HEAD does not resolve" in e.args[0]: 