# Fairyfly: A Plugin for Environmental Analysis (GPL)
# This file is part of Fairyfly.
#
# Copyright (c) 2025, Ladybug Tools.
# You should have received a copy of the GNU Affero General Public License
# along with Fairyfly; If not, see <http://www.gnu.org/licenses/>.
# 
# @license AGPL-3.0-or-later <https://spdx.org/licenses/AGPL-3.0-or-later>


"""
Dump any fairyfly object to a JSON file. You can use "FF Load Objects" component
to load the objects from the file back into Grasshopper.
-
Fairyfly objects include any fairyfly Model, Material, or Boundary.
-

    Args:
        _ff_objs: A list of Fairyfly objects to be written to a file.
        _name_: A name for the file to which the fairyfly objects will be
            written. (Default: 'unnamed').
        _folder_: An optional directory into which the fairyfly objects will be
            written.  The default is set to the default simulation folder.
        indent_: An optional positive integer to set the indentation used in the
            resulting JSON file.
        abridged_: Set to "True" to serialize the object in its abridged form.
            Abridged objects cannot be reserialized back to fairyfly objects
            on their own but they are used throughout fairyfly to minimize
            file size and unnecessary duplication.
        _dump: Set to "True" to save the fairyfly objects to file.

    Returns:
        report: Errors, warnings, etc.
        ff_file: The location of the file where the fairyfly JSON is saved.
"""

ghenv.Component.Name = 'FF Dump Objects'
ghenv.Component.NickName = 'DumpObjects'
ghenv.Component.Message = '1.9.0'
ghenv.Component.Category = 'Fairyfly'
ghenv.Component.SubCategory = '0 :: Create'
ghenv.Component.AdditionalHelpFromDocStrings = '4'

import sys
import os
import json

try:  # import the core fairyfly dependencies
    from fairyfly.model import Model
    from fairyfly.shape import Shape
    from fairyfly.boundary import Boundary
    from fairyfly.config import folders
except ImportError as e:
    raise ImportError('\nFailed to import fairyfly:\n\t{}'.format(e))

try:  # import the core ladybug_rhino dependencies
    from ladybug_rhino.grasshopper import all_required_inputs, give_warning
except ImportError as e:
    raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e))


def geo_object_warning(obj):
    """Give a warning that individual geometry objects should be added to a Model."""
    msg = 'An individual {} has been connected to the _ff_objs.\n' \
        'The recommended practice is to add this object to a Model and\n' \
        'serialize the Model instead of serializing individual objects.'.format(
            obj.__class__.__name__)
    print(msg)
    give_warning(ghenv.Component, msg)


if all_required_inputs(ghenv.Component) and _dump:
    # set the component defaults
    name = _name_ if _name_ is not None else 'unnamed'
    file_name = '{}.json'.format(name) if len(_ff_objs) > 1 or not \
        isinstance(_ff_objs[0], Model) else '{}.ffjson'.format(name)
    folder = _folder_ if _folder_ is not None else folders.default_simulation_folder
    ff_file = os.path.join(folder, file_name)
    abridged = bool(abridged_)

    # check to see if any objects are of the geometry type and give a warning
    geo_types = (Shape, Boundary)
    for obj in _ff_objs:
        if isinstance(obj, geo_types):
            geo_object_warning(obj)

    # create the dictionary to be written to a JSON file
    if len(_ff_objs) == 1:  # write a single object into a file if the length is 1
        try:
            obj_dict = _ff_objs[0].to_dict(abridged=abridged)
        except TypeError:  # no abridged option
            obj_dict = _ff_objs[0].to_dict()
    else:  # create a dictionary of the objects that are indexed by name
        obj_dict = {}
        for obj in _ff_objs:
            try:
                obj_dict[obj.identifier] = obj.to_dict(abridged=abridged)
            except TypeError:  # no abridged option
                obj_dict[obj.identifier] = obj.to_dict()

    # write the dictionary into a file
    if not os.path.isdir(folder):
        os.makedirs(folder)
    if (sys.version_info < (3, 0)):  # we need to manually encode it as UTF-8
        with open(ff_file, 'wb') as fp:
            obj_str = json.dumps(obj_dict, indent=indent_, ensure_ascii=False)
            fp.write(obj_str.encode('utf-8'))
    else:
        with open(ff_file, 'w', encoding='utf-8') as fp:
            obj_str = json.dump(obj_dict, fp, indent=indent_, ensure_ascii=False)
