# • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        
# ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     
# ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ 
# ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌
# ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪
#               Magicbane Emulator Project © 2013 - 2022
#                          www.magicbane.com

import glob
import json
import os
import sys

from arcane.enums.arc_object import *
from arcane.objects import *
from arcane.util import *
from arcane.zones import *

DUMP_DIRECTORY = 'ARCANE_DUMP'
WORKING_DIRECTORY = os.path.dirname(__file__)
TARGET_DIRECTORY = os.path.join(WORKING_DIRECTORY, DUMP_DIRECTORY)
COBJECTS_DIRECTORY = os.path.join(TARGET_DIRECTORY, 'COBJECTS')
CZONE_DIRECTORY = os.path.join(TARGET_DIRECTORY, 'CZONE')
COBJECTS_MAGIC = 0x434c4e54

COBJECTS_MAP = {
    OBJECT_TYPE_LIGHT: ArcObj,
    OBJECT_TYPE_DOOR: ArcDoorObject,
    OBJECT_TYPE_STATIC: ArcStaticObject,
    OBJECT_TYPE_STRUCTURE: ArcStructureObject,
    OBJECT_TYPE_ASSETSTRUCTURE: ArcAssetStructureObject,
    OBJECT_TYPE_DUNGEONUNIT: ArcDungeonUnitObject,
    OBJECT_TYPE_DUNGEONEXIT: ArcDungeonExitObject,
    OBJECT_TYPE_DUNGEONSTAIR: ArcDungeonStairObject,
    OBJECT_TYPE_ITEM: ArcItem,
    OBJECT_TYPE_PLAYER: ArcCharacter,
    OBJECT_TYPE_MOBILE: ArcCharacter,
    OBJECT_TYPE_RUNE: ArcRune,
    OBJECT_TYPE_CONTAINER: ArcContainerObject,
    OBJECT_TYPE_DEED: ArcDeed,
    OBJECT_TYPE_KEY: ArcKey,
    OBJECT_TYPE_ASSET: ArcCityAssetTemplate,
    OBJECT_TYPE_OBJECT: ArcObj,
}


def init_cobjects():
    for type in COBJECTS_MAP:
        directory_path = os.path.join(COBJECTS_DIRECTORY, OBJECT_TYPE_TO_STRING[type])
        os.makedirs(directory_path, exist_ok=True)


def init_czones():
    directory_path = CZONE_DIRECTORY
    os.makedirs(directory_path, exist_ok=True)


def unpack_cobjects():
    init_cobjects()

    resources = load_cache_file('CObjects.cache')

    for res_id, data in resources:
        in_stream = ResStream(data)
        magic = in_stream.read_dword()
        obj_type = in_stream.read_dword()

        arc_object = COBJECTS_MAP[obj_type]()
        filepath = os.path.join(
            COBJECTS_DIRECTORY,
            OBJECT_TYPE_TO_STRING[obj_type],
            f'{res_id:020d}.json'
        )
        arc_object.load_binary(in_stream)
        parsed = arc_object.save_json()
        with open(filepath, 'w') as fp:
            json.dump(parsed, fp, indent=2)


def pack_cobjects():
    init_cobjects()

    resources = []
    for obj_type in COBJECTS_MAP:
        directory = os.path.join(
            COBJECTS_DIRECTORY,
            OBJECT_TYPE_TO_STRING[obj_type]
        )
        for filepath in glob.glob(os.path.join(directory, '*.json')):
            filename = os.path.basename(filepath)
            res_id = int(filename.split('.json')[0])
            json_data = json.load(open(filepath))

            out_stream = ResStream()
            out_stream.write_dword(COBJECTS_MAGIC)
            out_stream.write_dword(obj_type)

            arc_object = COBJECTS_MAP[obj_type]()
            try:
                arc_object.load_json(json_data)
                arc_object.save_binary(out_stream)
            except:
                print(res_id)
                raise

            resources.append([res_id, out_stream.get_bytes()])

    save_cache_file('CObjects.cache.new', resources)


def test_cobjects():
    resources = load_cache_file('CObjects.cache')

    for res_id, data in resources:
        in_stream = ResStream(data)
        out_stream = ResStream()

        magic = in_stream.read_dword()
        o_type = in_stream.read_dword()
        out_stream.write_dword(magic)
        out_stream.write_dword(o_type)

        arc_in = COBJECTS_MAP[o_type]()
        arc_out = COBJECTS_MAP[o_type]()

        arc_in.load_binary(in_stream)
        parsed = arc_in.save_json()
        arc_out.load_json(parsed)
        arc_out.save_binary(out_stream)
        try:
            assert in_stream.get_bytes() == out_stream.get_bytes()
        except:
            print(res_id, in_stream.buffer.tell(), out_stream.buffer.tell())
            print()


def unpack_czones():
    init_czones()

    resources = load_cache_file('CZone.cache')

    for res_id, data in resources:
        arc_zone = ArcZone()
        in_stream = ResStream(data)
        filepath = os.path.join(
            CZONE_DIRECTORY,
            f'{res_id:020d}.json'
        )
        arc_zone.load_binary(in_stream)
        parsed = arc_zone.save_json()
        with open(filepath, 'w') as fp:
            json.dump(parsed, fp, indent=2)


def pack_czones():
    init_czones()

    resources = []
    directory = CZONE_DIRECTORY
    for filepath in glob.glob(os.path.join(directory, '*.json')):
        filename = os.path.basename(filepath)
        res_id = int(filename.split('.json')[0])
        json_data = json.load(open(filepath))

        out_stream = ResStream()
        arc_object = ArcZone()
        try:
            arc_object.load_json(json_data)
            arc_object.save_binary(out_stream)
        except:
            print(res_id)
            raise

        resources.append([res_id, out_stream.get_bytes()])

    save_cache_file('CZone.cache.new', resources)


def test_czones():
    resources = load_cache_file('CZone.cache')
    for res_id, data in resources:
        in_stream = ResStream(data)
        out_stream = ResStream()

        arc_in = ArcZone()
        arc_out = ArcZone()

        arc_in.load_binary(in_stream)
        parsed = arc_in.save_json()
        arc_out.load_json(parsed)
        arc_out.save_binary(out_stream)
        try:
            assert in_stream.get_bytes() == out_stream.get_bytes()
        except:
            print(res_id, in_stream.buffer.tell(), out_stream.buffer.tell())
            print()


def usage():
    print('mbEditorPro commands:')
    print('usage: {} unpack cobjects'.format(sys.argv[0]))
    print('usage: {} pack cobjects'.format(sys.argv[0]))
    print('usage: {} unpack czones'.format(sys.argv[0]))
    print('usage: {} pack czones'.format(sys.argv[0]))
    exit(1)


def main():
    if not sys.argv[2:]:
        usage()

    if sys.argv[1] not in ['pack', 'unpack']:
        usage()

    if sys.argv[2] not in ['cobjects', 'czones']:
        usage()

    method = '_'.join(sys.argv[1:3]) + '()'
    print(method)
    exec(method)


main()
