import os
import numpy as np
import networkx as nx
import settings


def getMethodsPredefinedSets(key):
    if 'enron' in key:
        snapshots_network_construction_list = ['snapshots_1month_1','snapshots_2month_1','snapshots_3month_1']
    else:
        snapshots_network_construction_list = ['snapshots_5_1']#,'snapshots_5_1']
    aggregated_network_construction_list = ['aggregated_1']
    methods = []
    if 'key_aging' in key:
            methods = ['aggregated_1_sgdvs_3','aggregated_1_sgdvs_4','aggregated_1_sgdvs_5',#'stgdvs_3','stgdvs_4','stgdvs_5',
                       'dgdvs_3_3_1','dcgdvs_3_3_1','dgdvs_6_4_1','dcgdvs_6_4_1']
    elif 's1' in key:
         for n in [3,4,5]:
            for snapshots_network_construction in snapshots_network_construction_list:
                methods.append(snapshots_network_construction + '_stgdvs_%s' % n)
            for aggregated_network_construction in aggregated_network_construction_list:
                methods.append(aggregated_network_construction + '_sgdvs_%s' % n)
    elif key == 'key_d1':
        for d_type in ['d','dc']:
            for k in [3,4,5,6,7]:
                for n in [3,4]:
                    if k!=6 and n>3:
                        continue
                    for snapshots_network_construction in snapshots_network_construction_list:
                        methods.append(snapshots_network_construction + '_%sgdvs_%s_%s_1' % (d_type,k,n))
    elif 'd2' in key:
        for d_type in ['d','dc']:
            for (k,n) in [(3,3),(5,3),(7,3),(4,4),(6,4)]:
                    for snapshots_network_construction in snapshots_network_construction_list:
                        methods.append(snapshots_network_construction + '_%sgdvs_%s_%s_1' % (d_type,k,n))
    elif key == 'key_10a':
         for d_type in ['d','dc']:
            for k in [3,4,5,6,7]:
                for n in [3,4]:
                    if (k>6 and n>3) or (d_type == 'd' and n==4):
                        continue
                    for snapshots_network_construction in ['snapshots_10_1_acc']:
                        methods.append(snapshots_network_construction + '_%sgdvs_%s_%s_1' % (d_type,k,n))
    elif 'enron_large' in key:
          for d_type in ['d','dc']:
            for k in [3,4,5,6,7]:
                for n in [3,4]:
                    if k<n-1:
                        continue
                    for snapshots_network_construction in snapshots_network_construction_list:
                        methods.append(snapshots_network_construction + '_%sgdvs_%s_%s_1' % (d_type,k,n))
         # for n in [3,4,5]:
         #    for snapshots_network_construction in ['snapshots_10_1_acc']:
         #        methods.append(snapshots_network_construction + '_stgdvs_%s' % n)

    return methods

def getNodeNames(network_name, input_sub_dir = '',min_degree = None):
    G = nx.read_edgelist(settings.BASE_INPUT_DIR + input_sub_dir + '%s/aggregated/%s_aggregated_1.txt' % (network_name,network_name))
    if min_degree is not None:
        return [n for n in G if G.degree(n)>=min_degree]
    else:
        return [n for n in G]

def getMethodGraphletCountsFile(networkName,methodName, networkSubDir = ''):
    path =  settings.BASE_OUTPUT_DIR
    if networkSubDir != '':
        path += networkSubDir + '/'
    path += networkName + '/counts/'

    m_dict = getMethodDetailsDict(methodName)

    if m_dict['type'] == 's':
        path += 'static/'
    elif m_dict['type'] == 'st':
        path += 'static-temporal/'
        if m_dict['network_construction'] != '':
            path += m_dict['network_construction'] + '/'


    path += '%s_%s.txt' % (networkName,methodName.replace('gdvs','gcounts'))
    #print 'path: '+path
    return path


def getMethodGdvFile(networkName,methodName, networkSubDir = '', similarities = False):
    path =  settings.BASE_OUTPUT_DIR
    if networkSubDir != '':
        path += networkSubDir + '/'
    path += networkName + '/counts/'

    m_dict = getMethodDetailsDict(methodName)

    if m_dict['type'] == 's':
        path += 'static/'
    elif m_dict['type'] == 'st':
        path += 'static-temporal/'
        if m_dict['network_construction'] != '':
            path += m_dict['network_construction'] + '/'

    path += '%s_%s.txt' % (networkName,methodName.replace('gdvs','gdv') if not similarities else methodName)
    #print 'path: '+path
    return path


