import requests
# install the requests Python package
# pip install requests   or   easy_install requests
import json
import hmac
import base64
import hashlib
import sys
from datetime import datetime
import calendar
import uuid
import gzip
from io import BytesIO

""" Procedure -->
1. make an init call
    - check if game is disabled
    - calculate client timestamp offset from server time
2. start a session
3. add a user event (session start) to queue
4. add a business event + some design events to queue
5. submit events in queue
6. add some design events to queue
7. add session_end event to queue
8. submit events in queue
"""

# device information
platform = 'ios'
os_version = 'ios 8.2'
sdk_version = 'rest api v2'
device = 'iPhone6.1'
manufacturer = 'apple'
user_id = "aaaaa-bbbb"

# game information
build_version = 'alpha 0.0.1'
engine_version = 'unity 5.1.0'

# sandbox game keys
game_key = "5c6bcb5402204249437fb5a7a80a4959"
secret_key = "16813a12f718bc5c620f56944e1abc3ea13ccbac"

live_game_key = "<your_game_key>"
live_secret_key = "<your_secret_key>"

# sandbox API URLs
url_init = 'https://sandbox-api.gameanalytics.com/v2/' + game_key + '/init'
url_events = 'https://sandbox-api.gameanalytics.com/v2/' + game_key + '/events'

#live API URLs
url_init_live = 'https://api.gameanalytics.com/v2/' + live_game_key + '/init'
url_events_live = 'https://api.gameanalytics.com/v2/' + live_game_key + '/events'


# settings
use_gzip = True
verbose_log = False


# global state to track changes when code is running
state_config = {
    # the amount of seconds the client time is offset by server_time
    # will be set when init call receives server_time
    'client_ts_offset': 0,
    # will be updated when a new session is started
    'session_id': None,
    # set if SDK is disabled or not - default enabled
    'enabled': True,
    # event queue - contains a list of event dictionaries to be JSON encoded
    'event_queue': []
}


# main entry point
def run():
    print("")
    print("----- GameAnalytics REST V2 Example --------")
    print ("Gzip enabled!" if use_gzip else "Gzip disabled!")
    print("--------------------------------------------")

    # 1. init call
    init_response, init_response_code = request_init()

    # calculate client_ts offset from server
    update_client_ts_offset(init_response['server_ts'])

    print("Init call successful !")
    print_verbose("Init response: " + str(init_response))

    # 2. send events (only one business event)
    if state_config['enabled'] is False:
        print("SDK disabled. Will not continue event submit.")
        sys.exit()

    # generate session id
    generate_new_session_id()

    # add session start event (user event)
    add_to_event_queue(get_test_user_event())

    # add business event
    add_to_event_queue(get_test_business_event_dict())

    # add design events
    add_to_event_queue(get_test_design_event("player:death:shotgun"))
    add_to_event_queue(get_test_design_event("player:kill:blue_yeti", value=23.9))
    add_to_event_queue(get_test_design_event("player:kill:black_yeti", value=90.3))

    # submit the event queue and clear it
    response_data, response_code = submit_events()

    # add more design events
    add_to_event_queue(get_test_design_event("player:kill:black_yeti", value=90.9))
    add_to_event_queue(get_test_design_event("player:death:black_yeti"))
    add_to_event_queue(get_test_design_event("player:kill:golden_goose", value=21.5))

    # end session: add session end event
    add_to_event_queue(get_test_session_end_event(length_in_seconds=200))

    # submit the event queue and clear it
    response_data, response_code = submit_events()

    sys.exit()


# adding an event to the queue for later submit
def add_to_event_queue(event_dict):
    if not isinstance(event_dict, dict):
        return

    annotate_event_with_default_values(event_dict)
    state_config['event_queue'].append(event_dict)


