# -*- coding: utf-8 -*-
import os
import sys
import csv
import numpy
import scipy

from subprocess import STDOUT, check_output, CalledProcessError

from rmtk.vulnerability.common import utils

# DV: this file has too many problems, I give up

def calculate_fragility(capacity_curves, hysteresis, msa, gmrs,gmr_characteristics,damage_model,damping,degradation,number_models_in_DS):
    
    no_damage_states = len(damage_model['damage_states'])
    no_bins = msa['n. bins']
    no_rec_bin = msa['records per bin']
    no_gmrs = no_bins*no_rec_bin
    no_capacity_curves = len(capacity_curves['Sd'])
    
    # dictionary to allocate damage states for all the capacity curves for all the levels of pre-damage
    PDMs, Sds, gmr_info = {},{},{}
    for iDS in range(1,no_damage_states):
        PDMs['iDS'+str(iDS)] = numpy.zeros((no_bins,no_damage_states+1))
        Sds['iDS'+ str(iDS)] = []
        gmr_info['iDS'+ str(iDS)] = []
        
    for icc in range(no_capacity_curves):
        print str((icc+1)*100/no_capacity_curves) + '%'
        # run first Multiple Stripe Analysis            
        run_NLTHA(icc, capacity_curves, hysteresis, gmrs, gmr_characteristics,
                  damage_model, damping, degradation, number_models_in_DS, msa)
        # run second Multiple Stripe Analysis
        PDMs, Sds, gmr_info = run_2MSA(icc,capacity_curves, hysteresis, 
                                       msa,damage_model, gmrs, damping,degradation,
                                       PDMs, Sds, gmr_info,gmr_characteristics)
        
    return PDMs, Sds, gmr_info

def run_NLTHA(icc,capacity_curves,hysteresis,gmrs,gmr_characteristics,damage_model,damping,degradation,number_models_in_DS,msa):
    
    # This function runs a set of NLTHA and writes for each 
    # Damage States a file with the list of records causing the structure to 
    # fall in the given DS. The analyses stop when the number_models_in_DS is 
    # reached for each DS.
    
    no_damage_states = len(damage_model['damage_states'])
    no_gmrs = len(gmrs['time'])
    no_rec_bin = msa['records per bin']
    
    # remove files containing results of previous analyses if they exist
    for iDS in range(0,no_damage_states+1):
        try:
            os.remove('initial_DS%d.csv' %iDS)
        except OSError:
            pass
        
    counter = 0
    # we are running all the gmrs and counter is the variable that records 
    # the count of gmrs already run
    PDM1 = numpy.zeros((no_gmrs,no_damage_states+1))
    store_DS = []
    for iDS in range(0,no_damage_states):
    # in this loop are excluded DS=0 and DS=collapse because they are 
    # not of interest for fragility of damaged buildings
        for igmr in range(counter,no_gmrs):
            limit_states = utils.define_limit_states(capacity_curves,icc,damage_model)
            counter = igmr+1
            target_name = gmrs['name'][igmr]
            available_aftershocks = filter_M_T(target_name,gmr_characteristics)
            if available_aftershocks >= no_rec_bin:
                time, disps = run_time_history_analysis(capacity_curves, hysteresis, icc,gmrs,target_name,1,damping,degradation)
                Sdi = max(numpy.abs(numpy.array(disps)))
                PDM1, ds = utils.allocate_damage(igmr,PDM1,Sdi,limit_states)
                store_DS.append([gmrs['name'][igmr], ds, 1])
            # if maximum number of models in DS is achieved stop
            if sum(PDM1[:,iDS]) >= number_models_in_DS:
                break
            
        # This section works in case not enough records per DS are found
        if sum(PDM1[:,iDS]) < number_models_in_DS and iDS != 0:
            print "WARNING: there are not enough strong ground motions in DS ", str(iDS),", scaling is required"
            for previous_DS in numpy.arange(iDS,0,-1):
                # finding gmrs leading to iDS-1 and applying scaling factor to them
                temp = numpy.array(store_DS)
                list_of_models = temp[scipy.logical_and(temp[:,1] == str(iDS-1),temp[:,2] == str(1))] # names of the ground motion for the previous iDS
                for scaling_factor in numpy.linspace(1.2,3,7):       
                    for ele in list_of_models:
                        limit_states = utils.define_limit_states(capacity_curves,icc,damage_model)
                        gmr_name = ele[0]                        
                        igmr = gmrs['name'].index(gmr_name)
                        # mainshock Mw and Tg are compared to all aftershock Mw and Tg
                        available_aftershocks = filter_M_T(gmr_name,gmr_characteristics)
                        if available_aftershocks < no_rec_bin:
                            index = numpy.where(list_of_models[:,0] == ele[0])
                            list_of_models = numpy.delete(list_of_models, (index), axis=0)
                        else:
                            time, disps = run_time_history_analysis(capacity_curves, hysteresis, icc,gmrs,gmr_name,scaling_factor,damping,degradation)              
                            Sdi = max(numpy.abs(numpy.array(disps)))
                            PDM1, ds = utils.allocate_damage(igmr,PDM1,Sdi,limit_states)
                            store_DS.append([gmr_name, ds, scaling_factor])
                            if ds >= iDS:
                                index = numpy.where(list_of_models[:,0] == ele[0])
                                list_of_models = numpy.delete(list_of_models, (index), axis=0)
                                
                        if sum(PDM1[:,iDS]) >= number_models_in_DS: break
                    if sum(PDM1[:,iDS]) >= number_models_in_DS: break
                if sum(PDM1[:,iDS]) >= number_models_in_DS: break
            if sum(PDM1[:,iDS]) < number_models_in_DS:
                print "WARNING: not enough models in DS%d" %iDS 

        # Store names of records and corresponding DS achieved so far            
        save_DS_list(iDS,store_DS, limit_states)

    