def getMethodGdvsMatrix(networkName,methodName, networkSubDir = '', as_a_distance_matrix = False, as_a_list = False, sort_list = False):
    with open(getMethodGdvFile(networkName, methodName, networkSubDir, similarities=True), 'r') as f:
        first_line = f.readline()
        node_names = first_line.split()
        if node_names[0] == '#':
            del node_names[0]

        num_cols = len(node_names)
        f.seek(0)

        data = np.genfromtxt(f, skip_header=1, usecols = range(1,num_cols+1))

    if as_a_distance_matrix:
        data = 1 - data

    if as_a_list:
        object_distances = np.zeros((len(node_names) * (len(node_names)-1))/2, dtype=('f8,a30,a30'))
        k=0
        for i in xrange(0,len(node_names)-1):
            for j in xrange(i+1,len(node_names)):
                object_distances[k] = (data[i,j],node_names[i],node_names[j])
                k+=1
        if sort_list:
            object_distances = object_distances.sort(axis = 0)
        return object_distances, node_names

    return data, node_names


def getMethodDetailsDict(method_string):
    #print 'method string: ' + method_string
    record = {}
    record['full_name'] = method_string
    record['type'] =  getMethodType(method_string)
   
    if not ('_%sgdvs' %  record['type'] ) in method_string:
        record['network_construction'] = ''
    else:
        record['network_construction'] =  method_string[:method_string.find('_%sgdvs' %  record['type'] )]

    method_params = method_string[method_string.find('%sgdvs_' %  record['type'] ) + len('%sgdvs_' %  record['type']):]

    if record['type'].startswith('s'):#static or static-temporal
        record['n'] = int(method_params)
    else:
        values = method_params.split('_')

        record['k'] = int(values[0])
        record['n'] = int(values[1])
        record['delta_t'] = int(values[2])

    #print 'record [con]: '+record['network_construction']
    #print 'record method param: '+method_params
    return record


def loadGraphletCounts(gcountsFile, two_columns = False, delimiter = '\t'):
    """
    Load graphlet counts
    :param gcountsFile:
    :return:
    """
    if not two_columns:
        return [int(l) for l in open(gcountsFile,'r').readlines()]
    else:
        return [int(l.split(delimiter)[1]) for l in open(gcountsFile,'r').readlines()]


def loadGdvFile(gdvFile):
    node_names = []
    gdv_list = []
    with open(gdvFile, 'r') as f:
        for line in f.readlines():
            values = line.replace('\n','').split(' ')
            node_names.append(values[0])
            gdv_list.append([int(v) for v in values[1:] if v !=''])

    return node_names, gdv_list


def getMethodType(method):
    if 'stgdvs' in method:
       return 'st'
    if 'dgdvs' in method:
        return 'd'
    elif 'dzgdvs' in method:
        return 'dz'
    elif 'dcgdvs' in method:
        return 'dc'
    elif 'dczgdvs' in method:
        return 'dcz'
    elif 'dsgdvs' in method:
        return 'ds'
    elif 'dcsgdvs' in method:
        return 'dcs'
    elif 'sgdvs' in method:
        return 's'
    return None

def getMethodTypeDescr(method_type):
    if method_type == 's':
        return 'Static'
    elif method_type == 'st':
        return 'Static-temporal'
    elif method_type == 'd':
        return 'Dynamic'
    elif method_type == 'dc':
        return 'Constrained dynamic'
    elif method_type == 'ds':
        return 'Dynamic static'
    elif method_type == 'dcs':
        return 'Constrained dynamic  static'

def getMethodTypeInt(method):

    if 's' == method:
        return 1
    elif 'st' == method:
       return 2
    if 'd' == method:
        return 3
    elif 'ds' == method:
        return 4
    elif 'dc' == method:
        return 5
    elif 'dcs' == method:
        return 6
    return None

def getAllMethodTypes():
    return ['s','st','d', 'dz','dc', 'dcz'];

