"""
Extensions to the Open Spherical Camera API specific to the insta 360 camera (2017).

Usage:
At the top of your Python script, use

  from osc.insta360_2017 import insta360_2017

After you import the library, you can use the commands like this:

  insta360 = insta360()
  insta360.state()
  insta360.info()

  # Capture image
  response = insta360.takePicture()

  # Wait for the stitching to finish
  insta360.waitForProcessing(response['id'])

  # Copy image to computer
  insta360.getLatestImage()

  insta360.closeSession()
"""

import osc
import json
import requests
import time

g_insta360Options = [
        "photoStitchingSupport",
        "photoStitching"
]

class insta360(osc.OpenSphericalCamera):
    insta360Options = g_insta360Options

    # Instance variables / methods
    def __init__(self, ip_base="192.168.42.1", httpPort=80):
        self.sid = None
        self.fingerprint = None
        self._api = None

        self._ip = ip_base
        self._httpPort = httpPort
        self._httpUpdatesPort = httpPort


    def getOptionNames(self):
        defaultOptions = self.oscOptions
        # this camera does not support 'fileFormatSupport' and produced error when getting all options
        # removing it, adding two camera specific options
        while 'fileFormatSupport' in defaultOptions: defaultOptions.remove('fileFormatSupport')
        return defaultOptions + self.insta360Options

    def getAllOptions(self):
        """
        Helper function that will get the value for all options.
        """
        url = self._request("commands/execute")
        body = json.dumps({"name": "camera.getOptions",
                 "parameters": {
                    "optionNames": self.getOptionNames()
                 }
             })
        try:
            req = requests.post(url, data=body)
        except Exception as e:
            self._httpError(e)
            return None

        if req.status_code == 200:
            response = req.json()
            returnOptions = response["results"]["options"]
        else:
            self._oscError(req)
            returnOptions = None
        return returnOptions

    def setOption(self, option, value):
        """
        Set an option to a value. The validity of the option is checked. The
        validity of the value is not.  

        Reference:
        https://developers.google.com/streetview/open-spherical-camera/reference/camera/setoptions
        https://developers.theta360.com/en/docs/v2/api_reference/commands/camera.set_options.html
        
        API Level 2 does not use session or sessionID
        """
        if option not in self.getOptionNames():
            response = None
            return response

        print( "setOption - %s : %s" % (option, value) )

        url = self._request("commands/execute")
        body = json.dumps({"name": "camera.setOptions",
             "parameters": {
                "sessionId": self.sid,
                "options": {
                        option: value,
                        }
             }
             })
        try:
            req = requests.post(url, data=body)
        except Exception as e:
            self._httpError(e)
            return None

        if req.status_code == 200:
            response = req.json()
            #print( "setOption suceeeded - %s " % response )
        else:
            self._oscError(req)
            response = None

        return response

    def takePicture(self):
        """
        Take a still image.  The sessionId is either taken from
        startSession or from state.  You can change the mode
        from video to image with captureMode in the options.

        Reference:
        https://developers.google.com/streetview/open-spherical-camera/reference/camera/takepicture
        """
        # if self.sid == None:
        #     response = None
        #     return response
        url = self._request("commands/execute")
        body = json.dumps({"name": "camera.takePicture",
             "parameters": {
                "sessionId": self.sid
             }
             })
        try:
            req = requests.post(url, data=body)
        except Exception as e:
            self._httpError(e)
            return None

        if req.status_code == 200:
            response = req.json()
        else:
            self._oscError(req)
            response = None
        return response


    def listFiles(self, fileType="all", entryCount = 300, maxThumbSize = None):
        """
        fileType:
                Type of the file to be listed: "image", "video", "all"
        startPostion:
                (optional). If omitted , start is 0
        entryCount:
                No. of still images and video files to be acquired
        maxThumbSize:
                (Optional) Maximum size of thumbnail images; 
                max(thumbnail_width, thumbnail_height).  Set to null to 
                omit thumbnails from result

        Reference:
        https://developers.google.com/streetview/open-spherical-camera/reference/camera/listfiles
        """
        parameters = {
                "fileType": fileType,
                "entryCount": entryCount,
                "maxThumbSize": maxThumbSize
             }

        url = self._request("commands/execute")
        body = json.dumps({"name": "camera.listFiles",
             "parameters": parameters
             })
        try:
            req = requests.post(url, data=body)
        except Exception as e:
            self._httpError(e)
            return None

        if req.status_code == 200:
            response = req.json()
        else:
            self._oscError(req)
            response = None
        return response

    def waitForProcessing(self, command_id, maxWait=20):
        """
        Helper function that will poll the camera until the status to changes 
        to 'done' or the timeout is hit.

        Reference:
        https://developers.google.com/streetview/open-spherical-camera/guides/osc/commands/status
        """

        print( "Waiting for processing")
        for i in range(maxWait):
            status = self.status(command_id)
            if status == "done":
                print( "Image processing finished" )
                return status 
            elif not status or "error" in status:
                print( "Status failed. Stopping wait." )
                break
            print( "%d - %s" % (i, status) )
            time.sleep( 1 )

        return
