"""
    mahali_cloud_service.py

    $Id: mahali_cloud_service.py 476 2015-08-13 21:20:35Z flind $

    This service convert performs data synchronization to the cloud for a
    persistently connected environment.

"""
#
#
#------------------------------------------------------------
#          imports
#------------------------------------------------------------
#
import time
import sys
import os
import io
import optparse
import string
import json
import redis
import subprocess               # 'system' call
import uuid                     # unique id
import daemon                   # for running as a daemon

from mahali_common import *
#
#------------------------------------------------------------
#          globals
#------------------------------------------------------------
#

DEBUG = False

# constants for the future INI file:
#


#
#-------------------------------------------------------------------
#
class ExceptionString(Exception):
    """ Simple exception handling string """
    def __str__(self):
          return repr(self.args[0])
#
#-------------------------------------------------------------------
#
#-------------------------------------------------------------------


# -------------------------------
#       sync to owncloud
# -------------------------------
#
def cloud_sync_owncloud(options, logQ, config, sync_dir):

  if options.debug:
      logQ.logDebug('cloud_sync to owncloud for source %s' % (sync_dir))
      sync_start = time.time()

  # need to build proper paths?

  base_uri = config['mahali-cloud-service']['cloud_uri']
  target_path = config['mahali-cloud-service']['cloud_target_path']
  site_path = config['mahali-site']['name'] + '-' + config['mahali-site']['id']

  base_uri = fixup_path(base_uri)
  target_path = fixup_path(target_path)

  sync_dest = string.split(sync_dir,'/data')[-1]

  dest_uri = base_uri + target_path + site_path + sync_dest

  if options.debug and options.verbose:
      logQ.logDebug('cloud_sync source %s to dest %s' % (sync_dir, dest_uri))

  # p =
  if options.simulate:
      logQ.logInfo('simulate owncloudcmd call for synchronization.')
      logQ.logDebug('simulate synchronize src %s dest %s' % (sync_dir, dest_uri))
  else:
      if self.config['mahali-cloud-service']['cloud_mode'] == 'relay':
          logQ.logInfo('owncloud synchronize in relay mode')
          subprocess.check_call(["/data/software/mahali/bin/mahali_owncloud_refresh.sh"])
      else:
          logQ.logInfo('owncloud synchronize in direct mode')
          subprocess.check_call(["owncloudcmd",
                                "--non-interactive",
                                "--silent",
                                "-n",
                                sync_dir,
                                dest_uri
                                ])
                                #,stdout=subprocess.PIPE,
                                #stderr=subprocess.STDOUT)

  #while True:
  #  line = p.stdout.readline()  # blocks?
  #  if not line:
  #     break
  #  print line

  if options.debug:
      sync_complete = time.time()
      sync_time = sync_complete - sync_start
      logQ.logDebug('cloud sync to owncloud complete in time %s seconds.' % (sync_time))

def cloud_sync_list(config):
    sync_list = []

    # paths from config
    sync_list.append(config['mahali-summary-service']['summary_path'])
    sync_list.append(config['mahali-recorder-service']['recorder_path'])
    sync_list.append(config['mahali-data-service']['ready_data_path'])
    #sync_list.append(config['mahali-command-service']['command_path'])
    #sync_list.append(config['mahali-command-service']['status_path'])

    # static paths
    # the intent here is to enable cloud based config and software updates
    # it would be nice if these were part of the config info
    # a restart is needed for config updates
    # a trigger is needed to run an install sudo script for software updates
    sync_list.append('/data/config')
    sync_list.append('/data/software')

    return sync_list

def cloud_sync(config, logQ):

    if not config['mahali-cloud-service']['cloud_sync']:
        logQ.logDebug('synchronization attempt with cloud_sync disabled!')
        return

    sync_list = cloud_sync_list(config)

    sync_cnt = 0

    for sync_dir in sync_list:
        print sync_dir
        sync_cnt += 1
        try:
            # support multiple cloud API types here!
            if config['mahali-cloud-service']['cloud_api_type'] == 'owncloud':
                cloud_sync_owncloud(options, logQ, config, sync_dir)
            else:
                logQ.logInfo('attempt to synchronize using unknown cloud service %s' % (config['mahali-cloud-service']['cloud_api_type']))
                return sync_cnt

        except Exception as eobj:
            exp_str = str(ExceptionString(eobj))
            emsg = "exception: %s. cloud_sync problem for %s " % (exp_str, sync_dir)
            logQ.logDebug(emsg)
            continue

    return sync_cnt