def compareMethodTypes(methodType1,methodType2):
    methodTypeInt1 = getMethodTypeInt(methodType1)
    methodTypeInt2 = getMethodTypeInt(methodType2)
    return -1 if methodTypeInt1 < methodTypeInt2 else 0 if methodTypeInt1 == methodTypeInt2 else 1

def compareMethodNames(method1,method2):

    if method1.find('random') == 0:
        if method2.find('random') == 0:
            return -1 if method1 < method2 else 0 if method1 == method2 else 1
        else:
            return 1
    else:
        if method2.find('random') == 0:
            return -1


    methodTypeInt1 = getMethodTypeInt(getMethodType(method1))
    methodTypeInt2 = getMethodTypeInt(getMethodType(method2))
   # print 'method1: %s || method2: %s' %(method1,method2)
    m1Details = getMethodDetailsDict(method1)
    m2Details = getMethodDetailsDict(method2)

    if methodTypeInt1 == methodTypeInt2:

        if m1Details['n'] != m2Details['n']:
            return -1 if m1Details['n']<m2Details['n'] else 1
        if m1Details.has_key('k') and m1Details['k'] != m2Details['k']:
            return -1 if m1Details['k']<m2Details['k'] else 1
        if 'aggregated_10' in m1Details['network_construction'] and not 'aggregated_10' in m2Details['network_construction']:
            return 1
        if 'aggregated_10' in m2Details['network_construction'] and not 'aggregated_10' in m1Details['network_construction']:
            return -1
        return -1 if method1 < method2 else 0 if method1 == method2 else 1
    else:
        return -1 if methodTypeInt1 < methodTypeInt2 else 1

def getMethodName(method, onlyType = False):

    networkConstructionType = None

    if 'stgdvs_' in method:
        methodAbbrevation = 'stgdvs'
        methodVerboseName = 'Static-temporal'
    elif 'dgdvs_' in method:
        methodAbbrevation = 'dgdvs'
        methodVerboseName = 'Dynamic'
    elif 'dcgdvs_' in method:
        methodAbbrevation = 'dcgdvs'
        methodVerboseName = 'Constrained dynamic'
    elif 'dsgdvs_' in method:
        methodAbbrevation = 'dsgdvs'
        methodVerboseName = 'Dynamic static'
    elif 'dcsgdvs_' in method:
        methodAbbrevation = 'dcsgdvs'
        methodVerboseName = 'Constrained dynamic static'
    elif 'sgdvs_' in method:
        methodAbbrevation = 'sgdvs'
        methodVerboseName = 'Static'
    else:
        raise Exception('Invalid method: %s' % method)

    if method.find(methodAbbrevation) != 0:
        networkConstructionType = method[:method.find(methodAbbrevation)-1].replace('_','-')

    name = methodVerboseName

   # print 'methodName-- %s || networkConstro-- %s' %(methodName,networkConstructionType)
   # print 'networkCons: '+networkConstructionType
    if onlyType:
        return name

    name += ' (%s)' % method[method.find(methodAbbrevation) + len(methodAbbrevation) + 1:].replace('_','-')

    if networkConstructionType is not None:
        name += ', ' + networkConstructionType

    return name

def readMethodsDirRecursively(input_dir, networkName, pattern, ignorePattern = None):
    res_methods = []
    for root, subFolders, files in os.walk(input_dir):
        for f in files:
            if not os.path.exists(os.path.join(input_dir,f)):
                #print 'Path not exists: ' + os.path.join(input_dir,f)
                continue
            if ignorePattern is not None and ignorePattern in f:
                print 'Ignoring %s' % f
                continue
            if os.path.getsize(os.path.join(input_dir,f)) == 0:
                continue
            if pattern in f:
                res_methods.append(f[f.find(networkName)+len(networkName)+1:f.find('.txt')])
        for subDir in subFolders:
            res_methods += readMethodsDirRecursively(os.path.join(input_dir,subDir + '/'), networkName, pattern)
    return res_methods

