##
# Copyright (c) 2012-2017 Apple Inc. All rights reserved.
#
# 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.
##

# Adjust method names

# PROPFINDs
METHOD_PROPFIND_CALENDAR_HOME = "PROPFIND Calendar Home"
METHOD_PROPFIND_CACHED_CALENDAR_HOME = "PROPFIND cached Calendar Home"
METHOD_PROPFIND_CALENDAR = "PROPFIND Calendar"
METHOD_PROPFIND_INBOX = "PROPFIND Inbox"
METHOD_PROPFIND_ADDRESSBOOK_HOME = "PROPFIND Adbk Home"
METHOD_PROPFIND_CACHED_ADDRESSBOOK_HOME = "PROPFIND cached Adbk Home"
METHOD_PROPFIND_ADDRESSBOOK = "PROPFIND Adbk"
METHOD_PROPFIND_DIRECTORY = "PROPFIND Directory"
METHOD_PROPFIND_PRINCIPALS = "PROPFIND Principals"
METHOD_PROPFIND_CACHED_PRINCIPALS = "PROPFIND cached Principals"

# PROPPATCHs
METHOD_PROPPATCH_CALENDAR = "PROPPATCH Calendar"
METHOD_PROPPATCH_ADDRESSBOOK = "PROPPATCH Adbk Home"

# REPORTs
METHOD_REPORT_CALENDAR_MULTIGET = "REPORT cal-multi"
METHOD_REPORT_CALENDAR_QUERY = "REPORT cal-query"
METHOD_REPORT_CALENDAR_FREEBUSY = "REPORT freebusy"
METHOD_REPORT_CALENDAR_HOME_SYNC = "REPORT cal-home-sync"
METHOD_REPORT_CALENDAR_SYNC = "REPORT cal-sync"
METHOD_REPORT_ADDRESSBOOK_MULTIGET = "REPORT adbk-multi"
METHOD_REPORT_ADDRESSBOOK_QUERY = "REPORT adbk-query"
METHOD_REPORT_DIRECTORY_QUERY = "REPORT dir-query"
METHOD_REPORT_ADDRESSBOOK_HOME_SYNC = "REPORT adbk-home-sync"
METHOD_REPORT_ADDRESSBOOK_SYNC = "REPORT adbk-sync"
METHOD_REPORT_P_SEARCH_P_SET = "REPORT p-set"
METHOD_REPORT_P_P_SEARCH = "REPORT p-search"
METHOD_REPORT_EXPAND_P = "REPORT expand"

# POSTs
METHOD_POST_CALENDAR_HOME = "POST Calendar Home"
METHOD_POST_CALENDAR = "POST Calendar"
METHOD_POST_CALENDAR_ADD_MEMBER = "POST Calendar-add"
METHOD_POST_CALENDAR_OBJECT = "POST ics"
METHOD_POST_CALENDAR_OBJECT_SPLIT = "POST split"
METHOD_POST_CALENDAR_OBJECT_ATTACHMENT_ADD = "POST att-add"
METHOD_POST_CALENDAR_OBJECT_ATTACHMENT_UPDATE = "POST att-update"
METHOD_POST_CALENDAR_OBJECT_ATTACHMENT_REMOVE = "POST att-remove"
METHOD_POST_ADDRESSBOOK_HOME = "POST Adbk Home"
METHOD_POST_ADDRESSBOOK = "POST Adbk"
METHOD_POST_ADDRESSBOOK_ADD_MEMBER = "POST Adbk-add"
METHOD_POST_ISCHEDULE_FREEBUSY = "POST Freebusy iSchedule"
METHOD_POST_ISCHEDULE = "POST iSchedule"
METHOD_POST_TIMEZONES = "POST Timezones"
METHOD_POST_FREEBUSY = "POST Freebusy"
METHOD_POST_ORGANIZER = "POST Organizer"
METHOD_POST_ATTENDEE = "POST Attendee"
METHOD_POST_OUTBOX = "POST Outbox"
METHOD_POST_APNS = "POST apns"
METHOD_POST_CONDUIT = "POST conduit"

