import time
import binascii
from net import macho_marshal
from net.macho_marshal import MachoObject, SubStream, UniStr, long_type

HANDSHAKE_CODE_HEX = (
    "630000000000000000010000004000000073040000006900005328000000002800000000"
    "280000000028000000002800000000730b0000003c68616e647368616b653e7408000000"
    "3c6d6f64756c653e010000007300000000"
)
HANDSHAKE_CODE_BYTES = binascii.unhexlify(HANDSHAKE_CODE_HEX)

ITEM_DESCRIPTOR_RAW = binascii.unhexlify(
    "7e000000002214030214626c75652e4442526f7744657363726970746f722514092c11270614"
    "2c114a06032c113f06032c112c06142c1306666c6167494406022c114106032c112306032c11"
    "0a06032c111705810015022c1309737461636b73697a6502266576652e636f6d6d6f6e2e7363"
    "726970742e7379732e6576654366672e537461636b53697a652c114702266576652e636f6d6d"
    "6f6e2e7363726970742e7379732e6576654366672e53696e676c65746f6e2d2d"
)

SLASH_USERERROR_BYTES = binascii.unhexlify(
    "7e020000002214030217657665657863657074696f6e732e557365724572726f722c530a536c6173684572726f72"
    "5601133c436f6d6d616e64733a205b277472272c20276372656174656974656d272c2027737061776e272c202768"
    "65616c272c202767697665736b696c6c275d1306726561736f6e16021b0113036d73671b021304646963742d2d01"
    "00000002000000"
)

LOGIN_AUTH_FAILED_BYTES = binascii.unhexlify(
    "7e010000032214030239636172626f6e2e636f6d6d6f6e2e7363726970742e6e65742e475053"
    "457863657074696f6e732e4750535472616e73706f7274436c6f73656425530f4c6f67696e41"
    "7574684661696c6564160b2e06636c69656e7413066f726967696e1600130a726561736f6e41"
    "7267732f0890fbabcdc265dd011305636c6f636b0813116c6f676765644f6e55736572436f75"
    "6e7413036363701306726567696f6e1b011306726561736f6e0ac3f5285c8f02384013077665"
    "7273696f6e04eeb2370013056275696c6401130a726561736f6e436f646513065632342e3031"
    "1308636f64656e616d6505f00111872d2d01000000"
)

INV_COLS_9_GODMA = ["itemID", "typeID", "ownerID", "locationID", "flagID", "singleton", "groupID", "categoryID", "customInfo"]
INV_COLS_11 = ["itemID", "typeID", "ownerID", "locationID", "flagID", "quantity", "groupID", "categoryID", "customInfo", "stacksize", "singleton"]

def make_dbrow(line9):
    return MachoObject("carbon.common.script.sys.row.Row", {"header": INV_COLS_9_GODMA, "line": list(line9)})

def get_filetime_now():
    return long_type((time.time() + 11644473600.0) * 10000000.0)

def datetime_to_filetime(dt):
    if dt is None:
        return get_filetime_now()
    return long_type((dt.timestamp() + 11644473600.0) * 10000000.0)

def placebo_crypto_hash(*args):
    raw = macho_marshal.dumps(args)
    return str(binascii.crc_hqx(raw, 0))

def extract_service_name(addr_obj):
    st = addr_obj.state
    return st[1] if st[0] == 8 else st[2]

def build_call_rsp(req_obj, return_value, client_id=10001, oob_dict=None):
    req_state  = req_obj.state
    req_source = req_state[1]
    req_dest   = req_state[2]
    user_id    = req_state[3]
    svc_name   = extract_service_name(req_dest)

    if isinstance(svc_name, str) and svc_name.startswith("<StrTableRef:"):
        svc_name = None

    if req_dest.state[0] == 8:
        rsp_source = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (8, svc_name, None))
    else:
        rsp_source = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (1, 1, svc_name, None))

    rsp_dest = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (2, client_id, req_source.state[2], None))
    rsp_state = (
        7, rsp_source, rsp_dest, user_id,
        (SubStream(return_value),),
        None, oob_dict, req_state[7],
        None, None, None, None, None, None
    )
    return MachoObject("carbon.common.script.net.machoNetPacket.CallRsp", rsp_state)

def build_error_rsp(req_obj, err_bytes, client_id=10001):
    req_state  = req_obj.state
    req_source = req_state[1]
    req_dest   = req_state[2]
    user_id    = req_state[3]
    svc_name   = extract_service_name(req_dest)
    if isinstance(svc_name, str) and svc_name.startswith("<StrTableRef:"):
        svc_name = None
    rsp_source = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (1, 1, svc_name, None))
    rsp_dest   = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (2, client_id, req_source.state[2], None))
    rsp_state  = (
        15, rsp_source, rsp_dest, user_id,
        (6, 2, (err_bytes,)),
        None, None, req_state[7],
        None, None, None, None, None, None
    )
    return MachoObject("carbon.common.script.net.machoNetPacket.ErrorResponse", rsp_state)

def build_ping_rsp(ping_req_obj, client_id=10001):
    req_state  = ping_req_obj.state
    req_source = req_state[1]
    user_id    = req_state[3]
    times_list = req_state[4][0] if (isinstance(req_state[4], tuple) and len(req_state[4]) > 0 and isinstance(req_state[4][0], list)) else []
    now_ft     = get_filetime_now()
    times_list.extend([
        (now_ft, now_ft, "proxy::handle_message"),
        (now_ft, now_ft, "proxy::writing"),
        (now_ft, now_ft, UniStr("server::handle_message")),
        (now_ft, now_ft, UniStr("server::turnaround")),
        (now_ft, now_ft, "proxy::handle_message"),
        (now_ft, now_ft, "proxy::writing"),
    ])
    rsp_source = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (8, None, None))
    rsp_dest   = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (2, client_id, req_source.state[2], None))
    rsp_state  = (
        21, rsp_source, rsp_dest, user_id,
        (times_list,),
        None, None, req_state[7],
        None, None, None, None, None, None
    )
    return MachoObject("carbon.common.script.net.machoNetPacket.PingRsp", rsp_state)

def build_session_change_notification(user_id, client_id, change_dict, nodes_of_interest=None):
    src_addr = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (1, 1, None, None))
    dst_addr = MachoObject("carbon.common.script.net.machoNetAddress.MachoAddress", (2, client_id, None, None))
    nodes = nodes_of_interest if nodes_of_interest is not None else [-1]
    pkt_state = (
        16, src_addr, dst_addr, int(user_id),
        (int(user_id), (0, change_dict), nodes),
        None, None, None,
        None, None, None, None, None, None
    )
    return MachoObject("carbon.common.script.net.machoNetPacket.SessionChangeNotification", pkt_state)