def parse_command_line():
    parser = optparse.OptionParser()
    parser.add_option("-v", "--verbose",action="store_true",
                       dest="verbose", default=False,help="prints debug output and additional detail.")
    parser.add_option("-d", "--debug",action="store_true",
                      dest="debug", default=False,help="run in debug mode and not service context.")
    parser.add_option("-s", "--simulate",action="store_true",
                      dest="simulate", default=False,help="simulate cloud sync commands.")
    parser.add_option("-c", "--config",dest="CPATH",help="Use configuration files in CPATH.")
    parser.add_option("-f", "--foreground",action="store_true",dest="foreground",help="Execute in foreground and not daemon context.")


    (options, args) = parser.parse_args()

    return (options, args)
# end parse_command_line
#
#-------------------------------------------------------------------
#
def mahali_cloud_service(options):

    start_time = time.time()

    # create redis interface
    redis_db = redis.StrictRedis(host='localhost', port=6379, db=0)

    if not mahali_register_service(redis_db, 'mahali-cloud-service'):
        sys.exit()

    pubsub = redis_db.pubsub()  # listener

    # create logging instance
    logQ = redisLoggerQueue('cloud-service',redis_db, options)

    # channel subcriptions, hardware static for now
    pubsub.psubscribe('mahali-cloud-command')

    mahali_cfg = mahaliConfiguration('mahali-gps-array',options.CPATH, redis_db, logQ)

    config = mahali_cfg.load('mahali-cloud-config')

    # log startup
    logQ.logInfo('mahali-cloud-service startup')

    trg_count = 0
    sync_count = 0

    try:
        for ocmsg in pubsub.listen():    # blocking call, runs forever

            if options.debug and options.verbose:
                if ocmsg:
                    print ocmsg

            if ocmsg['type'] == 'psubscribe':
                continue

            try:
                jmsg = ocmsg['data']
            except Exception as eobj:
                exp_str = str(ExceptionString(eobj))
                emsg = "exception: %s. Problem with message separation for %s." % (exp_str, str(ocmsg))
                logQ.logError(emsg)
                continue

            msg = None
            # deserialize message
            try:
                msg = deserialize_json(jmsg)
            except Exception as eobj:
                exp_str = str(ExceptionString(eobj))
                emsg = "exception: %s. Problem with message deserialization for %s." % (exp_str, str(jmsg))
                logQ.logError(emsg)
                continue

            if msg and msg['event_type'] == 'command' and msg['event'] == 'trigger':
                if (msg['event_data'] == 'cloud-sync'):
                    trg_count += 1
                    if options.debug:
                        logQ.logDebug('cloud sync event %s' % (msg))
                        try:
                            sync_count += cloud_sync(config,logQ)
                        except Exception as eobj:
                            exp_str = str(ExceptionString(eobj))
                            emsg = "exception: %s in cloud sync" % (exp_str)
                            logQ.logError(emsg)
                            continue

            # mirror state
            active_time = start_time - time.time()

            state_info = {'start_time':start_time,
                          'active_time':active_time,
                          'update_time':time.time(),
                          'trigger_count':trg_count,
                          'sync_count': sync_count}

            mahali_set_object(redis_db,logQ,'mahali-cloud-service-state', state_info)


    except KeyboardInterrupt:
        lmsg = 'exiting on keyboard interrupt'
        if logQ:
            logQ.logInfo(lmsg)
        else:
            print lmsg

    except Exception as eobj:
        exp_str = str(ExceptionString(eobj))
        emsg = "exception: %s. Problem with mahali cloud service" % (exp_str)
        if logQ:
            logQ.logError(emsg)
        else:
            print emsg

    # publish shutdown
    if logQ:
        logQ.logInfo('shutdown')
    else:
        print 'shutdown'

    """ Note : When the service exits we do not by default shutdown the power relay. This is intended to avoid turning off a working device on an error. """

    if not mahali_unregister_service(redis_db, 'mahali-cloud-service'):
        sys.exit()

    # deregister with redis
    redis_db.connection_pool.disconnect()

# end main_program
#
#-------------------------------------------------------------------
#
#                 MAIN
#
#-------------------------------------------------------------------
#

if __name__ == '__main__':

   # parse command line options
   options,args = parse_command_line()

   if options.debug:
       DEBUG = options.debug

   if options.foreground:
       print "Mahali cloud service in DEBUG mode"
       mahali_cloud_service(options)     # direct call
   else:
     with daemon.DaemonContext():
        mahali_cloud_service(options)  # called through daemonizer
