import os
import numpy
import csv
import sys
from .helper import getOSIndependentFilePath


class LP():
    def __init__(self): #initialise the LP data
        self.chan_list = [] #list of channel IDs
        self.chan_index = []  # list of index values in the ChanInfo class
        self.node_list = []
        self.node_index = []
        self.node_bed = []
        self.node_top = []
        self.H_nd_index = []
        self.dist_nodes = []
        self.dist_chan_inverts = []
        self.dist_inverts= []
        self.Hmax = []
        self.Hdata = []
        self.chan_inv = []
        self.chan_LB = []
        self.chan_RB = []
        self.pit_dist = []
        self.pit_z = []
        self.npits = int(0)
        self.connected = False
        self.static = False

class Data1D():
    def __init__(self): #initialise the 1D data
        self.nNode = 0
        self.nChan = 0
        self.H = None
        self.V = None
        self.Q = None
        self.message = []

    def findData(self, ID, domain, geom, dat_type):
        # see if the data exists in the file
        try:
            indA = []
            indB = []
            indC = []
            indD = []
            for i, id in enumerate(self.ID): # have to enumerate rather than index as index only returns a single entry there could be the same ID in 1D and 2D
                if id == ID:
                    indA.append(i)
            if len(indA)>0: #ID found - check that 1D/2D is correct
                for ind in indA:
                    if self.domain[ind]==domain:
                        indB.append(ind)
            if len(indB)>0: #id and domain match
                for ind in indB:
                    if self.geom[ind]==geom:
                        indC.append(ind)
            if len(indC)>0: #id, domain and geom match
                for ind in indC:
                    if (self.dat_type[ind].find(dat_type)>=0):
                        indD.append(ind)
            if len(indD)==1:
                #data found
                return True, indD
            elif len(indD)>1:
                self.message.append('WARNING - More than 1 matching dataset - using 1st occurence.')
                return True, indD[0]
            else:
                return False, 0
        except:
            self.message.append('WARNING - Unknown exception finding data in res.find_data().')
            return False, -99  # error shouldn't really be here

class Timeseries():
    """
    Timeseries - used for both 1D and 2D data
    """
    
    def __init__(self,fullpath,prefix, simID):
        self.loaded = False
        self.message = []
        try:
            with open(fullpath, 'r') as csvfile:
                reader = csv.reader(csvfile, delimiter=',', quotechar='"')
                header = next(reader)
            csvfile.close()
        except:
            self.message.append("ERROR - Error reading header from: " + fullpath)
        header[0]='Timestep'
        header[1]='Time'
        self.ID = []
        i=1
        for col in header[2:]:
            i= i+1
            a = col[len(prefix)+1:]
            indA = a.find(simID)
            indB = a.rfind('[') #find last occurrence of [
            if (indA >= 0) and (indB >= 0): # strip simulation ID from header
                a = a[0:indB-1]
            self.ID.append(a)
            header [i] = a
        self.Header = header
        try:
            self.Values = numpy.genfromtxt(fullpath, delimiter=",", skip_header=1)
            self.loaded = True
        except:
            self.message.append("ERROR - Error reading data from: " + fullpath)

        self.nVals = len(self.Values[:,2])
        self.nLocs = len(self.Header)-2
        
        
class NodeInfo():
    """
    Node Info data class
    """
    def __init__(self, fullpath):
        self.node_num = []
        self.node_name = []
        self.node_bed = []
        self.node_top = []
        self.node_nChan = []
        self.node_channels = []
        self.message = []
        with open(fullpath, 'r') as csvfile:
            reader = csv.reader(csvfile, delimiter=',', quotechar='"')
            header = next(reader)
            for (counter, row) in enumerate(reader):
                self.node_num.append(int(row[0]))
                self.node_name.append(row[1])
                self.node_bed.append(float(row[2]))
                self.node_top.append(float(row[3]))
                self.node_nChan.append(int(row[4]))
                chan_list = row[5:]
                if len(chan_list) != int(row[4]):
                    if int(row[4]) != 0:
                        self.message.append("ERROR - Number of channels connected to ID doesn't match. ID: " + str(row[1]))
                else:
                    self.node_channels.append(chan_list)
        csvfile.close()