# PUTs
METHOD_PUT_ICS = "PUT ics"
METHOD_PUT_ORGANIZER = "PUT Organizer"
METHOD_PUT_ATTENDEE = "PUT Attendee"
METHOD_PUT_DROPBOX = "PUT dropbox"
METHOD_PUT_VCF = "PUT VCF"

# GETs
METHOD_GET_CALENDAR_HOME = "GET Calendar Home"
METHOD_GET_CALENDAR = "GET Calendar"
METHOD_GET_ICS = "GET ics"
METHOD_GET_INBOX_ICS = "GET inbox ics"
METHOD_GET_DROPBOX = "GET dropbox"
METHOD_GET_ADDRESSBOOK_HOME = "GET Adbk Home"
METHOD_GET_ADDRESSBOOK = "GET Adbk"
METHOD_GET_VCF = "GET VCF"
METHOD_GET_TIMEZONES = "GET Timezones"

# DELETEs
METHOD_DELETE_CALENDAR_HOME = "DELETE Calendar Home"
METHOD_DELETE_CALENDAR = "DELETE Calendar"
METHOD_DELETE_ICS = "DELETE ics"
METHOD_DELETE_INBOX_ICS = "DELETE inbox ics"
METHOD_DELETE_DROPBOX = "DELETE dropbox"
METHOD_DELETE_ADDRESSBOOK_HOME = "DELETE Adbk Home"
METHOD_DELETE_ADDRESSBOOK = "DELETE Adbk"
METHOD_DELETE_VCF = "DELETE vcf"


