#!/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/>.
##-------------------------------------------------------------------

from __future__ import unicode_literals

from datetime import date
import codecs
import math
import os
import re
import shutil
import time
import io as cStringIO
import wx

import CheckerCreate


def meanbearing(bearing):
    y = 0
    x = 0
    for i, angle in enumerate(bearing):
        y += math.sin(math.radians(bearing[i]))
        x += math.cos(math.radians(bearing[i]))
    avbearing = math.degrees(math.atan2(y, x))
    ##tan2 gives result between -180 and 180, in correct direction
    if avbearing < 0:
        avbearing += 360
    return avbearing


def therionline(L, thfile, dataorder, addroll):
    for i in dataorder:
        if (i == 'to') | (i == 'from'):
            thfile.write(L[i] + '\t')
        elif i == 'tape':
            thfile.write(str(round(L[i], 3)) + '\t')
        else:
            thfile.write(str(round(L[i], 2)) + '\t')
    if addroll:
        thfile.write(str(int(L['rollangle'])) + '\t')
    if 'comment' in L:
        #comment can be multiple line so need # for subsequent lines
        thfile.write(re.sub(r'\n', r'\n\t\t\t\t\t#', L['comment']) + '\n')
    else:
        thfile.write('\n')
    return


def dataline(dataorder, thfile):
    ''' sets the data normal line'''
    dline = '\ndata normal '
    for i in dataorder:
        dline += i + ' '
    dline += 'ignoreall\n'
    thfile.write(dline)
    return


def wcavers(thfile, settings, cavers, tripdate):
    '''adds in the surveyors and exploration information'''
    #build the individual lists

    if settings['docavers']:
        if len(cavers['Notes']):
            thfile.write('\n\tteam "')
            thfile.writelines('" notes\n\tteam "'.join(cavers['Notes']))
            thfile.write('" notes\n')
        if len(cavers['Instruments']):
            thfile.write('\tteam "')
            thfile.writelines('" instruments\n\tteam "'.
                                join(cavers['Instruments']))
            thfile.write('" instruments\n')
        if len(cavers['Dog']):
            thfile.write('\tteam "')
            thfile.writelines('" dog\n\tteam "'.join(cavers['Dog']))
            thfile.write('" dog\n')
        #And the instrument used
        if len(settings['instrument']):
            thfile.write('\n\tinstrument  instruments "' +
                            settings['instrument'] +
                            '"\n')

    if settings['doexplo']:
        if settings['samedate']:
            edate = time.strftime("%Y.%m.%d", tripdate)
        else:
            edate = settings['explodate']
        thfile.write('\n\texplo-date ' + edate)
        if len(cavers['Explorer']):
            thfile.write('\n\texplo-team "')
            thfile.writelines('"\n\texplo-team "'.
                                join(cavers['Explorer']))
            thfile.write('"\n')
        else:
            thfile.write('\n')
    return


def begincentreline(trip, settings, cavers, thfile):
    '''date, comment and declination, plus cavers'''
    dataorder = settings['dataorder']
    thfile.write('\ncentreline')
    if settings['docopyright']:
        thfile.write('\n\ncopyright ' + str(date.today().year) + " \""
                        + settings['cright'] + "\"")
    #Comments can be multiple line so add a #
    thfile.write('\n\n### TRIP COMMENT FROM POCKETTOPO ###\n#' +
                 re.sub(r'\n', r'\n#', trip['comment']))
    thfile.write('\n\n\tdate ' +
                    time.strftime("%Y.%m.%d", trip['date']) + '\n')
    if settings['docavers'] | settings['doexplo']:
        wcavers(thfile, settings, cavers, trip['date'])
    dataline(dataorder, thfile)


def decadjust(dataline, declination):
    dataline['compass'] += declination
    return dataline