class ChanInfo():
    """
    Channel Info data class
    """
    def __init__(self, fullpath):
        self.chan_num = []
        self.chan_name = []
        self.chan_US_Node = []
        self.chan_DS_Node = []
        self.chan_US_Chan = []
        self.chan_DS_Chan = []
        self.chan_Flags = []
        self.chan_Length = []
        self.chan_FormLoss = []
        self.chan_n = []
        self.chan_slope = []
        self.chan_US_Inv = []
        self.chan_DS_Inv = []
        self.chan_LBUS_Obv = []
        self.chan_RBUS_Obv = []
        self.chan_LBDS_Obv = []
        self.chan_RBDS_Obv = []
        self.chan_Blockage = []

        with open(fullpath, 'r') as csvfile:
            reader = csv.reader(csvfile, delimiter=',', quotechar='"')
            header = next(reader)
            for (counter, row) in enumerate(reader):
                self.chan_num.append(int(row[0]))
                self.chan_name.append(row[1])
                self.chan_US_Node.append(row[2])
                self.chan_DS_Node.append(row[3])
                self.chan_US_Chan.append(row[4])
                self.chan_DS_Chan.append(row[5])
                self.chan_Flags.append(row[6])
                self.chan_Length.append(float(row[7]))
                self.chan_FormLoss.append(float(row[8]))
                self.chan_n.append(float(row[9]))
                self.chan_slope.append(float(row[10]))
                self.chan_US_Inv.append(float(row[11]))
                self.chan_DS_Inv.append(float(row[12]))
                self.chan_LBUS_Obv.append(float(row[13]))
                self.chan_RBUS_Obv.append(float(row[14]))
                self.chan_LBDS_Obv.append(float(row[15]))
                self.chan_RBDS_Obv.append(float(row[16]))
                self.chan_Blockage.append(float(row[17]))
        self.nChan = counter + 1
        csvfile.close()