# requesting init URL and returning result
def request_init():
    init_payload = {
        'platform': platform,
        'os_version': os_version,
        'sdk_version': sdk_version,
        'user_id': user_id
    }

    init_payload_json = json.dumps(init_payload)

    headers = {
        'Authorization': hmac_hash_with_secret(init_payload_json.encode('utf-8'), secret_key),
        'Content-Type': 'application/json'
    }

    response_dict = None
    status_code = None

    try:
        init_response = requests.post(url_init, data=init_payload_json, headers=headers)
    except:
        print("Init request failed!")
        sys.exit()

    status_code = init_response.status_code

    try:
        response_dict = init_response.json()
    except:
        response_dict = None

    if not isinstance(status_code, int):
        print("---- Submit Init ERROR ----")
        print("URL: " + str(url_init))
        print("Payload JSON: " + str(init_payload_json))
        print("Headers: " + str(headers))

    response_string = ("" if status_code is None else "Returned: " + str(status_code) + " response code.")

    if status_code == 401:
        print("Submit events failed due to UNAUTHORIZED.")
        print("Please verify your Authorization code is working correctly and that your are using valid game keys.")
        sys.exit()

    if status_code != 200:
        print("Init request did not return 200!")
        print(response_string)
        if isinstance(response_dict, dict):
            print(response_dict)
        elif isinstance(init_response.text, basestring):
            print("Response contents: " + init_response.text)
        sys.exit()

    # init request ok --> get values
    if 'server_ts' not in response_dict or not isinstance(response_dict['server_ts'], int):
        print("Init request failed! Did not return proper server_ts..")
        sys.exit()

    if 'enabled' in response_dict and response_dict['enabled'] is False:
        state_config['enabled'] = False

    return (response_dict, status_code)


# submitting all events that are in the queue to the events URL
def submit_events():
    try:
        event_list_json = json.dumps(state_config['event_queue'])
        if verbose_log:
            print(event_list_json)
    except:
        print("Event queue failed JSON encoding!")
        event_list_json = None
        return

    # clear event queue
    state_config['event_queue'] = []

    if event_list_json is None:
        return

    # if gzip enabled
    if use_gzip is True:
        event_list_json = get_gzip_string(event_list_json.encode('utf-8'))
    else:
        event_list_json = event_list_json.encode('utf-8')

    # create headers with authentication hash
    headers = {
        'Authorization': hmac_hash_with_secret(event_list_json, secret_key),
        'Content-Type': 'application/json'
    }

    # if gzip enabled add the encoding header
    if use_gzip is True:
        headers['Content-Encoding'] = 'gzip'

    try:
        events_response = requests.post(url_events, data=event_list_json, headers=headers)
    except Exception as e:
        print("Submit events request failed!")
        print(e.reason)
        return (None, None)

    status_code = events_response.status_code
    try:
        response_dict = events_response.json()
    except:
        response_dict = None

    print_verbose("Submit events response: " + str(response_dict))

    # check response code
    status_code_string = ("" if status_code is None else "Returned: " + str(status_code) + " response code.")
    if status_code == 400:
        print(status_code_string)
        print("Submit events failed due to BAD_REQUEST.")

        if isinstance(response_dict, (dict, list)):
            print("Payload found in response. Contents can explain what fields are causing a problem.")
            print(events_response.text)
            sys.exit()
    elif status_code == 401:
        print(status_code_string)
        print("Submit events failed due to UNAUTHORIZED.")
        print("Please verify your Authorization code is working correctly and that your are using valid game keys.")
        sys.exit()
    elif status_code != 200:
        print(status_code_string)
        print("Submit events request did not succeed! Perhaps offline.. ")
        sys.exit()

    if not isinstance(response_dict, dict):
        print("Submit events request returned 200, but reading and JSON decoding response failed..")
        print(events_response.text)
        sys.exit()

    if status_code == 200:
        print("Events submitted !")
    else:
        print("Event submission FAILED!")

    return (response_dict, status_code)


# ------------------ HELPER METHODS ---------------------- #


def generate_new_session_id():
    state_config['session_id'] = str(uuid.uuid1())
    print_verbose("Session Id: " + state_config['session_id'])


