#!/usr/bin/env python
#
# Cloudlet Infrastructure for Mobile Computing
#
#   Author: Junjue Wang <junjuew@cs.cmu.edu>
#
#   Copyright (C) 2011-2013 Carnegie Mellon University
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

import multiprocessing
import Queue
from optparse import OptionParser
import os
import pprint
import struct
import sys
import time
import pdb
dir_file = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(dir_file, "../.."))
import gabriel
import gabriel.proxy
import json
import cv2
import numpy as np
import base64
LOG = gabriel.logging.getLogger(__name__)
ANDROID_CLIENT=True

def process_command_line(argv):
    VERSION = 'gabriel proxy : %s' % gabriel.Const.VERSION
    DESCRIPTION = "Gabriel cognitive assistance"

    parser = OptionParser(usage='%prog [option]', version=VERSION,
            description=DESCRIPTION)

    parser.add_option(
            '-s', '--address', action='store', dest='address',
            help="(IP address:port number) of directory server")
    settings, args = parser.parse_args(argv)
    if len(args) >= 1:
        parser.error("invalid arguement")

    if hasattr(settings, 'address') and settings.address is not None:
        if settings.address.find(":") == -1:
            parser.error("Need address and port. Ex) 10.0.0.1:8081")
    return settings, args


class DummyVideoApp(gabriel.proxy.CognitiveProcessThread):

    def add_to_byte_array(self, byte_array, extra_bytes):
        return struct.pack("!{}s{}s".format(len(byte_array),len(extra_bytes)), byte_array, extra_bytes)

    def handle(self, header, data):
        # PERFORM Cognitive Assistance Processing
        LOG.info("processing: ")
        LOG.info("%s\n" % header)
        np_data=np.fromstring(data, dtype=np.uint8)
        bgr_img=cv2.imdecode(np_data,cv2.IMREAD_COLOR)
        mirror_img=cv2.flip(bgr_img,1)
        _, jpeg_img=cv2.imencode('.jpg', mirror_img)

        if ANDROID_CLIENT:
            # old version return
            if gabriel.Const.LEGACY_JSON_ONLY_RESULT:
                jpeg_str = base64.b64encode(jpeg_img)
                msg={
                    gabriel.Protocol_result.JSON_KEY_STATUS:'success',
                    gabriel.Protocol_result.JSON_KEY_IMAGE: jpeg_str,
                    gabriel.Protocol_result.JSON_KEY_IMAGES_ANIMATION: [jpeg_str],
                    gabriel.Protocol_result.JSON_KEY_SPEECH: "mirror"
                }
                return json.dumps(msg)
            # new gabriel version return
            else:
                # image data
                header[gabriel.Protocol_result.JSON_KEY_STATUS]='success'
                # numpy tostring is equal to tobytes
                rtn_data=jpeg_img.tostring()
                # header has (offset, size) for each data type
                header[gabriel.Protocol_result.JSON_KEY_IMAGE]=(0, len(rtn_data))

                # animation data
                # animation is two images: before mirror and after mirror
                offset=len(rtn_data)
                _, ori_img=cv2.imencode('.jpg', bgr_img)
                animation_data=[jpeg_img.tostring(), ori_img.tostring()]
                # animation bytes format:
                # first integer: number of frames
                # for each frame: size of the frame + data
                animation_bytes=struct.pack("!I", len(animation_data))
                for frame in animation_data:
                    frame_bytes=struct.pack("!I{}s".format(len(frame)), len(frame), frame)
                    animation_bytes=self.add_to_byte_array(animation_bytes, frame_bytes)
                rtn_data=self.add_to_byte_array(rtn_data, animation_bytes)
                header[gabriel.Protocol_result.JSON_KEY_IMAGES_ANIMATION]=(offset, len(animation_bytes))

                # speech data
                offset=len(rtn_data)
                speech="mirror"
                rtn_data=self.add_to_byte_array(rtn_data, speech)
                header[gabriel.Protocol_result.JSON_KEY_SPEECH]=(offset, len(speech))
                return rtn_data
        else:
            # python client can only handle image data
            return jpeg_img.tostring()


if __name__ == "__main__":
    result_queue = multiprocessing.Queue()
    print result_queue._reader

    settings, args = process_command_line(sys.argv[1:])
    ip_addr, port = gabriel.network.get_registry_server_address(settings.address)
    service_list = gabriel.network.get_service_list(ip_addr, port)
    LOG.info("Gabriel Server :")
    LOG.info(pprint.pformat(service_list))

    video_ip = service_list.get(gabriel.ServiceMeta.VIDEO_TCP_STREAMING_IP)
    video_port = service_list.get(gabriel.ServiceMeta.VIDEO_TCP_STREAMING_PORT)
    ucomm_ip = service_list.get(gabriel.ServiceMeta.UCOMM_SERVER_IP)
    ucomm_port = service_list.get(gabriel.ServiceMeta.UCOMM_SERVER_PORT)

    # image receiving and processing threads
    image_queue = Queue.Queue(gabriel.Const.APP_LEVEL_TOKEN_SIZE)
    print "TOKEN SIZE OF OFFLOADING ENGINE: %d" % gabriel.Const.APP_LEVEL_TOKEN_SIZE # TODO
    video_receive_client = gabriel.proxy.SensorReceiveClient((video_ip, video_port), image_queue)
    video_receive_client.start()
    video_receive_client.isDaemon = True
    dummy_video_app = DummyVideoApp(image_queue, result_queue, engine_id = 'dummy') # dummy app for image processing
    dummy_video_app.start()
    dummy_video_app.isDaemon = True

    # result publish
    result_pub = gabriel.proxy.ResultPublishClient((ucomm_ip, ucomm_port), result_queue)
    result_pub.start()
    result_pub.isDaemon = True

    try:
        while True:
            time.sleep(1)
    except Exception as e:
        pass
    except KeyboardInterrupt as e:
        sys.stdout.write("user exits\n")
    finally:
        if video_receive_client is not None:
            video_receive_client.terminate()
        if dummy_video_app is not None:
            dummy_video_app.terminate()
        #if acc_client is not None:
        #    acc_client.terminate()
        #if acc_app is not None:
        #    acc_app.terminate()
        result_pub.terminate()