def getGdvsMethods(networkName, inputSubDir= '', only_with_gdvs = False):
    """

    :param networkName:
    :param inputSubDir:
    :param with_gdvs: Only methods that have gdvs (otherwise just having gcounts is enough)
    :return:
    """
    res_methods = []
    ignorePattern  = 'dist_matrix'
    inputDir = settings.BASE_OUTPUT_DIR + inputSubDir + networkName + '/counts/'
    for f in os.listdir(inputDir):
        if ignorePattern in f:
            print 'Ignoring %s' % f
            continue
        if os.path.getsize(inputDir + f) == 0:
            continue
        if 'dcgdv_4_5' in f or 'dcgdv_5_6' in f or 'gdv_5_3' in f or 'gdv_8_3' in f or 'gdv_7_4' in f or 'gdv_3_4' in f or 'gdv_5_4' in f or 'gdv_6_5' in f:#temporary
            continue
        if (only_with_gdvs and ('_dgdvs_' in f or '_dzgdvs_' in f or '_dcgdvs_' in f or '_dczgdvs_' in f) and (not 'time' in f and not 'dist_matrix' in f)) or (not only_with_gdvs and ('_dgdv_' in f or '_dcgdv_' in f  or 'gcounts_' in f)):
            res_methods.append(f[f.find(networkName)+len(networkName)+1:f.find('.txt')])

    if os.path.exists(inputDir + 'static/'):
        for f in os.listdir(inputDir + 'static/'):
            if  ignorePattern in f:
                print 'Ignoring %s' % f
                continue
            # if os.path.getsize(inputDir + 'static/' + f) == 0:
            #     continue
            if not 'aggregated_1' in f:#temporary
                continue
            if (only_with_gdvs and '_sgdvs_' in f  and (not 'time' in f and not 'dist_matrix' in f)) or (not only_with_gdvs and 'gcounts_' in f):
                  res_methods.append(f[f.find(networkName)+len(networkName)+1:f.find('.txt')])

    if os.path.exists(inputDir + 'static-temporal/'):
         res_methods += readMethodsDirRecursively(inputDir + 'static-temporal/', networkName, 'stgdvs' if only_with_gdvs else 'stgcounts', ignorePattern=ignorePattern)

    if not only_with_gdvs:
        res_methods = [m.replace('gcounts','gdvs').replace('gdv_','gdvs_') for m in res_methods]

    return sorted(list(set(res_methods)), cmp = compareMethodNames)


def getPrRocMethods(networkName, inputSubDir= '', enrichment_analysis_method_type = ''):
    res_methods = []
    inputDir = settings.BASE_OUTPUT_DIR + inputSubDir + networkName + '/enrichment/' + enrichment_analysis_method_type + '/'
    for f in os.listdir(inputDir):
        if not('sgdvs_' in f or 'stgdvs_' in f or 'dgdvs_' in f or 'dzgdvs_' in f or 'dcgdvs_' in f or 'dczgdvs_' in f):
            continue
        if not '_pr_roc' in f:
            continue

        #to fix old notation
        if '_pr_roc_' in f:
            f_new = f.replace('_pr_roc_','_').replace('.txt','_pr_roc.txt')
            os.rename(inputDir + f, inputDir + f_new)
            f = f_new

        if 'random' in f:
            continue

        res_methods.append(f[f.find(networkName)+len(networkName)+1:f.find('_pr_roc.txt')])

    return sorted(res_methods, cmp = compareMethodNames)


def getPcaAnalysisPrRocMethods(analysis_type, input_sub_dir, distance_measure, ignoring_ground_truth_pairs = False):
    res_methods = []

    inputDir = settings.BASE_OUTPUT_DIR
    if analysis_type == 'model':
        inputDir += 'MODEL_ANALYSIS/%s/' % (input_sub_dir)
    else:
        inputDir += '%s/pca/%s/' % (analysis_type, input_sub_dir)
    inputDir += '%s_distances%s/' % (distance_measure, '' if not ignoring_ground_truth_pairs else '_ignoring')

    for f in os.listdir(inputDir):
        if not('sgdvs_' in f or 'stgdvs_' in f or 'dgdvs_' in f or 'dzgdvs_' in f or 'dcgdvs_' in f or 'dczgdvs_' in f):
            continue
        if 'random' in f:
            continue
        if not '_pr_roc.txt' in f:
            continue

        res_methods.append(f[:f.find('_pr_roc.txt')])

    return sorted(res_methods, cmp = compareMethodNames)