def update_client_ts_offset(server_ts):
    # calculate client_ts using offset from server time
    now_ts = datetime.utcnow()
    client_ts = calendar.timegm(now_ts.timetuple())
    offset = client_ts - server_ts

    # if too small difference then ignore
    if offset < 10:
        state_config['client_ts_offset'] = 0
    else:
        state_config['client_ts_offset'] = offset
    print_verbose('Client TS offset calculated to: ' + str(offset))


def hmac_hash_with_secret(message, key):
    return base64.b64encode(hmac.new(key.encode('utf-8'), message, digestmod=hashlib.sha256).digest())


def get_test_business_event_dict():
    event_dict = {
        'category': 'business',
        'amount': 999,
        'currency': 'USD',
        'event_id': 'Weapon:SwordOfFire',  # item_type:item_id
        'cart_type': 'MainMenuShop',
        'transaction_num': 1,  # should be incremented and stored in local db
        'receipt_info': {'receipt': 'xyz', 'store': 'apple'}  # receipt is base64 encoded receipt
    }
    return event_dict


def get_test_user_event():
    event_dict = {
        'category': 'user'
    }
    return event_dict


def get_test_session_end_event(length_in_seconds=0):
    event_dict = {
        'category': 'session_end',
        'length': length_in_seconds
    }
    return event_dict


def get_test_design_event(event_id, value=None):
    event_dict = {
        'category': 'design',
        'event_id': event_id,
    }
    if isinstance(value, (int, float)):
        event_dict['value'] = value

    return event_dict


def get_gzip_string(string_for_gzip):
    zip_text_file = BytesIO()
    zipper = gzip.GzipFile(mode='wb', fileobj=zip_text_file)
    zipper.write(string_for_gzip)
    zipper.close()

    enc_text = zip_text_file.getvalue()
    return enc_text


# add default annotations (will alter the dict by reference)
def annotate_event_with_default_values(event_dict):
    now_ts = datetime.utcnow()
    # calculate client_ts using offset from server time
    client_ts = calendar.timegm(now_ts.timetuple()) - state_config['client_ts_offset']

    # TEST IDFA / IDFV
    idfa = 'AEBE52E7-03EE-455A-B3C4-E57283966239'
    idfv = 'AEBE52E7-03EE-455A-B3C4-E57283966239'

    default_annotations = {
        'v': 2,                                     # (required: Yes)
        'user_id': idfa,                            # (required: Yes)
        'ios_idfa': idfa,                           # (required: No - required on iOS)
        'ios_idfv': idfv,                           # (required: No - send if found)
        # 'google_aid'                              # (required: No - required on Android)
        # 'android_id',                             # (required: No - send if set)
        # 'googleplus_id',                          # (required: No - send if set)
        # 'facebook_id',                            # (required: No - send if set)
        # 'limit_ad_tracking',                      # (required: No - send if true)
        # 'logon_gamecenter',                       # (required: No - send if true)
        # 'logon_googleplay                         # (required: No - send if true)
        'gender': 'male',                           # (required: No - send if set)
        # 'birth_year                               # (required: No - send if set)
        # 'progression                              # (required: No - send if a progression attempt is in progress)
        'custom_01': 'ninja',                       # (required: No - send if set)
        # 'custom_02                                # (required: No - send if set)
        # 'custom_03                                # (required: No - send if set)
        'client_ts': client_ts,                     # (required: Yes)
        'sdk_version': sdk_version,                 # (required: Yes)
        'os_version': os_version,                   # (required: Yes)
        'manufacturer': manufacturer,                    # (required: Yes)
        'device': device,                      # (required: Yes - if not possible set "unknown")
        'platform': platform,                       # (required: Yes)
        'session_id': state_config['session_id'],   # (required: Yes)
        'build': build_version,                     # (required: No - send if set)
        'session_num': 1,                           # (required: Yes)
        'connection_type': 'wifi',                  # (required: No - send if available)
        # 'jailbroken                               # (required: No - send if true)
        'engine_version': engine_version            # (required: No - send if set by an engine)
    }
    event_dict.update(default_annotations)

def print_verbose(message):
    if verbose_log:
        print(message)

# -- RUN -- #
run()