import dict_comparison as dc
import numpy as np
import os
import os.path
import stat
import json
from custom_json import json_dumper, json_hook

par_ext = ".params"
run_ext = ".run"

# maybe move the following to grace plot?
class InDirectory:
    def __init__(self, path):
        self.path = path
    def __enter__(self):
        self.oldwd = os.getcwd()
        os.chdir(os.path.dirname(self.path))
    def __exit__(self, exc_type, exc_val, exc_tb):
        os.chdir(self.oldwd)

def save(d, filename):
    fp = open(filename, "w")
    json.dump(d, fp, default=json_dumper, sort_keys=True, indent=4)
    fp.close()

def load(filename):
    fp = open(filename, "r")
    d = json.load(fp, object_hook=json_hook)
    fp.close()
    return d

def get_shortest_possible_hash(ha, all_hashes, min_len=4):
    all_hashes = list(all_hashes)
    if ha in all_hashes:
        all_hashes.remove(ha)
    l = min_len
    while l < len(ha):
        col = False
        for oh in all_hashes:
            if oh.startswith(ha[:l]):
                col = True
                break
        if col:
            l += 1
        else:
            break
    return ha[:l]

def file_system_boundary_crossed(p1, p2):
    return os.stat(p1)[stat.ST_DEV] != os.stat(p2)[stat.ST_DEV]

def find_in_parents(path):
    path = os.path.realpath(path)
    dirname = os.path.dirname(path)
    basename = os.path.basename(path)

    while True:
        if os.path.exists(dirname + "/" + basename):
            return dirname + "/" + basename
        elif dirname != '/':
            dirname = os.path.dirname(dirname) # up to parent 
        if dirname == "/" or file_system_boundary_crossed(os.path.dirname(path), dirname):
            break
    raise IOError("'%s' not found up to file system boundary" % basename)

def find_all_in_parents(path):
    path = os.path.realpath(path)
    dirname = os.path.dirname(path)
    basename = os.path.basename(path)
    
    res = []

    while True:
        if os.path.exists(dirname + "/" + basename):
            res.append(dirname + "/" + basename)
        if dirname != '/':
            dirname = os.path.dirname(dirname) # up to parent 
        if dirname == "/" or file_system_boundary_crossed(os.path.dirname(path), dirname):
            break
    if len(res) == 0:
        IOError("'%s' not found up to file system boundary" % basename)
    return res

def load_parameters_incrementally(path):
    fns = find_all_in_parents(path)
    pd = {}
    for fn in fns[::-1]:
        d = load(fn)
        pd.update(d)
    return pd


def load_run(name):
    if os.path.exists(name):
        return name, load(name)
    else:
        rs = load_runs_in_dir(os.path.dirname(os.path.realpath(name)))
        resrfn = ""
        resr = None
        for rfn, r in rs:
            if rfn.startswith(os.path.basename(name)):
                if resr is not None:
                    raise IOError("'%s' does not unambiguously identify a run" % os.path.basename(name))
                resr = r
                resrfn = rfn
        if len(rs) == 0 or resr is None:
            raise IOError("'%s' not found" % name)
        return resrfn, resr

def load_runs_in_dir(d="."):
    rfns = [rfn for rfn in os.listdir(d) if rfn.endswith(run_ext)]
    return [(rfn, load(d + "/" + rfn)) for rfn in rfns]

def load_newest_run(d="."):
    return load_newest_run_matching({}, d)

def load_newest_run_matching(compare_to, d=".", enforce_subtree=True):
    rs = load_runs_in_dir(d)
    if len(rs) == 0:
        raise IOError("no %s files in %s" % (run_ext, d))
    wd = [(r["timestamp"], rfn, r) for rfn, r in rs if dc.is_subtree(compare_to, r)]
    if len(wd) == 0:
        raise IOError("no run found of which the requested is a subtree")
    wd.sort(key=lambda x: x[0])
    t, rfn, r = wd[-1]
    return rfn, r