#!/usr/bin/env python
"""
`manageHFRadarCompactModelData.py`
==================================

"""

import os
import sys
import time
import datetime
import subprocess
import socket
import paramiko
import smtplib
import email
try:
    from ConfigParser import SafeConfigParser # for python 2.x
except:
    from configparser import SafeConfigParser # for python 3+

# TODO remove below
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders

# wd = os.path.dirname(os.path.abspath(__file__)) 
wd = os.path.abspath(os.path.curdir) # XXX might not be portable... 

def main(configfile, verbose = 0):
    """Read configuration, queue and send HF Radar data to vehicles."""
    print "{0}".format(datetime.datetime.utcnow())
    parser = SafeConfigParser()                                             
    try: parser.read(configfile.name)     
    except: parser.read(configfile) # in case arg input as string
    if verbose: print parser.sections()
    for vehicle in parser.sections():
        kw = boolean_strings(dict(parser.items(vehicle)))
        handle_data_for_vehicle(vehicle, **kw)


def boolean_strings(d): # There should be a builtin method somewhere...
    for k, v in d.iteritems():
        if v in ('True','true','TRUE','Yes','yes','YES','1'):
            d[k] = True
        elif v in ('False','false','FALSE','No','no','NO','0'):
            d[k] = False
    return d
        

def handle_data_for_vehicle(vehicle, 
        username=None,
        imei=None, encode_key='TethysEncryption', from_address = None, 
        lrauv_root=os.path.join('/','mnt','mmc','LRAUV'),
        queue=True, send_via_sftp=True, send_via_iridium=False):
    """Handle the HF Radar compact model data for a specific vehicle.

    """
    if queue:
        cmd = '/usr/local/bin/octave -q --eval "queueOutgoing(\'{0}\')"'.format(vehicle)
        retcode = subprocess.call(cmd, cwd=wd, shell=True)
    if (username is not None) and send_via_sftp:
        print 'Sending to', vehicle, 'via SFTP'
        try: 
            sendViaSFTP(vehicle, lrauv_root, username)
        except socket.error, e:
            print 'socket.error: ', e
            print 'Could not connect to {0} via network.'.format(vehicle)
    if (imei is not None) and (from_address is not None) and send_via_iridium:
        print 'Sending to', vehicle, 'via Iridium SBD'
        sendViaIridium(vehicle, imei, encode_key, from_address)
    print 'Done handling HF radar updates for vehicle', vehicle
   

def sendViaSFTP(host, lrauvPath, username='root', port=22):
    # figure out which files to copy
    localdir = os.path.join(wd, 'outgoing', host)
    filesToCopy = [os.path.join(localdir, fn) 
            for fn in os.listdir(localdir) 
            if len(fn) == 10 and fn.isdigit()]
    filesToCopy.sort()
    # set up transport layer
    transport = paramiko.Transport((host, port))
    private_key_path = os.path.expanduser('~/.ssh/lrauv_rsa')
    rsa_key = paramiko.RSAKey.from_private_key_file(private_key_path)
    transport.connect(username=username, pkey=rsa_key)
    cex = transport.get_exception() # Why do I have to do this explicitly?
    if cex: 
        transport.close()
        raise cex
    sftp = paramiko.SFTPClient.from_transport(transport)
    # put files on vehicle
    remotedir = os.path.join(lrauvPath,'Data','HFRadarModel')
        # TODO test and mkdir
        #sftp.mkdir(remoteDir)
    for src in filesToCopy:
        fn = os.path.basename(src)
        dst = os.path.join(remotedir, fn)
        atts = sftp.put(src, dst)
        if atts.st_size == 208: # TODO check md5sum as well...
            print 'Moved {0}, {1}, {2}'.format(fn, atts.st_size, atts.st_atime)
            # TODO archive in 'sent', same as Iridium
            os.remove(src)
    # remove 'extra' files on vehicle (why limit to 49?)
        #files = filter( validFileFilter, sftp.listdir(remotedir) )
    remoteFiles = [fn for fn in sftp.listdir(remotedir) 
            if (len(fn) is 10 and fn.isdigit())]
    remoteFiles.sort() # in-place list sort
    while( len(remoteFiles) > 49 ):
        fileToRemove = remoteFiles.pop(0)
        sftp.remove(os.path.join(remotedir, fileToRemove))
        print 'Removing old {0}'.format(fileToRemove)

def validFileFilter(name):
    return len(name)==10 and name.isdigit()

def sendViaIridium( target, imei, encodeKey, from_address ):
    local_lrauv_root = os.path.split(os.path.split(wd)[0])[0]
    localdir = os.path.join(wd, 'outgoing', target)
    filesToSend = filter( validFileFilter, os.listdir(localdir) )
    filesToSend.sort()
    for file in filesToSend:
        localfile = "%s/%s" %( localdir, file )
        targetfile = "Data/HFRadarModel/%s" % file
        encodeCmd = "Tools/encode2Send.py %s %s %s" % ( localfile, targetfile, encodeKey )
        ret = subprocess.call(encodeCmd, shell=True, cwd=local_lrauv_root)
        # NOTE encode2Send expects to be run from a specific directory
        sbdfile = os.path.join(local_lrauv_root, "%s.000.sbd" % file)
        send_mail(from_address,
                ['data@sbd.iridium.com',from_address],
                imei,file,[sbdfile])
        localfileBackup = "%s/%s.sent" %( localdir, file )
        os.rename(localfile,localfileBackup)
        os.remove(sbdfile)
        
def send_mail(send_from, send_to, subject, text, files=[], server="localhost"):
  assert type(send_to)==list
  assert type(files)==list

  msg = MIMEMultipart()
  msg['From'] = send_from
  msg['To'] = COMMASPACE.join(send_to)
  msg['Date'] = formatdate(localtime=True)
  msg['Subject'] = subject

  msg.attach( MIMEText(text) )

  for f in files:
    part = MIMEBase('application', "octet-stream")
    print( f)
    part.set_payload( open(f,"rb").read() )
    Encoders.encode_base64(part)
    part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
    msg.attach(part)

  smtp = smtplib.SMTP(server)
  smtp.set_debuglevel(1)
  smtp.sendmail(send_from, send_to, msg.as_string())
  print "Sent email from %s to %s with msg=%s" % (send_from, send_to, msg.as_string())
  smtp.close()

if __name__ == '__main__':
    try:
        import argparse
        parser = argparse.ArgumentParser(description='Data manager for HF Radar compact model.')
        parser.add_argument('-V', '--version', action='version',                    
                    version='%(prog)s 0.0.1',                                               
            help='display version information and exit')                            
        parser.add_argument('-v', '--verbose', action='count', default=0,           
                    help='display verbose output')                                          
        parser.add_argument('-c', '--configfile', metavar='filename',                  
                    type=argparse.FileType('r'), 
                    default='manageHFRadarCompactModelData.ini',                     
                    help='configuration file')
        # TODO use mutually-exclusive args to override config file and handle
        # each vehicle manually
        args = parser.parse_args()                                         
        main(**args.__dict__) #TODO more pythonic?     
    except: # they probably don't have argparse
        main(configfile='manageHFRadarCompactModelData.ini')