def legdata(topodata, settings, cavers, thfile):
    '''Calculates relative positions of the stations and writes out
       the th file eg centreline data
       '''
    trips = topodata['trips']
    shots = topodata['shots']
    dataorder = settings['dataorder']
    addroll = settings['addroll']
    doplan = settings['doplan']
    doelev = settings['doelevation']
    doproj = settings['doprojection']
    addlrud = settings['addlrud']
    References = topodata['ref']
    usetrip = 0

    #Start the centreline data
    if len(trips) > 1:
        #Build a list of comments
        commentlist = []
        for c in trips:
            commentlist.append(time.strftime("%Y.%m.%d", c['date']) +
                               ': ' + c['comment'])
            choicecap = 'It appears you have more than one set of trip \
information.\n\
To place all of them in the th file hit cancel.\n\
To use one of them for the whole data set select one and press OK,'
        usetrip = wx.GetSingleChoice(choicecap, 'Trip information', commentlist)
        try:
            usetrip = commentlist.index(usetrip)
        except ValueError:
            usetrip = -1
        #usetrip: -1 is all; any other number is use that trip only
        if usetrip == -1:
            #Check the first shot for trip info and put it in if there is
            #declination is used to correct plots for th2 files
            #and for *declination in svx file
            if shots[0]['trip'] != -1:
                begincentreline(trips[shots[0]['trip']], settings, cavers, thfile)
                declination = trips[shots[0]['trip']]['dec']
            else:
                thfile.write('centreline\n')
                dataline(settings['dataorder'], thfile)
                declination = 0
        else:
            begincentreline(trips[usetrip], settings, cavers, thfile)
            declination = trips[usetrip]['dec']
    else:
        begincentreline(trips[0], settings, cavers, thfile)
        declination = trips[0]['dec']

    #Get all the shots and calculate the relative station points
    #Set D to something it cannot be to force first line write
    D = 'R'
    c = 1
    bearing = []
    #rpos = []
    # container for shots to be used
    shots2 = []
    for ln1, ln2 in zip(shots, shots[1:]):
        #print(ln1)
        if ln1['direction'] != D:
            D = ln1['direction']
            if D == '<':
                thfile.write('  extend left\n')
            else:
                thfile.write('  extend right\n')
        #If shot is splay just write it
        if ln1['to'] == '-':
            therionline(ln1, thfile, dataorder, addroll)
            if doplan | doelev | doproj | addlrud:
                shots2.append(decadjust(ln1, declination))

        #if shot check for group of shots
        #write in shot as comment and build average
        elif (ln1['to'] == ln2['to']) & (ln1['from'] == ln2['from']):
            #for the first set up the Dict
            bearing.append(ln1['compass'])
            if c == 1:
                avln = ln1

            #otherwise add on the value
            else:
                for i in ('tape', 'clino'):
                    avln[i] += ln1[i]
            c += 1
            #write the line out as a comment
            thfile.write('#')
            therionline(ln1, thfile, dataorder, addroll)
        else:
            #if single shot c will equal 1, just print line
            if c == 1:
                therionline(ln1, thfile, dataorder, addroll)
                if doplan | doelev | doproj | addlrud:
                    shots2.append(decadjust(ln1, declination))
            #otherwise print the average
            else:
                for i in ('tape', 'compass', 'clino'):
                    #Add on the last bit of data
                    avln[i] += ln1[i]
                    #take mean of each
                    if i == 'compass':
                        avln[i] = meanbearing(bearing)
                    else:
                        avln[i] /= c
                thfile.write('#')
                therionline(ln1, thfile, dataorder, addroll)
                avln['comment'] = "Calculated leg from " + str(c) + " above"
                #do not have a roll for a calculated line
                therionline(avln, thfile, dataorder, addroll=False)
                if doplan | doelev | doproj | addlrud:
                    shots2.append(decadjust(avln, declination))
                c = 1
                bearing = []
        #when trip info changes, set declination

        if (ln1['trip'] != ln2['trip']):
            declination = trips[ln2['trip']]['dec']
            #if there is more than one set of trip info and using all of them:
            #if (ln1['trip'] != -1):
            if usetrip == -1:
                thfile.write('endcentreline\n\n')
                begincentreline(trips[ln2['trip']], settings, cavers, thfile)
    #Do something with the last line
    #for a leg in a group
    if ((ln1['to'] == ln2['to'])
        & (ln1['from'] == ln2['from'])
        & (ln2['to'] != '-')):
        bearing.append(ln1['compass'])
        for i in ('tape', 'clino'):
            #Add on the last bit of data
            avln[i] += ln2[i]
            #take mean of each
            if i == 'compass':
                avln[i] = meanbearing(bearing)
            else:
                avln[i] /= c
        thfile.write('#')
        therionline(ln2, thfile, dataorder, addroll)
        avln['comment'] = "Calculated leg from " + str(c) + " above"
        #do not have a roll for a calculated line
        therionline(avln, thfile, dataorder, addroll=False)
        if doplan | doelev | doproj | addlrud:
            shots2.append(decadjust(avln, declination))
    #otherwise just output the line
    else:
        therionline(ln2, thfile, dataorder, addroll)
        if doplan | doelev | doproj | addlrud:
            shots2.append(decadjust(ln2, declination))
    return shots2