def run_2MSA(icc,capacity_curves,hysteresis,msa,damage_model,gmrs,damping,degradation,PDMs, Sds, gmr_info, gmr_characteristics):
    
    no_damage_states = len(damage_model['damage_states'])
    no_bins = msa['n. bins']
    no_rec_bin = msa['records per bin']
    no_gmrs = no_bins*no_rec_bin
    filter_aftershock = msa['filter']
    Mw_multiplier = gmr_characteristics[-1]
    
    counter = 0
    for iDS in range(1,no_damage_states):
        
        PDM = PDMs['iDS'+str(iDS)]
        # Open files with records name bringing the system to DS = iDS         
        with open('initial_DS%d.csv' %iDS, 'rU') as f:
            gmr_list = [row for row in csv.reader(f)]
            
        for index_gmr1, gmr_name in enumerate(gmr_list):
            print "Initial DS=", iDS
            igmr1 = gmrs['name'].index(gmr_name[0]) # index for 1st gmr within gmrs list
            scl1 = float(gmr_name[1]) #scaling factor for first ground motion
            [M_m, Tg_m, Mw, Tg] = find_M_T(gmr_name[0], gmr_characteristics)
            
            # Read files with records name and corresponding scaling factor for each target IM
            folder_scaled_recs = msa['input folder']
            target_files = os.listdir(folder_scaled_recs)
            for ele in target_files:
            # Remove files that are not csv
                if ele[-3:] != 'csv':
                    target_files.remove(ele)
                    
            for index,ibin in enumerate(target_files):
                if index>=no_bins: break
                print ibin
                
                with open(folder_scaled_recs+'/'+ibin, 'rb') as f:
                    reader = csv.reader(f)
                    newlist = [row for row in reader]
                record_name = [ele[0] for ele in newlist]
                record_scaling = [float(ele[1]) for ele in newlist]
                
                # Run NLTHA with 1st gmr to bring the system to iDS and 2nd gmr corresponding to target IM
                for igmr in range(len(record_name)):
                    limit_states = utils.define_limit_states(capacity_curves,icc,damage_model)
                    target_name = record_name[igmr]
                    scl2 = record_scaling[igmr]
                    [M_a, Tg_a, Mw, Tg] = find_M_T(target_name, gmr_characteristics)
                    if scipy.logical_and(M_a<M_m*Mw_multiplier, Tg_a<Tg_m) or filter_aftershock=='FALSE':
                        igmr2 = gmrs['name'].index(target_name) # index of 2nd gmr within the gmrs list
                        time, disps = run_2time_history_analyses(capacity_curves,hysteresis,icc,gmrs,igmr1,igmr2,scl1,scl2,damping,degradation)
                        Sdi = max(numpy.abs(numpy.array(disps)))
                        # In PDM the number of models in each final damage state corresponding 
                        # to the same IM bin (defined by the variable index) are summed up                     
                        PDM, ds = utils.allocate_damage(index,PDM,Sdi,limit_states)
                        gmr_info['iDS' + str(iDS)].append([igmr2, scl2])
                        Sds['iDS' + str(iDS)].append(Sdi)
                        counter = counter+1
                        print "gmr n.", counter, "max disp. =", Sdi, "DS =", ds
                    if sum(PDM[index,:]) >= no_rec_bin*(index_gmr1+1)*(icc+1): break
                        
        PDMs['iDS' + str(iDS)] = PDM
            
    return PDMs, Sds, gmr_info
    
