#!/usr/bin/python

##
## Copyright (C) 2011-2012 Andrew Atkinson
##
##-------------------------------------------------------------------
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##-------------------------------------------------------------------

import math

#Function bisectors seems  not to be used any more
def bisectors(shots):
    """Calculates bisectors of the stns, using 2 legs only"""
    stn = []
    stndir = []
    for s in shots:
        if s['to'] != '-':
            stndir.append([s['from'], s['compass'], True])
            stndir.append([s['to'], s['compass'], False])
            stn.append(s['from'])
            stn.append(s['to'])
        else:
            continue
    # Unique list of stations
    stnbisectors = {}
    for s in set(stn):
        station = [a for a in stndir if a[0] == s]
        if len(station) == 1:
            bisector = station[0][1] + 90
##            if station[0][5] == '>':
##                d = '>'
##            else:
##                d = '<'
##                if [station[0][1] >180:
##                    sta -= 180
##                else:
##                    sta += 180
##            stnbisectors[s] = [station[0][1], station[0][5],
##                               sta+180, d]
        else:
            for st in station:
                # Reverse bearing if reading towards station
                if not st[2]:
                    if st[1] > 180:
                        st[1] -= 180
                    else:
                        st[1] += 180
            # Only take first 2 stations
            bisector = (station[0][1] + station[1][1]) / 2
        stnbisectors[s] = bisector
##            stnbisectors[s] = [station[0][1], station[0][5],
##                               station[[0][1], station[0][5]]
    return stnbisectors

#planlength * math.sin((args[0] - D['compass']) * math.pi/180)


def elevsplays(stn, comp, direction, stns):
    """Calculates the sideview splays in the plain of the legs"""
    # Get the outgoing/incoming station information
    # Bisector should alway to be to the true right of the legs
    #print(stn)
    station = [a for a in stns if a[0] == stn]
    if len(station) == 0:
        bisector = 0
        print('''You appear to have a station, {!r} not connected to a previous station
Sorry this breaks this script, it might be editable in the top file
Ending'''.format (stn))
        pass
    elif len(station) == 1:
        # For single station take bisector as perpendicular
    # Reverse bearing if reading towards station
        if not station[0][2]:
            if station[0][3] < 180:
                station[0][3] += 180
            else:
                station[0][3] -= 180
        if station[0][1]:  # Outgoing
            bisector = station[0][3] + 90
        else:
            bisector = station[0][3] - 90
    else:
        for st in station:
            # Reverse bearing if reading towards station
            ######Not safe FIXME######
            if not st[2]:
                if st[3] < 180:
                    st[3] += 180
                else:
                    st[3] -= 180
        # Only take first 2 stations
        # (Outgoing and Incoming assumed not checked)
        bisector = (station[0][3] + station[1][3]) / 2
        if ((station[0][3] < station[1][3]) |
            ((station[0][3] > station[1][3]) &
            (station[0][3] - station[1][3] > 180))):
                bisector -= 180
    return math.sin((bisector - comp) * math.pi / 180)


def RelPos(D, pangle, *args):
    """Works out the plan, elevation and projection x,y relative coordinates"""
    #copy accross the station names
    A = {}
    A['from'] = D['from']
    A['to'] = D['to']
    planlength = D['tape'] * math.cos(D['clino'] * math.pi / 180)
    px = planlength * math.sin(D['compass'] * math.pi / 180)
    py = planlength * math.cos(D['compass'] * math.pi / 180)
    A['plan'] = (px, py)
    ey = D['tape'] * math.sin(D['clino'] * math.pi / 180)
    if args:
        ex = planlength * elevsplays(D['from'], D['compass'], D['direction'],
                                     args[0])
    else:
        ex = planlength
    # Need to mltiply ex by -1 if direction is left
    if D['direction'] == '<':
        ex *= -1
    A['elev'] = (ex, ey)
    A['proj'] = ((planlength * math.cos((pangle + 90 -
                                D['compass']) * math.pi / 180)), ey)
    return A


def xsecsplay(splay, xsec):
    '''Splays for xsections'''
    #Assumption made: angle of xsec is direction facing)
    clino_rad = splay['clino'] * math.pi / 180
    planlength = (splay['tape'] * math.cos(clino_rad))
    comp_rad = splay['compass'] * math.pi / 180
    if xsec[3] == -1:
        x = planlength * math.sin(comp_rad)
        y = planlength * math.cos(comp_rad)
    else:
        y = splay['tape'] * math.sin(clino_rad)
        angle = (90 + xsec[3] - splay['compass']) * math.pi / 180
        x = planlength * (math.cos(angle))
    #print(xsec[2], xsec[0], xsec[1], xsec[0]+x, xsec[1]+y)
    return [xsec[0], xsec[1], xsec[0] + x, xsec[1] + y]


def relshots(shots, topodata, doplan=True, doelev=True, pangle=0):
    """Calculates the relative positions for the xvi files"""
    xsecsplays = {'plan': [], 'elev': []}
    xsec = {}
    rpos = []
    #stnbisectors = None
    legdir = []
    outgoing = True
    incoming = False
    fm = True
    to = False
    startstn = None
    if doplan:
        xsec['plan'] = topodata['outline']['xsec']
        #print(topodata['outline']['xsec'])
    if doelev:
        xsec['elev'] = topodata['sideview']['xsec']
        # For elevation, the bisector of the legs is needed
        #- This appears not to  be used
        #stnbisectors = bisectors(shots)
    for ln in shots:
        #The first data line should set the zero, zero station
        if startstn is None:
            startstn = ln['from']
        if ln['to'] != '-':
            rpos.append(RelPos(ln, pangle))
            # Produce a list of incoming/outgoing compass directions
##            if ln['tape'] == 0:
##                continue
            #Set up first leg
            if legdir == []:
                if ln['from'] == startstn:
                    legdir.append([ln['from'], outgoing, fm, ln['compass']])
                    legdir.append([ln['to'], incoming, to, ln['compass']])
                else:
                    legdir.append([ln['to'], outgoing, to, ln['compass']])
                    legdir.append([ln['from'], incoming, fm, ln['compass']])
            #Add on remaining legs
            #If station is new it is incoming
            else:
                if any(ln['to'] == e[0] for e in legdir):
                    legdir.append([ln['from'], incoming, fm, ln['compass']])
                    legdir.append([ln['to'], outgoing, to, ln['compass']])
                elif any(ln['from'] == e[0] for e in legdir):
                    legdir.append([ln['to'], incoming, to, ln['compass']])
                    legdir.append([ln['from'], outgoing, fm, ln['compass']])
                else:
                    print ('''Line {} {} {:6.2f} {:6.2f} {:6.2f} not connected -  Missing out for now'''
.format(ln['from'], ln['to'], ln['tape'], ln['compass'], ln['clino']))
                    #FIXME  Orphan: ignored at the moment
                    pass
    for ln in shots:
        if ln['to'] == '-':
            if doplan and not doelev:
                rpos.append(RelPos(ln, pangle))
            else:
                rpos.append(RelPos(ln, pangle, legdir))
            if doplan:
                for px in xsec['plan']:
                    #print(px)
                    if px[2] == ln['from']:
                        xsecsplays['plan'].append(xsecsplay(ln, px))
            if doelev:
                for ex in xsec['elev']:
                    if ex[2] == ln['from']:
                        xsecsplays['elev'].append(xsecsplay(ln, ex))
    return {'stnpos': rpos, 'xsecsplays': xsecsplays, 'startstn': startstn}
