import filecmp
import json
import os

from client import SmartDataContextAPIClient

CLIENT_CERTIFICATE="client.pem"
CLIENT_CERTIFICATE_KEY="client.key"
API_URL="https://localhost/"

def generate_random_file(filename, size_in_mb):
    size_in_bytes = size_in_mb * 1024 * 1024
    with open(filename, 'wb') as f:
        f.write(os.urandom(size_in_bytes))

# Create a new SmartDataContextClient, passing a custom URL for the API and the certificates to access the domain
# The parameter verifyCertificate allows using self-signed certificates for development and test environments.
# Should not be used in production ideally
client = SmartDataContextAPIClient(cert_file=CLIENT_CERTIFICATE, key_file=CLIENT_CERTIFICATE_KEY, url=API_URL, verifyCertificate=False,
                                   logfile="requests.log")

# Creates a new perene SmartDataContext (no start not end time). The minimum required parameters are the content
# and the features, with at least one feature (tags), which indicate tags to be associated with this SmartDataContext
# The function returns the create SmartDataContext id or throws an exception in case of error
pereneSmartDataContextId = client.createSmartDataContext(content={"meta": True}, features={"tags": ["sampleTag", "simulation"]})
print(f"Create a new SmartDataContext with id {pereneSmartDataContextId}")

# Now that we have a smartDataContext we can associate it to a SmartDataUnit or to SmartDataSource
# A smartDataUnit is represented by its value
# A smartDataSource is represented by its sphere (stationary) or its signature (mobile)
# Either smartDataUnits or smartDataSources or both can be associated to a smartDataContext
# A list of smartDataContextIds could be passed as well
# The function returns the list of updated SmartDataContext object or the single SmartDataContext
smartDataUnits = [0x84963924]
smartDataSources = [ [1,1,2,10], 'aeccd287']
pereneSmartDataContext = client.associateSmartDataContext(smartDataUnits=smartDataUnits, smartDataSources=smartDataSources, smartDataContextIds=pereneSmartDataContextId)
print(f"Updated SmartDataContext \n {json.dumps(pereneSmartDataContext, indent=2)}")

# To unassociate just call the unassociateSmartDataContext following the same syntax
smartDataSources = [ [1,1,2,10] ]
pereneSmartDataContext = client.unassociateSmartDataContext(smartDataSources=smartDataSources, smartDataContextIds=pereneSmartDataContextId)
print(f"Updated SmartDataContext \n {json.dumps(pereneSmartDataContext, indent=2)}")

# To retrieve a SmartDataContext by its id
pereneSmartDataContext = client.getSmartDataContext(smartDataContextId=pereneSmartDataContextId)
print(f"Retrieved SmartDataContext \n {json.dumps(pereneSmartDataContext, indent=2)}")

# To find all SmartDataContext associated to a set of smartDataSources or smartDataUnits
smartDataContextForSources = client.findSmartDataContext(smartDataSources=smartDataSources)
print(f"Retrieved SmartDataContext for {smartDataSources}: {len(smartDataContextForSources)} \n {json.dumps(smartDataContextForSources, indent=2)}")

smartDataContextForUnits = client.findSmartDataContext(smartDataUnits=smartDataUnits)
print(f"Retrieved SmartDataContext for {smartDataSources}: {len(smartDataContextForUnits)} \n {json.dumps(smartDataContextForUnits, indent=2)}")

smartDataContextForUnitsOrSources = client.findSmartDataContext(smartDataUnits=smartDataUnits, smartDataSources=smartDataSources)
print(f"Retrieved SmartDataContext for {smartDataSources} and {smartDataUnits}: {len(smartDataContextForUnitsOrSources)} \n {json.dumps(smartDataContextForUnitsOrSources, indent=2)}")

# To find all SmartDataContext associated to a set of smartDataSources or smartDataUnits under a time range
temporalSmartDataContextId = client.createSmartDataContext(content={"meta": True}, features={"tags": ["sampleTag", "simulation"]},
                                                         smartDataUnits=smartDataUnits, t0=15, t1=25)
