#!/usr/bin/env python
# $Id$
#
# Copyright 2013 Matthew Wall
#
# 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 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 http://www.gnu.org/licenses/

"""Command line utility for configuring LaCrosse WS-28xx weather stations

Many thanks to Eddie De Pieri, who did the first python implementation of a
WS-28xx driver, and Lucas Heijst, who sorted out the communication and timing
issues between weather station console and transceiver.
"""

import optparse
import syslog
import time
import sys

import weewx.drivers.ws28xx
import weewx.units
import weeutil.weeutil

description = """Configuration utility for WS-28xx weather stations."""

usage = """%prog [config_file] [options] [--debug]"""

epilog = """Mutating actions will request confirmation before proceeding."""

def main():
    syslog.openlog('wee_config_ws28xx', syslog.LOG_PID|syslog.LOG_CONS)

    # Create a command line parser:
    parser = optparse.OptionParser(description=description, usage=usage, epilog=epilog)
    
    # Add the various options:
    parser.add_option("--config", dest="cfgfn", type=str, metavar="FILE",
                      help="configuration file")
    parser.add_option("--check-transceiver", dest="check", action="store_true",
                      help="check USB transceiver")
    parser.add_option("--pair", dest="pair", action="store_true",
                      help="pair the USB transceiver with a station console")
    parser.add_option("--info", dest="info", action="store_true",
                      help="display weather station configuration")
    parser.add_option("--set-interval", dest="interval", type=int, metavar="N",
                      help="set logging interval to N minutes")
    parser.add_option("--current", dest="current", action="store_true",
                      help="get the current weather conditions")
    parser.add_option("--history-since", dest="recmin", type=int, metavar="N",
                      help="display history records since N minutes ago")
    parser.add_option("--history", dest="nrecords", type=int, metavar="N",
                      help="display N history records")
    parser.add_option("--maxtries", dest="maxtries", type=int,
                      help="maximum number of retries, 0 indicates no max")
    parser.add_option("-y", dest="noprompt", action="store_true",
                      help="answer yes to every prompt")
    parser.add_option("--debug", dest="debug", action="store_true",
                      help="display diagnostic information while running")

    # Now we are ready to parse the command line:
    (options, args) = parser.parse_args()
    if options.debug is not None:
        weewx.debug = options.debug

    config_fn, config_dict = weeutil.weeutil.read_config(options.cfgfn, args)
    print 'Using configuration file %s' % config_fn

    print 'Driver version %s' % weewx.drivers.ws28xx.DRIVER_VERSION
    altitude_m = weewx.units.getAltitudeM(config_dict)
    station = weewx.drivers.ws28xx.WS28xx(altitude=altitude_m,
                                          **config_dict['WS28xx'])

    if options.noprompt:
        prompt = False
    else:
        prompt = True

    maxtries = 3 if options.maxtries is None else int(options.maxtries)
    if options.check:
        check_transceiver(station, maxtries)
    elif options.pair:
        pair(station, maxtries)
    elif options.interval is not None:
        set_interval(station, maxtries, options.interval, prompt)
    elif options.current:
        current(station, maxtries)
    elif options.nrecords is not None:
        history(station, maxtries, count=options.nrecords)
    elif options.recmin is not None:
        ts = int(time.time()) - options.recmin * 60
        history(station, maxtries, ts=ts)
    else:
        info(station, maxtries)

    station.shutDown()



def check_transceiver(station, maxtries):
    """See if the transceiver is installed and operational."""
    print 'Checking for transceiver...'
    ntries = 0
    while ntries < maxtries:
        ntries += 1
        if station.transceiver_is_present():
            print 'Transceiver is present'
            sn = station.get_transceiver_serial()
            print 'serial: %s' %  sn
            tid = station.get_transceiver_id()
            print 'id: %d (0x%04x)' % (tid, tid)
            break
        print 'Not found (attempt %d of %d) ...' % (ntries, maxtries)
        time.sleep(5)
    else:
        print 'Transceiver not responding.'

