#!/usr/bin/env python3
#
# DecoTengu - dive decompression library.
#
# Copyright (C) 2013 by Artur Wroblewski <wrobell@pld-linux.org>
#
# 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/>.
#

#
# DecoTengu library support script.
#

import argparse
import logging
import re
import sys

#
# Arguments parsing
#

class ValidateAlternative(argparse.Action):
    ALT = {'tab', 'ascentjump', 'decostep'}
    def __call__(self, parser, args, values, option_string=None):
        alt = set(values.split(','))

        if 'tab' in alt:
            raise argparse.ArgumentError(
                self, 'Tabular tissue calculator not supported yet'
            )


        if alt - self.ALT:
            s = ', '.join(alt - self.ALT)
            raise argparse.ArgumentError(self,
                'Invalid alternative(s): {}'.format(s))
        setattr(args, self.dest, tuple(sorted(alt)))


parser = argparse.ArgumentParser(description='DecoTengu 0.6.0.')
parser.add_argument(
    '-v', '--verbose', action='store_true', dest='verbose', default=False,
    help='explain what is being done'
)
parser.add_argument(
    '--gf_low', '-gl', dest='gf_low', default=30, type=int,
    help='GF Low, i.e. 30 [percentage]'
)
parser.add_argument(
    '--gf_high', '-gh', dest='gf_high', default=85, type=int,
    help='GF High, i.e. 85 [percentage]'
)
parser.add_argument(
    '--gas_list', '-l', dest='gas_list',
    help='gas list, i.e. "28,0@0" or "28,0@0 50,0@22"'
)
parser.add_argument(
    '-6', dest='last_stop_6m', default=False, action='store_true',
    help='last decompression stop at 6m'
)
parser.add_argument(
    '--pressure', '-p', dest='pressure', default=None, type=int,
    help='atmosphere pressure, i.e. 1013 [millibar]'
)
#parser.add_argument('--altitude', '-a', dest='altitude', default=None,
#        type=int, help='set altitude, i.e. 700m')
parser.add_argument(
    '--model', '-m', dest='model', default='zh-l16b-gf',
    choices=('zh-l16b-gf', 'zh-l16c-gf'), help='decompression model'
)
parser.add_argument(
    '--descent-rate', dest='descent_rate', default=20.0, type=float,
    help='descent rate, i.e. 20 [m/min]'
)
parser.add_argument(
    '--time-delta', '-t', dest='time_delta', default=None, type=float,
    help='slice dive time into chunks to create dive steps every time delta' \
        ' value, i.e. 60, 0.5 [s]; no slicing by default'
)
#parser.add_argument('--no-descent', '-nd', dest='descent',
#        default=True, type=bool, help='decompression model')
parser.add_argument(
    '--tissue-file', '-f', dest='tissue_file',
    default=None, type=str, help='tissue saturation data output file'
)
parser.add_argument(
    '--use', dest='alt',
    default=(), type=str, action=ValidateAlternative,
    help='list of alternative implementations to be used: ascentjump,'  \
        ' decostep, tab'
)
parser.add_argument('depth', type=int, help='dive maximum depth [meter]')
parser.add_argument('time', type=int, help='dive bottom time [minute]')
args = parser.parse_args()

if args.verbose:
    logging.basicConfig(level=logging.DEBUG)
else:
    logging.basicConfig(level=logging.WARN)

#
# Create DecoTengu objects and configure
#

import decotengu
from decotengu.alt.tab import TabTissueCalculator, FirstStopTabFinder
from decotengu.alt.naive import AscentJumper, DecoStopStepper
from decotengu.output import DiveStepInfoGenerator, csv_writer
from decotengu.flow import sender

engine, deco_table = decotengu.create(time_delta=args.time_delta)
engine.last_stop_6m = args.last_stop_6m
pipeline = []

if args.tissue_file:
    f = open(args.tissue_file, 'w')
    csv_writer = csv_writer(f)
    info = DiveStepInfoGenerator(engine, csv_writer)
    pipeline.append(info)

if args.model == 'zh-l16b-gf':
    engine.model = decotengu.ZH_L16B_GF()
elif args.model == 'zh-l16c-gf':
    engine.model = decotengu.ZH_L16C_GF()
else:
    assert False, 'unknown decompression model'

engine.model.gf_low = args.gf_low / 100
engine.model.gf_high = args.gf_high / 100

if args.pressure is not None:
    engine.surface_pressure = int(args.pressure) / 1000
engine.descent_rate = args.descent_rate

if args.gas_list:
    for mix in args.gas_list.split():
        o2, he, depth = re.split('[,@]', mix)
        travel = o2[0] == '+'
        engine.add_gas(int(depth), int(o2), int(he), travel=travel)
else:
    engine.add_gas(0, 21)

if 'ascentjump' in args.alt:
    engine._free_ascent = AscentJumper(engine)
if 'decostep' in args.alt:
    engine._deco_ascent = DecoStopStepper(engine)
if 'tab' in args.alt:
    engine._find_first_stop = FirstStopTabFinder(engine)
    engine._deco_ascent = DecoStopStepper(engine)

    calc = TabTissueCalculator(
        engine.model.N2_HALF_LIFE,
        engine.model.HE_HALF_LIFE
    )
    engine.model.calc = calc
    engine.conveyor.time_delta = 60

#
# Execute calculations and provide summary
#

f = sender(engine.calculate, *pipeline)
data = f(args.depth, args.time)
for s in data: pass

print('Dive profile: {:3}m for {}min'.format(args.depth, args.time))
print('Descent rate: {}m/min'.format(engine.descent_rate))
print('Ascent rate: {}m/min'.format(engine.ascent_rate))
print()
print('GF Low: {}%'.format(int(engine.model.gf_low * 100)))
print('GF High: {}%'.format(int(engine.model.gf_high * 100)))
print('Surface pressure: {:.2f} millibar'.format(engine.surface_pressure * 1000))
print()

print('Gas list:')
for m in engine._travel_gas_list:
    print(' o2={}%, he={}% at {}m (travel gas mix)'.format(m.o2, m.he, m.depth))
for m in engine._gas_list:
    print(' o2={}%, he={}% at {}m'.format(m.o2, m.he, m.depth))
print()

stops = deco_table.stops
if stops:
    print('Decompression stops ({}):'.format(args.model.upper()))
    for stop in stops:
        print(' {:4.0f}m {:3}min'.format(stop.depth, stop.time))
    print('-' * 13)
    print('Sum: {:5}min'.format(deco_table.total))
else:
    print('No decompression dive ({}).'.format(args.model.upper()))

# vim: sw=4:et:ai