def save_DS_list(iDS,store_DS,limit_states):
# This function saves in a csv file the names of records leading to each DS

    # open existing file containing names of grms leading to each 
    # DS and continue writing
    out_file = open(os.path.join('initial_DS%d.csv' %iDS), 'a')
    for row in store_DS:
        if row[1] == iDS:
            out_file.write('%s,' %row[0])
            out_file.write('%f\n' %row[2])
    out_file.close()

def run_time_history_analysis(capacity_curves, hysteresis, icc,gmrs,target_name,scaling_factor,damping,degradation):
    
    igmr = gmrs['name'].index(target_name)
    write_gmr(gmrs,igmr, scaling_factor)
    create_SDOF_paramfile(capacity_curves, hysteresis, icc,damping,degradation)
    tcl = 'simplestSDOFgen.tcl'

    try:
        if 'darwin' in sys.platform:
            check_output([os.path.join(bin_directory,
                         "OpenSees.mac"), tcl],
                         cwd=package_directory, stderr=STDOUT)
        elif 'linux' in sys.platform:
            check_output([os.path.join(bin_directory,
                         "OpenSees.u1604"), tcl],
                         cwd=package_directory, stderr=STDOUT)
        else:
            sys.exit('Your OS is not currently support')
    except CalledProcessError as e:
        print(e.output)
        raise

    disps = []
    time = []
    disp_file = open('NodeD.out')
    lines = disp_file.readlines()
    for line in lines:
        values = line.strip().split(' ')
        time.append(float(values[0]))
        disps.append(float(values[1]))
        
    return time, disps
    
def run_2time_history_analyses(capacity_curves, hysteresis,icc,gmrs,igmr1,igmr2,scaling_factor1,scaling_factor2,damping,degradation):
    
    write_double_gmr(gmrs,igmr1,igmr2,scaling_factor1,scaling_factor2)
    
    create_SDOF_paramfile(capacity_curves,hysteresis,icc,damping,degradation)
    os.system('./OpenSees simplestSDOFgen.tcl')
    
    disps = []
    time = []
    disp_file = open('NodeD.out')
    lines = disp_file.readlines()
        
    for line in lines[gmrs['no_points'][igmr1]+2000:]:
        values = line.strip().split(' ')
        time.append(float(values[0]))
        disps.append(float(values[1]))

    return time, disps
    
def write_gmr(gmrs,igmr,scaling_factor):
    
    out_file = open('gmr.txt','w')
    out_file.write(str(gmrs['no_points'][igmr])+'\n')
    out_file.write(str(gmrs['dt'][igmr])+'\n')
    for iacc in range(gmrs['no_points'][igmr]):
        out_file.write(str(gmrs['acc'][igmr][iacc]*scaling_factor)+'\n')
    out_file.close()
    
def write_double_gmr(gmrs,igmr1,igmr2,scaling_factor1,scaling_factor2):
    
    # Harmonise two records
    # change dt in second series if different from previous dt
    if gmrs['dt'][igmr2] != gmrs['dt'][igmr1]:
        time2 = numpy.arange(gmrs['dt'][igmr1],gmrs['time'][igmr2][-1]+gmrs['dt'][igmr1],gmrs['dt'][igmr1])
        gmrs['acc'][igmr2] = numpy.interp(time2,gmrs['time'][igmr2],gmrs['acc'][igmr2])
        gmrs['dt'][igmr2] = gmrs['dt'][igmr1]
        gmrs['time'][igmr2] = time2
        gmrs['no_points'][igmr2] = len(gmrs['acc'][igmr2])
    tot_n_points = gmrs['no_points'][igmr1]+2000+gmrs['no_points'][igmr2]
    
    # first acceleration series
    out_file = open("gmr.txt",'w')
    out_file.write(str(tot_n_points)+'\n')
    out_file.write(str(gmrs['dt'][igmr1])+'\n')
    for iacc in range(gmrs['no_points'][igmr1]):
        out_file.write(str(gmrs['acc'][igmr1][iacc]*scaling_factor1)+'\n')     
    # 20 secs of acc=0 to make the structure come to rest before the aftershock
    for iacc in range(2000):  
        out_file.write(str(0.0)+'\n')
    # second acceleration series
    for iacc in range(gmrs['no_points'][igmr2]):
        out_file.write(str(gmrs['acc'][igmr2][iacc]*scaling_factor2)+'\n')
    out_file.close()
    