def getAdjustedMethodName(stats, method=None, uri=None):

    if method is None:
        method = stats["method"]
    if uri is None:
        uri = stats["uri"]
    uribits = uri.rstrip("/").split('/')[1:]
    if len(uribits) == 0:
        uribits = [uri]

    calendar_specials = ("attachments", "dropbox", "notification", "freebusy", "outbox",)
    adbk_specials = ("notification",)

    def _PROPFIND():
        cached = "cached" in stats

        if uribits[0] == "calendars":

            if len(uribits) == 3:
                return METHOD_PROPFIND_CACHED_CALENDAR_HOME if cached else METHOD_PROPFIND_CALENDAR_HOME
            elif len(uribits) > 3:
                if uribits[3] in calendar_specials:
                    return "PROPFIND %s" % (uribits[3],)
                elif len(uribits) == 4:
                    if uribits[3] == "inbox":
                        return METHOD_PROPFIND_INBOX
                    else:
                        return METHOD_PROPFIND_CALENDAR

        elif uribits[0] == "addressbooks":

            if len(uribits) == 3:
                return METHOD_PROPFIND_CACHED_ADDRESSBOOK_HOME if cached else METHOD_PROPFIND_ADDRESSBOOK_HOME
            elif len(uribits) > 3:
                if uribits[3] in adbk_specials:
                    return "PROPFIND %s" % (uribits[3],)
                elif len(uribits) == 4:
                    return METHOD_PROPFIND_ADDRESSBOOK

        elif uribits[0] == "directory":
            return METHOD_PROPFIND_DIRECTORY

        elif uribits[0] == "principals":
            return METHOD_PROPFIND_CACHED_PRINCIPALS if cached else METHOD_PROPFIND_PRINCIPALS

        return method

    def _REPORT():

        if "(" in method:
            report_type = method.split("}" if "}" in method else ":")[1][:-1]
            if report_type == "addressbook-query":
                if uribits[0] == "directory":
                    report_type = "directory-query"
            if report_type == "sync-collection":
                if uribits[0] == "calendars":
                    if len(uribits) == 3:
                        report_type = "cal-home-sync"
                    else:
                        report_type = "cal-sync"
                elif uribits[0] == "addressbooks":
                    if len(uribits) == 3:
                        report_type = "adbk-home-sync"
                    else:
                        report_type = "adbk-sync"
            mappedNames = {
                "calendar-multiget": METHOD_REPORT_CALENDAR_MULTIGET,
                "calendar-query": METHOD_REPORT_CALENDAR_QUERY,
                "free-busy-query": METHOD_REPORT_CALENDAR_FREEBUSY,
                "cal-home-sync": METHOD_REPORT_CALENDAR_HOME_SYNC,
                "cal-sync": METHOD_REPORT_CALENDAR_SYNC,
                "addressbook-multiget": METHOD_REPORT_ADDRESSBOOK_MULTIGET,
                "addressbook-query": METHOD_REPORT_ADDRESSBOOK_QUERY,
                "directory-query": METHOD_REPORT_DIRECTORY_QUERY,
                "adbk-home-sync": METHOD_REPORT_ADDRESSBOOK_HOME_SYNC,
                "adbk-sync": METHOD_REPORT_ADDRESSBOOK_SYNC,
                "principal-search-property-set": METHOD_REPORT_P_SEARCH_P_SET,
                "principal-property-search": METHOD_REPORT_P_P_SEARCH,
                "expand-property": METHOD_REPORT_EXPAND_P,
            }
            return mappedNames.get(report_type, "REPORT %s" % (report_type,))

        return method

    def _PROPPATCH():

        if uribits[0] == "calendars":
            return METHOD_PROPPATCH_CALENDAR
        elif uribits[0] == "addressbooks":
            return METHOD_PROPPATCH_ADDRESSBOOK

        return method

    def _POST():

        if uribits[0] == "calendars":

            if "(" in method:
                post_type = method.split("(")[1][:-1]
                mappedNames = {
                    "add-member": METHOD_POST_CALENDAR_ADD_MEMBER,
                    "split": METHOD_POST_CALENDAR_OBJECT_SPLIT,
                    "attachment-add": METHOD_POST_CALENDAR_OBJECT_ATTACHMENT_ADD,
                    "attachment-update": METHOD_POST_CALENDAR_OBJECT_ATTACHMENT_UPDATE,
                    "attachment-remove": METHOD_POST_CALENDAR_OBJECT_ATTACHMENT_REMOVE,
                }
                return mappedNames.get(post_type, "POST %s" % (post_type,))

            elif len(uribits) == 3:
                return METHOD_POST_CALENDAR_HOME
            elif len(uribits) == 4:
                if uribits[3] == "outbox":
                    if "recipients" in stats:
                        return METHOD_POST_FREEBUSY
                    elif "freebusy" in stats:
                        return METHOD_POST_FREEBUSY
                    elif "itip.request" in stats or "itip.cancel" in stats:
                        return METHOD_POST_ORGANIZER
                    elif "itip.reply" in stats:
                        return METHOD_POST_ATTENDEE
                    else:
                        return METHOD_POST_OUTBOX
                elif uribits[3] in calendar_specials:
                    pass
                else:
                    return METHOD_POST_CALENDAR
            elif len(uribits) == 5:
                return METHOD_POST_CALENDAR_OBJECT

        elif uribits[0] == "addressbooks":

            if len(uribits) == 3:
                return METHOD_POST_ADDRESSBOOK_HOME
            elif len(uribits) == 4:
                if uribits[3] in adbk_specials:
                    pass
                else:
                    return METHOD_POST_ADDRESSBOOK

        elif uribits[0] == "ischedule":
            if "fb-cached" in stats or "fb-uncached" in stats or "freebusy" in stats:
                return METHOD_POST_ISCHEDULE_FREEBUSY
            else:
                return METHOD_POST_ISCHEDULE

        elif uribits[0].startswith("timezones"):
            return METHOD_POST_TIMEZONES

        elif uribits[0].startswith("apns"):
            return METHOD_POST_APNS

        elif uribits[0].startswith("conduit"):
            return METHOD_POST_CONDUIT

        return method

    def _PUT():

        if uribits[0] == "calendars":
            if len(uribits) > 3:
                if uribits[3] in calendar_specials:
                    return "PUT %s" % (uribits[3],)
                elif len(uribits) == 4:
                    pass
                else:
                    if "itip.requests" in stats:
                        return METHOD_PUT_ORGANIZER
                    elif "itip.reply" in stats:
                        return METHOD_PUT_ATTENDEE
                    else:
                        return METHOD_PUT_ICS

        elif uribits[0] == "addressbooks":
            if len(uribits) > 3:
                if uribits[3] in adbk_specials:
                    return "PUT %s" % (uribits[3],)
                elif len(uribits) == 4:
                    pass
                else:
                    return METHOD_PUT_VCF

        return method

    def _GET():

        if uribits[0] == "calendars":

            if len(uribits) == 3:
                return METHOD_GET_CALENDAR_HOME
            elif len(uribits) > 3:
                if uribits[3] in calendar_specials:
                    return "GET %s" % (uribits[3],)
                elif len(uribits) == 4:
                    return METHOD_GET_CALENDAR
                elif uribits[3] == "inbox":
                    return METHOD_GET_INBOX_ICS
                else:
                    return METHOD_GET_ICS

        elif uribits[0] == "addressbooks":

            if len(uribits) == 3:
                return METHOD_GET_ADDRESSBOOK_HOME
            elif len(uribits) > 3:
                if uribits[3] in adbk_specials:
                    return "GET %s" % (uribits[3],)
                elif len(uribits) == 4:
                    return METHOD_GET_ADDRESSBOOK
                else:
                    return METHOD_GET_VCF

        elif uribits[0].startswith("timezones"):
            return METHOD_GET_TIMEZONES

        return method

    def _DELETE():

        if uribits[0] == "calendars":

            if len(uribits) == 3:
                return METHOD_DELETE_CALENDAR_HOME
            elif len(uribits) > 3:
                if uribits[3] in calendar_specials:
                    return "DELETE %s" % (uribits[3],)
                elif len(uribits) == 4:
                    return METHOD_DELETE_CALENDAR
                elif uribits[3] == "inbox":
                    return METHOD_DELETE_INBOX_ICS
                else:
                    return METHOD_DELETE_ICS

        elif uribits[0] == "addressbooks":

            if len(uribits) == 3:
                return METHOD_DELETE_ADDRESSBOOK_HOME
            elif len(uribits) > 3:
                if uribits[3] in adbk_specials:
                    return "DELETE %s" % (uribits[3],)
                elif len(uribits) == 4:
                    return METHOD_DELETE_ADDRESSBOOK
                else:
                    return METHOD_DELETE_VCF

        return method

    def _ANY():
        return method

    return {
        "DELETE": _DELETE,
        "GET": _GET,
        "POST": _POST,
        "PROPFIND": _PROPFIND,
        "PROPPATCH": _PROPPATCH,
        "PUT": _PUT,
        "REPORT": _REPORT,
    }.get(method.split("(")[0], _ANY)()