def pair(station, maxtries):
    """Pair the transceiver with the station console."""
    print 'Pairing transceiver with console...'
    maxwait = 90 # how long to wait between button presses, in seconds
    ntries = 0
    while ntries < maxtries or maxtries == 0:
        if station.transceiver_is_paired():
            print 'Transceiver is paired to console'
            break
        ntries += 1
        msg = 'Press and hold the [v] key until "PC" appears'
        if maxtries > 0:
            msg += ' (attempt %d of %d)' % (ntries, maxtries)
        else:
            msg += ' (attempt %d)' % ntries
        print msg
        now = start_ts = int(time.time())
        while now - start_ts < maxwait and not station.transceiver_is_paired():
            time.sleep(5)
            now = int(time.time())
    else:
        print 'Transceiver not paired to console.'

def get_interval(station, maxtries):
    cfg = get_config(station, maxtries)
    if cfg is None:
        return None
    return weewx.drivers.ws28xx.getHistoryInterval(cfg['history_interval'])

def get_config(station, maxtries):
    start_ts = None
    ntries = 0
    while ntries < maxtries or maxtries == 0:
        cfg = station.get_config()
        if cfg is not None:
            return cfg
        ntries += 1
        if start_ts is None:
            start_ts = int(time.time())
        else:
            dur = int(time.time()) - start_ts
            print 'No data after %d seconds (press SET to sync)' % dur
        time.sleep(30)
    return None

def set_interval(station, maxtries, interval, prompt):
    """Set the station archive interval"""
    print "This feature is not yet implemented"

def info(station, maxtries):
    """Query the station then display the settings."""
    print 'Querying the station for the configuration...'
    cfg = get_config(station, maxtries)
    if cfg is not None:
        print_dict(cfg)

def current(station, maxtries):
    """Get current weather observation."""
    print 'Querying the station for current weather data...'
    start_ts = None
    ntries = 0
    while ntries < maxtries or maxtries == 0:
        packet = station.get_observation()
        if packet is not None:
            print_dict(packet)
            break
        ntries += 1
        if start_ts is None:
            start_ts = int(time.time())
        else:
            dur = int(time.time()) - start_ts
            print 'No data after %d seconds (press SET to sync)' % dur
        time.sleep(30)

def history(station, maxtries, ts=0, count=0):
    """Display the indicated number of records or the records since the 
    specified timestamp (local time, in seconds)"""
    print "Querying the station for historical records..."
    ntries = 0
    last_n = n = nrem = None
    last_ts = now = int(time.time())
    station.start_caching_history(since_ts=ts, num_rec=count)
    while nrem is None or nrem > 0:
        if ntries >= maxtries:
            print 'Giving up after %d tries' % ntries
            break
        time.sleep(30)
        ntries += 1
        now = int(time.time())
        n = station.get_num_history_scanned()
        if n == last_n:
            dur = now - last_ts
            print 'No data after %d seconds (press SET to sync)' % dur
        else:
            ntries = 0
            last_ts = now
        last_n = n
        nrem = station.get_uncached_history_count()
        ni = station.get_next_history_index()
        li = station.get_latest_history_index()
        msg = "  scanned %s records: current=%s latest=%s remaining=%s\r" % (n, ni, li, nrem)
        sys.stdout.write(msg)
        sys.stdout.flush()
    station.stop_caching_history()
    records = station.get_history_cache_records()
    station.clear_history_cache()
    print
    print 'Found %d records' % len(records)
    for r in records:
        print r

def print_dict(data):
    for x in sorted(data.keys()):
        if x == 'dateTime':
            print '%s: %s' % (x, weeutil.weeutil.timestamp_to_string(data[x]))
        else:
            print '%s: %s' % (x, data[x])


if __name__=="__main__" :
    main()
