// solarsys.h
//
// Copyright (C) 2001 Chris Laurel <claurel@shatters.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

#pragma once

#include <cstdint>
#include <filesystem>
#include <iosfwd>
#include <memory>
#include <unordered_map>

#include <celutil/associativearray.h>
#include "astroobj.h"
#include "parseobject.h"

class FrameCache;
class FrameTree;
class Location;
class PlanetarySystem;
class Selection;
class Star;
class Timeline;
class TimelinePhase;
class Universe;

namespace celestia::engine
{
class GeometryPaths;
class TexturePaths;
class UrlManager;
}

class SolarSystem
{
public:
    SolarSystem(Star*);
    ~SolarSystem();

    Star* getStar() const;
    PlanetarySystem* getPlanets() const;
    FrameTree* getFrameTree() const;

private:
    Star* star;
    std::unique_ptr<PlanetarySystem> planets;
    std::unique_ptr<FrameTree> frameTree;
};

using SolarSystemCatalog = std::unordered_map<AstroCatalog::IndexNumber, std::unique_ptr<SolarSystem>>;

class SolarSystemsBuilder
{
public:
    SolarSystemsBuilder(Universe&,
                        celestia::engine::GeometryPaths&,
                        celestia::engine::TexturePaths&,
                        celestia::engine::UrlManager&);

    bool parseSsc(std::istream& in, const std::filesystem::path& resDir);

private:
    enum class BodyType
    {
        ReferencePoint,
        NormalBody,
        SurfaceObject,
        UnknownBodyType,
    };

    Body* createBody(const std::string&,
                     PlanetarySystem*,
                     Body*,
                     const celestia::util::AssociativeArray&,
                     const std::filesystem::path&,
                     DataDisposition,
                     BodyType);

    Body* createReferencePoint(const std::string& name,
                               PlanetarySystem* system,
                               Body* existingBody,
                               const celestia::util::AssociativeArray& refPointData,
                               const std::filesystem::path& path,
                               DataDisposition disposition);

    std::unique_ptr<Location> createLocation(const celestia::util::AssociativeArray& locationData,
                                             const Body* body) const;

    bool createTimeline(Body* body,
                        PlanetarySystem* system,
                        const celestia::util::AssociativeArray& planetData,
                        const std::filesystem::path& path,
                        DataDisposition disposition,
                        BodyType bodyType);

    std::unique_ptr<Timeline> createTimelineFromArray(Body* body,
                                                      Selection parent,
                                                      const celestia::util::ValueArray& timelineArray,
                                                      const std::filesystem::path& path,
                                                      FrameId defaultOrbitFrame,
                                                      FrameId defaultBodyFrame);

    bool createLegacyTimeline(Body* body,
                              Selection parentObject,
                              const celestia::util::AssociativeArray& planetData,
                              const std::filesystem::path& path,
                              DataDisposition disposition,
                              FrameId defaultOrbitFrame,
                              FrameId defaultBodyFrame);

    std::unique_ptr<TimelinePhase> createTimelinePhase(Body* body,
                                                       Selection parent,
                                                       const celestia::util::AssociativeArray& phaseData,
                                                       const std::filesystem::path& path,
                                                       FrameId defaultOrbitFrame,
                                                       FrameId defaultBodyFrame,
                                                       bool isFirstPhase,
                                                       bool isLastPhase,
                                                       double previousPhaseEnd);

    SolarSystem* getOrCreateSolarSystem(Star*);
    FrameTree* getFrameTree(const Selection&);

    Universe* m_universe;
    celestia::engine::GeometryPaths* m_geometryPaths;
    celestia::engine::TexturePaths* m_texturePaths;
    celestia::engine::UrlManager* m_urlManager;
    FrameCache m_frameCache;
};