osClients = (
    "Mac OS X/",
    "Mac+OS+X/",
    "Mac_OS_X/",
    "iOS/",
)

versionClients = (
    "iCal/",
    "iPhone/",
    "CalendarAgent/",
    "Calendar/",
    "CoreDAV/",
    "Safari/",
    "dataaccessd/",
    "Preferences/",
    "curl/",
    "DAVKit/",
)

quickclients = (
    ("InterMapper/", "InterMapper"),
    ("CardDAVPlugin/", "CardDAVPlugin"),
    ("Address%20Book/", "AddressBook"),
    ("AddressBook/", "AddressBook"),
    ("Mail/", "Mail"),
    ("iChat/", "iChat"),
)


def getAdjustedClientName(stats):

    userAgent = stats["userAgent"]
    os = ""
    for client in osClients:
        index = userAgent.find(client)
        if index != -1:
            l = len(client)
            endex = userAgent.find(' ', index + l)
            os = (userAgent[index:] if endex == -1 else userAgent[index:endex]) + " "

    for client in versionClients:
        index = userAgent.find(client)
        if index != -1:
            if os:
                return os + client[:-1]
            else:
                l = len(client)
                endex = userAgent.find(' ', index + l)
                return os + (userAgent[index:] if endex == -1 else userAgent[index:endex])

    for quick, result in quickclients:
        index = userAgent.find(quick)
        if index != -1:
            return os + result

    return userAgent[:20]