temporalSmartDataContext = client.getSmartDataContext(smartDataContextId=temporalSmartDataContextId)
print(f"Retrieved temporal SmartDataContext \n {json.dumps(temporalSmartDataContext, indent=2)}")


smartDataContextForUnitsOrSources = client.findSmartDataContext(smartDataUnits=smartDataUnits, smartDataSources=smartDataSources, t0=16, t1=20)
print(f"Retrieved SmartDataContext for {smartDataSources} and {smartDataUnits} between 16 and 20 : {len(smartDataContextForUnitsOrSources)} \n {json.dumps(smartDataContextForUnitsOrSources, indent=2)}")


# To find all SmartDataContext associated to a set of smartDataSources or smartDataUnits under a time range
smartDataContextForUnitsOrSources = client.findSmartDataContext(smartDataUnits=smartDataUnits, smartDataSources=smartDataSources, t0=5, t1=9)
print(f"Retrieved SmartDataContext for {smartDataSources} and {smartDataUnits} between 5 and 9 : {len(smartDataContextForUnitsOrSources)} \n {json.dumps(smartDataContextForUnitsOrSources, indent=2)}")

# To do a generic query using the MongoDB syntax
query = {
    "t0": 15
}
queryResult = client.querySmartDataContext(query)
print(f"Query result: \n {json.dumps(queryResult, indent=2)}")

# To change the contents of a SmartDataContext
temporalSmartDataContext = client.updateSmartDataContext(temporalSmartDataContextId, content={"meta": False})
print(f"Updated temporal SmartDataContext \n {json.dumps(temporalSmartDataContext, indent=2)}")

# To add an unstructured content to a SmartDataContext
generate_random_file("data.bin", 3)
storageObjectId = client.addUnstructuredDataFromFile(temporalSmartDataContextId, 'data.bin');
print(f"Stored object id is {storageObjectId}")
temporalSmartDataContext = client.getSmartDataContext(temporalSmartDataContextId);
print(f"Updated temporal SmartDataContext \n {json.dumps(temporalSmartDataContext, indent=2)}")

generate_random_file("data2.bin", 3)
storageObjectId2 = client.addUnstructuredDataFromFile(temporalSmartDataContextId, 'data2.bin');
print(f"Stored object id is {storageObjectId}")
temporalSmartDataContext = client.getSmartDataContext(temporalSmartDataContextId);
print(f"Updated temporal SmartDataContext \n {json.dumps(temporalSmartDataContext, indent=2)}")

# To retrieve an unstructured content from a SmartDataContent
client.saveUnstructuredDataToFile(temporalSmartDataContextId, storageObjectId, "data_retrieved.bin");
if filecmp.cmp("data.bin", "data_retrieved.bin"):
    print("Retrieved file data.bin is identical\n")
else:
    raise Exception("Retrieved file data.bin is not identical")
client.saveUnstructuredDataToFile(temporalSmartDataContextId, storageObjectId2, "data2_retrieved.bin");
if filecmp.cmp("data2.bin", "data2_retrieved.bin"):
    print("Retrieved file data2.bin is identical\n")
else:
    raise Exception("Retrieved file data2.bin is not identical")

os.remove("data_retrieved.bin")
os.remove("data2_retrieved.bin")
os.remove("data.bin")
os.remove("data2.bin")

# To remove a unstructured data
temporalSmartDataContext = client.removeUnstructuredData(temporalSmartDataContextId, storageObjectId)
print(f"Removed unstructured {storageObjectId} - containing unstructured count is {len(temporalSmartDataContext['unstructuredData'])} with id {temporalSmartDataContext['unstructuredData'][0]['id']}")

temporalSmartDataContext = client.removeUnstructuredData(temporalSmartDataContextId, storageObjectId2)
print(f"Removed unstructured {storageObjectId2} - containing unstructured count is {len(temporalSmartDataContext['unstructuredData'])}")