def create_SDOF_paramfile(capacity_curves, hysteresis, icc,damping,degradation):

    d_flag = 0
    if degradation:
        d_flag = 1
        
    out_file = open('SDOF_paramfile.tcl','w')
    out_file.write('set material "pinchgeneric" \n')
    f1 = capacity_curves['Sa'][icc][1]*9.81
    f2 = capacity_curves['Sa'][icc][2]*9.81
    f3 = capacity_curves['Sa'][icc][3]*9.81
    f4 = capacity_curves['Sa'][icc][4]*9.81
    d1 = capacity_curves['Sd'][icc][1]
    d2 = capacity_curves['Sd'][icc][2]
    d3 = capacity_curves['Sd'][icc][3]
    d4 = capacity_curves['Sd'][icc][4]
    d5 = capacity_curves['Sd'][icc][4]
    #-capacity_curves['Sd'][icc][1]
    out_file.write('set fi {%f %f %f %f 0.0} \n' %(f1,f2,f3,f4)) 
    out_file.write('set di {%f %f %f %f %f} \n' %(d1,d2,d3,d4,d5))
    out_file.write('set ksi %f \n' %damping)
    out_file.write('set Tperiod "0.0" \n')
    out_file.write('set mx "1.0" \n')
    out_file.write('set degflag "%d" \n' %d_flag)
    out_file.write('set analysistype "dynamic" \n')
    out_file.write('set pinchingtype "%s" \n' %hysteresis['type'])
    
    if hysteresis['type'] == 'user-defined':
        out_file.write('set rDisp %f \n' %hysteresis['rDisp'][0])
        out_file.write('set fForce %f \n' %hysteresis['fForce'][0])
        out_file.write('set uForce %f \n' %hysteresis['uForce'][0])
        gK1, gK2, gK3, gK4, gKLim = hysteresis['gK'][0] 
        gD1, gD2, gD3, gD4, gDLim = hysteresis['gD'][0]
        gF1, gF2, gF3, gF4, gFLim = hysteresis['gF'][0] 
        out_file.write('set gammaK [list %f %f %f %f %f]\n' %(gK1,gK2,gK3,gK4,gKLim))
        out_file.write('set gammaD [list %f %f %f %f %f]\n' %(gD1,gD2,gD3,gD4,gDLim))
        out_file.write('set gammaF [list %f %f %f %f %f]\n' %(gF1,gF2,gF3,gF4,gFLim))
        out_file.write('set gammaE %f \n' %hysteresis['gE'][0])
        out_file.write('set damage "%s"\n' %hysteresis['dmgType'][0])
        
    out_file.close()

def filter_M_T(gmr_name, gmr_characteristics):

    if gmr_characteristics[0] == 'None':
        # gmr_characteristics has not been defined because filtering of aftershocks is not of interest
        return 'Inf'
    else:
        Mw_multiplier = gmr_characteristics[-1]
        [M_m, Tg_m, Mw, Tg] = find_M_T(gmr_name, gmr_characteristics)
        available_aftershocks = len(Mw[scipy.logical_and(Mw<M_m*Mw_multiplier, Tg<Tg_m)])
        return available_aftershocks
    
def find_M_T(gmr_name, gmr_characteristics):
    
    if gmr_characteristics[0] == 'None':
        # gmr_characteristics has not been defined because filtering of aftershocks is not of interest
        return [0, 0, 0, 0]
    else:
        Mw = numpy.array(gmr_characteristics[1])
        Tg = numpy.array(gmr_characteristics[2])
        gmr_list = numpy.array(gmr_characteristics[0])
        M_r = Mw[ gmr_list == gmr_name]
        Tg_r = Tg[ gmr_list == gmr_name]
        
        return [M_r, Tg_r, Mw, Tg]
    