# results class
class ResData():
    """
    ResData class for reading and processing results
    """
    
    def __init__(self):
        self.filename = None
        self.fpath = None
        self.nTypes = 0
        self.Types = []
        self.LP = LP()
        self.Data_1D = Data1D()

    def getTSData(self, id, res, dom='1D'):
        message = ''
        if(res.upper() in ("H", "H_", "LEVEL","LEVELS")):
            try:
                ind = self.Data_1D.H.Header.index(id)
                data = self.Data_1D.H.Values[:,ind]
                return True, data, message
            except:
                message = 'Data not found for 1D H with ID: '+id
                return False, [0.0], message
        elif(res.upper() in ("Q","Q_","FLOW","FLOWS")):
            try:
                ind = self.Data_1D.Q.Header.index(id)
                data = self.Data_1D.Q.Values[:,ind]
                return True, data, message
            except:
                message = 'Data not found for 1D Q with ID: '+id
                return False, [0.0], message
        elif(res.upper() in ("V","V_","VELOCITY","VELOCITIES")):
            try:
                ind = self.Data_1D.V.Header.index(id)
                data = self.Data_1D.V.Values[:,ind]
                return True, data, message
            except:
                message = 'Data not found for 1D V with ID: '+id
                return False, [0.0], message
        elif(res.upper() in ("US_H", "US LEVELS")):
            chan_list = tuple(self.Channels.chan_name)
            ind = chan_list.index(str(id))
            a = str(self.Channels.chan_US_Node[ind])
            try:
                ind = self.Data_1D.H.Header.index(a)
            except:
                message = 'Unable to find US node: ',+a+' for channel '+ id
                return False, [0.0], message
            try:
                data = self.Data_1D.H.Values[:,ind]
                return True, data, message
            except:
                message = 'Data not found for 1D H with ID: '+a
                return False, [0.0], message
        elif(res.upper() in ("DS_H","DS LEVELS")):
            chan_list = tuple(self.Channels.chan_name)
            ind = chan_list.index(str(id))
            a = str(self.Channels.chan_DS_Node[ind])
            try:
                ind = self.Data_1D.H.Header.index(a)
            except:
                message = 'Unable to find DS node: ',+a+' for channel '+ id
                return False, [0.0], message
            try:
                data = self.Data_1D.H.Values[:,ind]
                return True, data, message
            except:
                message = 'Data not found for 1D H with ID: '+a
                return False, [0.0], message
        else:
            message = 'Warning - Expecting unexpected data type for 1D: '+res
            return False, [], message

    def getLPConnectivity(self, id1, id2=None):
        message = None
        error = False
        self.LP.chan_list = []
        if (id2 == None): # only one channel selected
            finished = False
            i = 0
            chan_list = tuple(self.Channels.chan_name)
            try:
                ind1 = chan_list.index(str(id1))
            except:
                message = 'ERROR - ID not found: ' + str(id1)
                error = True
                return error, message
            self.LP.chan_list = [id1]
            self.LP.chan_index = [ind1]
            self.LP.node_list = [(self.Channels.chan_US_Node[ind1])]
            self.LP.node_list.append(self.Channels.chan_DS_Node[ind1])
            id = ind1
            while not finished:
                i = i + 1
                chan = self.Channels.chan_DS_Chan[id]
                if(chan=='------'):
                    finished = True
                else:
                    self.LP.chan_list.append(chan)
                    try:
                        id = self.Channels.chan_name.index(chan)
                        self.LP.chan_index.append(id)
                        self.LP.node_list.append(self.Channels.chan_DS_Node[id])
                    except:
                        error = True
                        message = 'ERROR - Unable to process channel: '+chan
                        return error, message
            if not error:
                self.LP.connected = True
            return error, message

        else: # two channels selected (check for more than two in main routine)
            finished = False
            found = False
            i = 0
            chan_list = tuple(self.Channels.chan_name)
            # check 1st ID exists
            try:
                ind1 = chan_list.index(str(id1))
            except:
                error = True
                message = 'ERROR - ID not found: '+str(id1)
                return error, message
            # check 2nd ID exists
            try:
                ind2 = chan_list.index(str(id2))
            except:
                #QMessageBox.information(iface.mainWindow(),"ERROR", ("ID not found: " + str(id2)))
                error = True
                message = 'ERROR - ID not found: '+str(id2)
                return error, message
            # assume ID2 is downstream of ID1
            endchan = id2
            self.LP.chan_list = [id1]
            self.LP.chan_index = [ind1]
            self.LP.node_list = [(self.Channels.chan_US_Node[ind1])]
            self.LP.node_list.append(self.Channels.chan_DS_Node[ind1])
            id = ind1
            while not finished:
                i = i + 1
                chan = self.Channels.chan_DS_Chan[id]
                if(chan=='------'):
                    finished = True
                elif(chan==endchan):
                    found = True
                    finished = True
                    self.LP.chan_list.append(chan)
                    try:
                        id = self.Channels.chan_name.index(chan)
                        self.LP.chan_index.append(id)
                        self.LP.node_list.append(self.Channels.chan_DS_Node[id])
                    except:
                        error = True
                        message = 'ERROR - Unable to process channel: '+chan
                        return error, message
                else:
                    self.LP.chan_list.append(chan)
                    try:
                        id = self.Channels.chan_name.index(chan)
                        self.LP.chan_index.append(id)
                        self.LP.node_list.append(self.Channels.chan_DS_Node[id])
                    except:
                        error = True
                        message = 'ERROR - ID not found: '+str(id)
                        return error, message

            if not (found): # id2 is not downstream of 1d1, reverse direction and try again...
                #QMessageBox.information(iface.mainWindow(), "DEBUG", "reverse direction and try again")
                finished = False
                found = False
                i = 0
                endchan = id1
                self.LP.chan_list = [id2]
                self.LP.chan_index = [ind2]
                self.LP.node_list = [(self.Channels.chan_US_Node[ind2])]
                self.LP.node_list.append(self.Channels.chan_DS_Node[ind2])
                id = ind2
                while not finished:
                    i = i + 1
                    chan = self.Channels.chan_DS_Chan[id]
                    if(chan=='------'):
                        finished = True
                    elif(chan==endchan):
                        found = True
                        finished = True
                        self.LP.chan_list.append(chan)
                        try:
                            id = self.Channels.chan_name.index(chan)
                            self.LP.chan_index.append(id)
                            self.LP.node_list.append(self.Channels.chan_DS_Node[id])
                        except:
                            error = True
                            message = 'ERROR - Unable to process channel: '+chan
                            return error, message
                    else:
                        self.LP.chan_list.append(chan)
                        try:
                            id = self.Channels.chan_name.index(chan)
                            self.LP.chan_index.append(id)
                            self.LP.node_list.append(self.Channels.chan_DS_Node[id])
                        except:
                            error = True
                            message = 'ERROR - Unable to process channel: '+chan
                            return error, message
            if not (found): # id1 and 1d2 are not connected
                error = True
                message = 'Channels ' +id1 + ' and '+id2+' are not connected'
                return error, message
            else:
                if not error:
                    self.LP.connected = True
            return error, message

    def getLPStaticData(self):
        # get the channel and node properties lenghts, elevations etc doesn't change with results
        error = False
        message = None
        if (len(self.LP.chan_index)<1):
            error = True
            message = 'No LP channel data exists - Use .getLP_Connectivity to generate'
            return error, message

        # node info
        self.LP.node_bed = []
        self.LP.node_top = []
        self.LP.H_nd_index = []
        self.LP.node_index = []
        self.LP.Hmax = []
        for i, nd in enumerate(self.LP.node_list):
            try: #get node index and elevations
                ind = self.nodes.node_name.index(nd)
                self.LP.node_index.append(ind)
                self.LP.node_bed.append(self.nodes.node_bed[ind])
                self.LP.node_top.append(self.nodes.node_top[ind])
            except:
                error = True
                message = 'Unable to find node in _Nodes.csv file. Node: '+nd
                return error, message
            try: #get index to data in 1d_H.csv used when getting temporal data
                ind = self.Data_1D.H.Header.index(nd)
                self.LP.H_nd_index.append(ind)
                if i == 0:
                    self.LP.Hmax.append(max(self.Data_1D.H.Values[:,ind]))
                elif i < len(self.LP.node_list) - 1:
                    #self.LP.Hmax.append(max(self.Data_1D.H.Values[:,ind], self.LP.chan_inv[2 * i - 1]))
                    self.LP.Hmax.append(max(self.Data_1D.H.Values[:, ind]))
                    self.LP.Hmax.append(max(self.Data_1D.H.Values[:, ind]))
                else:
                    #self.LP.Hmax.append(max(self.Data_1D.H.Values[:, ind], self.LP.chan_inv[2 * i - 1]))
                    self.LP.Hmax.append(max(self.Data_1D.H.Values[:, ind]))
            except:
                error = True
                message = 'Unable to find node in _1d_H.csv file. Node: '+nd
                return error, message


        # channel info
        self.LP.dist_nodes = [0.0] # nodes only
        #self.LP.dist_chan_inverts = [0.0] # at each channel end (no nodes)
        self.LP.dist_chan_inverts = []
        self.LP.dist_inverts = [0.0] # nodes and channel ends
        self.LP.chan_inv = []
        self.LP.chan_LB = []
        self.LP.chan_RB = []

        for i, chan_index in enumerate(self.LP.chan_index):
            #length of current channel
            chan_len = self.Channels.chan_Length[chan_index] # length of current channel

            # distance at nodes
            cur_len = self.LP.dist_nodes[len(self.LP.dist_nodes)-1] #current length at node
            self.LP.dist_nodes.append(cur_len+chan_len)

            #distance at inverts
            #cur_len = self.LP.dist_chan_inverts[len(self.LP.dist_chan_inverts)-1] #current length at invert locations
            if len(self.LP.dist_chan_inverts) == 0:
                cur_len = 0.
            else:
                cur_len = self.LP.dist_chan_inverts[len(self.LP.dist_chan_inverts) - 1]
            self.LP.dist_chan_inverts.append(cur_len+0.0001) # dist for upstream invert
            new_len = cur_len + chan_len
            self.LP.dist_chan_inverts.append(new_len-0.0001) #dist for downstream invert

            #distance at both inverts and nodes
            cur_len = self.LP.dist_inverts[len(self.LP.dist_inverts)-1] #current length at invert locations
            self.LP.dist_inverts.append(cur_len+0.0001) # dist for upstream invert
            new_len = cur_len + self.Channels.chan_Length[chan_index]
            self.LP.dist_inverts.append(new_len-0.0001) #dist for downstream invert
            self.LP.dist_inverts.append(new_len) #dist at next node

            #elevations at channel inverts, left and right obverts
            self.LP.chan_inv.append(self.Channels.chan_US_Inv[chan_index])
            self.LP.chan_LB.append(self.Channels.chan_LBUS_Obv[chan_index])
            self.LP.chan_RB.append(self.Channels.chan_RBUS_Obv[chan_index])
            self.LP.chan_inv.append(self.Channels.chan_DS_Inv[chan_index])
            self.LP.chan_LB.append(self.Channels.chan_LBDS_Obv[chan_index])
            self.LP.chan_RB.append(self.Channels.chan_RBDS_Obv[chan_index])

        #get infor about pits
        self.LP.npits = int(0)
        self.LP.pit_dist = []
        self.LP.pit_z = []
        for i, nd_ind in enumerate(self.LP.node_index):
            nchan = self.nodes.node_nChan[nd_ind]
            chan_list = self.nodes.node_channels[nd_ind]
            for j in range(nchan):
                chan = chan_list[j]
                indC = self.Channels.chan_name.index(chan)
                usC = self.Channels.chan_US_Chan[indC]
                dsC = self.Channels.chan_DS_Chan[indC]
                if usC == "------" and dsC == "------": #channel is pit channel
                    self.LP.npits = self.LP.npits + 1
                    self.LP.pit_dist.append(self.LP.dist_nodes[i])
                    self.LP.pit_z.append(self.Channels.chan_US_Inv[indC])


        #normal return
        self.LP.static = True
        return error, message

    def getLPData(self,dat_type,time,dt_tol):
        self.LP.Hdata = []
        error = False
        message = None
        dt_abs = abs(self.times - time)
        t_ind = dt_abs.argmin()
        if (self.times[t_ind] - time)>dt_tol:
            error = True
            message = 'ERROR - Closest time: '+str(self.times[t_ind])+' outside time search tolerance: '+str(dt_tol)
            return  error, message
        if dat_type == 'Water Level' or dat_type == 'Head':
            #for h_ind in self.LP.H_nd_index:
            #    self.LP.Hdata.append(self.Data_1D.H.Values[t_ind,h_ind])
            for i, h_ind in enumerate(self.LP.H_nd_index):
                if i == 0:
                    self.LP.Hdata.append(self.Data_1D.H.Values[t_ind, h_ind])
                elif i < len(self.LP.H_nd_index) - 1:
                    self.LP.Hdata.append(max(self.Data_1D.H.Values[t_ind, h_ind], self.LP.chan_inv[2 * i - 1]))
                    self.LP.Hdata.append(self.Data_1D.H.Values[t_ind, h_ind])
                else:
                    self.LP.Hdata.append(max(self.Data_1D.H.Values[t_ind, h_ind], self.LP.chan_inv[2 * i - 1]))
        else:
            error = True
            message = 'ERROR - Only head supported for LP temporal data'
            return  error, message

        return error, message
    
    def load(self, fname):
        error = False
        message = []
        self.filename = fname
        self.fpath = os.path.dirname(fname)
        try:
            data = numpy.genfromtxt(fname, dtype=str, delimiter="==")
        except:
            return True, 'ERROR - Unable to load data, check file exists.'
        for i in range (0,len(data)):
            tmp = data[i,0]
            dat_type = tmp.strip()
            tmp = data[i,1]
            rdata = tmp.strip()
            if dat_type == 'Format Version':
                self.formatVersion = int(rdata)
            elif dat_type == 'Units':
                self.units = rdata
            elif dat_type == 'Simulation ID':
                self.displayname = rdata
            elif dat_type == 'Number Channels':
                #self.nChannels = int(rdata)
                self.Data_1D.nChan = int(rdata)
            elif dat_type == 'Number Nodes':
                self.Data_1D.nNode = int(rdata)
            elif dat_type == 'Channel Info':
                if rdata != 'NONE':
                    fullpath = getOSIndependentFilePath(self.fpath, rdata)
                    if not os.path.isfile(fullpath):
                        fname = rdata.replace('_1d_','_1d_1d_')
                        fullpath_original = fullpath
                        fullpath = os.path.join(self.fpath,fname)
                        if not os.path.exists(fullpath):
                            message.append('{0} does not exist and {1} does not exist'.format(fullpath_original, fullpath))
                            return True, '; '.join(message)
                    self.Channels = ChanInfo(fullpath)
                    if (self.Data_1D.nChan != self.Channels.nChan):
                        message.append("Number of Channels does not match value in .info")
                        return True, '; '.join(message)
            elif dat_type == 'Node Info':
                if rdata != 'NONE':
                    fullpath = getOSIndependentFilePath(self.fpath, rdata)
                    if not os.path.isfile(fullpath):
                        fname = rdata.replace('_1d_','_1d_1d_')
                        fullpath_original = fullpath
                        fullpath = os.path.join(self.fpath, fname)
                        if not os.path.exists(fullpath):
                            message.append('{0} does not exist and {1} does not exist'.format(fullpath_original, fullpath))
                            return True, '; '.join(message)
                    self.nodes = NodeInfo(fullpath)
                    if self.nodes.message:
                        return True, '; '.join(self.nodes.message)
            elif dat_type == 'Water Levels':
                if rdata != 'NONE':
                    fullpath = getOSIndependentFilePath(self.fpath, rdata)
                    self.Data_1D.H = Timeseries(fullpath, 'H', self.displayname)
                    if self.Data_1D.H.message:
                        return True, '; '.join(self.Data_1D.H.message)
                    self.nTypes = self.nTypes + 1
                    self.Types.append('1D Water Levels')
                    if self.nTypes == 1:
                        self.times = self.Data_1D.H.Values[:,1]
            elif dat_type == 'Flows':
                if rdata != 'NONE':
                    fullpath = getOSIndependentFilePath(self.fpath, rdata)
                    self.Data_1D.Q = Timeseries(fullpath, 'Q', self.displayname)
                    if self.Data_1D.Q.message:
                        return True, '; '.join(self.Data_1D.Q.message)
                    self.nTypes = self.nTypes + 1
                    self.Types.append('1D Flows')
                    if self.nTypes == 1:
                        self.times = self.Data_1D.Q.Values[:,1]
            elif dat_type == 'Velocities':
                if rdata != 'NONE':
                    fullpath = getOSIndependentFilePath(self.fpath, rdata)
                    self.Data_1D.V = Timeseries(fullpath, 'V', self.displayname)
                    if self.Data_1D.V.message:
                        return True, '; '.join(self.Data_1D.V.message)
                    self.nTypes = self.nTypes + 1
                    self.Types.append('1D Velocities')
                    if self.nTypes == 1:
                        self.times = self.Data_1D.V.Values[:,1]
            else:
                message.append("Warning - Unknown Data Type: {0}".format(dat_type))
                
        return False, '; '.join(message)

    def pointResultTypesTS(self):
        """
        Returns a list of all the available point result types.

        :return: list -> str result type e.g. 'flows'
        """
    
        types = []
    
        for type in self.Types:
            if 'WATER LEVELS' in type.upper():
                types.append('Level')
            elif 'ENERGY LEVELS' in type.upper():
                types.append('Energy Level')
            elif 'POINT VELOCITY' in type.upper():
                types.append('Velocity')
            elif 'POINT X-VEL' in type.upper():
                types.append('VX')
            elif 'POINT Y-VEL' in type.upper():
                types.append('VY')
    
        return types

    def lineResultTypesTS(self):
        """
        Returns a list of all the available line result types.

        :return: list -> str result type e.g. 'flows'
        """
    
        types = []
    
        for type in self.Types:
            if 'FLOWS' in type.upper():
                types.append('Flow')
            elif 'VELOCITIES' in type.upper():
                types.append('Velocity')
            elif 'LINE FLOW AREA' in type.upper():
                types.append('Flow Area')
            elif 'LINE INTEGRAL FLOW' in type.upper():
                types.append('Flow Integral')
            elif 'US LEVELS' in type.upper():
                types.append('US Levels')
            elif 'DS LEVELS' in type.upper():
                types.append('DS Levels')
            elif 'DS LEVELS' in type.upper():
                types.append('DS Levels')
            elif 'LINE STRUCTURE FLOW' in type.upper():
                types.append('Structure Flows')
            elif 'STRUCTURE LEVELS' in type.upper():
                types.append('Structure Levels')
    
        if types:
            if 'US Levels' not in types:
                types.append('US Levels')
            if 'DS Levels' not in types:
                types.append('DS Levels')
    
        return types

    def regionResultTypesTS(self):
        """
        Returns a list of all the available region result types. For 2013 results should return empty list.

        :return: list -> str result type e.g. 'flows'
        """
    
        types = []
    
        for type in self.Types:
            if 'REGION AVERAGE WATER LEVEL' in type.upper():  # 2017-09-AA
                types.append('Average Level')
            elif 'REGION MAX WATER LEVEL' in type.upper():  # 2017-09-AA
                types.append('Max Level')
            elif 'REGION FLOW INTO' in type.upper():  # 2017-09-AA
                types.append('Flow Into')
            elif 'REGION FLOW OUT OF' in type.upper():  # 2017-09-AA
                types.append('Flow Out')
            elif 'REGION VOLUME' in type.upper():  # 2017-09-AA
                types.append('Volume')
            elif 'REGION SINK/SOURCE' in type.upper():  # 2017-09-AA
                types.append('Sink/Source')
    
        return types

    def lineResultTypesLP(self):
        """
        Returns a list of all the available line result types for long plotting.

        :return: list -> str result type e.g. 'flows'
        """
    
        types = []
    
        for type in self.Types:
            if 'WATER LEVELS' in type.upper():
                types.append('Water Level')
            # types.append('Water Level at Time')
            elif 'ENERGY LEVELS' in type.upper():
                types.append('Energy Level')
            # types.append('Energy Level at Time')
    
        types.append('Bed Level')
        #types.append('Culverts and Pipes')
        types.append('Left Bank Obvert')
        types.append('Right Bank Obvert')
        #types.append('Pit Ground Levels (if any)')
        #types.append('Adverse Gradients (if any)')
    
        return types

    def timeSteps(self):
        """
        Returns a list of the available time steps. Assumes all time series results have the same timesteps.

        :return: list -> float time (hr)
        """
    
        if self.Data_1D.H.loaded:
            return self.Data_1D.H.Values[:, 1].tolist()
        elif self.Data_1D.V.loaded:
            return self.Data_1D.V.Values[:, 1].tolist()
        elif self.Data_1D.Q.loaded:
            return self.Data_1D.Q.Values[:, 1].tolist()

        else:
            return []

    def getLongPlotXY(self, type, time):
        """
        Generates long plot X, Y coordinates

        :param type: str -> result type e.g. 'Water Level'
        :param time: float
        :return: tuple -> list -> float e.g. (x, y)
        """

        error = False
        message = ''
    
        if 'water level' in type.lower() or type.upper() == 'H':
            if time == -99999:
                return False, '', (self.LP.dist_chan_inverts, self.LP.Hmax)
            else:
                error, message = self.getLPData('Water Level', time, 0.01)
                return error, message, (self.LP.dist_chan_inverts, self.LP.Hdata)
    
        #elif 'adverse gradients (if any)' in type.lower():
        #    return (self.LP.adverseH.chainage, self.LP.adverseH.elevation), \
        #           (self.LP.adverseE.chainage, self.LP.adverseE.elevation)
    
        elif 'bed level' in type.lower():
            return False, '', (self.LP.dist_chan_inverts, self.LP.chan_inv)
    
        elif 'left bank obvert' in type.lower():
            return False, '', (self.LP.dist_chan_inverts, self.LP.chan_LB)
    
        elif 'right bank obvert' in type.lower():
            return False, '', (self.LP.dist_chan_inverts, self.LP.chan_RB)
    
        elif 'pit ground levels (if any)' in type.lower():
            return False, '', (self.LP.pit_dist, self.LP.pit_z)
    
        #elif 'culverts and pipes' in type.lower():
        #    return self.LP.culv_verts, [0, 1, 2, 3, 4, 5]  # dummy y data - ultimately not used
    
        else:
            return True, 'Unrecognised result type', ([], [])