##    return {'stnpos':rpos, 'xsecsplays':xsecsplays}


def therionfile(topodata, settings, cavers, locations):
    '''Produces full th file'''
    surveyname = locations['surveyname']
    fileloc = locations['surveydir']
    pemapsuffix = settings['pemapsuffix'].replace('###',
                                                    str(settings['projangle']))
    #Use a virtual file, and only write if selected
    shotsfile = cStringIO.StringIO()
    if settings['dosvxfile']:
        svxfile = cStringIO.StringIO()
        svxfile.write('*begin ' + surveyname + '\n\n*alias station - ..\n')
    #If no thfile to be outputted only need to do the leg data
    if settings['dothfile']:
        thfile = cStringIO.StringIO()
        start = 'survey ' + surveyname + '\n\n'
        thfile.write(start)
        if settings['importth2plan']:
            line = 'input ' + surveyname + settings['plansuffix'] + '.th2\n'
            thfile.write(line)
        if settings['importth2elev']:
            line = 'input ' + surveyname + settings['elevsuffix'] + '.th2\n'
            thfile.write(line)
        if settings['importth2proj']:
#            if
            line = 'input ' + surveyname + settings['projsuffix']
            #if settings['pemapsuffixangle']:
                #line += str(settings['projangle'])
            line += '.th2\n'
            thfile.write(line)
        if settings['addplanmap']:
            line = '\nmap ' + surveyname + settings['pmapsuffix']
            thfile.write(line)
            for n in range(1, int(settings['pscraps']) + 1):
                thfile.write('\n\t' + surveyname
                             + settings['pscrapsuffix'] + str(n))
            thfile.write('\nendmap\n')
        if settings['addelevmap']:
            line = '\nmap ' + surveyname + settings['emapsuffix']
            thfile.write(line)
            for n in range(1, int(settings['escraps']) + 1):
                thfile.write('\n\t' + surveyname
                             + settings['escrapsuffix'] + str(n))
            thfile.write('\nendmap\n')
        if settings['addprojmap']:
            line = ('\nmap ' +
                    surveyname +
                    pemapsuffix +
                    ' -proj [elevation ' +
                    str(settings['projangle']) +
                    ']')
       #     if settings['pemapsuffixangle']:
       #         line += (str(settings['projangle'])

            thfile.write(line)
            for n in range(1, int(settings['pescraps']) + 1):
                line = ('\n\t' + surveyname +
                settings['pescrapsuffix'].replace('###',
                                            str(settings['projangle'])))
                #if settings['pemapsuffixangle']:
                    #line += str(settings['projangle']) + '_'
                line += str(n)
                thfile.write(line)
            thfile.write('\nendmap\n')
    shots2 = legdata(topodata, settings, cavers, shotsfile)
    #Write out the real files
    if settings['dothfile']:
        filename = os.path.join(fileloc, surveyname + '.th')
        thfile.write(shotsfile.getvalue())
        thfile.write('endcentreline\nendsurvey')
        CheckerCreate.CreateFile(filename, thfile, settings['overwrite'],
                                    settings['add2rep'], settings['repository'])
    if settings['dosvxfile']:
        shotsfile.seek(0)
        filename = os.path.join(fileloc, surveyname + '.svx')
        for line in shotsfile:
            if line.startswith('#'):
                line = line.replace('#', ';', 1)
            svxfile.write(line)  # currently uses therion comment symbol
        svxfile.write('\n*end ' + surveyname)
        CheckerCreate.CreateFile(filename, svxfile, settings['overwrite'],
                                    settings['add2rep'], settings['repository'])
    return shots2
