/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2020 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#include "SimpleSkyOptions"
#include <osgEarth/Sky>
#include <osgEarth/MapNode>
#include <osgEarth/PhongLightingEffect>
#include <osg/MatrixTransform>

namespace osg {
    class Light;
}

namespace osgEarth {
    namespace SimpleSky
    {
        using namespace osgEarth;
        using namespace osgEarth::Util;

        /**
         * Node that roots the silverlining adapter.
         */
        class SimpleSkyNode : public SkyNode
        {
        public:
            SimpleSkyNode(const SimpleSkyOptions& options);

        public: // SkyNode

            osg::Light* getSunLight() const { return _light.get(); }

            void attach(osg::View* view, int lightNum);

            void onSetEphemeris();
            void onSetDateTime();
            void onSetSunVisible();
            void onSetMoonVisible();
            void onSetStarsVisible();
            void onSetAtmosphereVisible();

        public: // osg::Object

            void releaseGLObjects(osg::State* state) const;
            void resizeGLObjectBuffers(unsigned maxSize);

        public: // osg::Node

            void traverse(osg::NodeVisitor&);

            osg::BoundingSphere computeBound() const;

        protected:
            virtual ~SimpleSkyNode() { }

        private:
            /** Sets the sun's position as a latitude and longitude. */
            //void setSunPosition(double lat_degrees, double lon_degrees, osg::View* view);

            struct StarData
            {
                std::string name;
                double right_ascension;
                double declination;
                double magnitude;

                StarData() : right_ascension(0.0), declination(0.0), magnitude(0.0) { }
                StarData(std::stringstream& ss);
            };

            osg::ref_ptr<osg::Light>   _light;
            osg::ref_ptr<osg::Uniform> _lightPosUniform;

            osg::ref_ptr<osg::MatrixTransform> _sunXform;
            osg::ref_ptr<osg::MatrixTransform> _moonXform;
            osg::ref_ptr<osg::MatrixTransform> _starsXform;

            osg::ref_ptr<osg::Group> _cullContainer;

            float _innerRadius, _outerRadius, _sunDistance, _starRadius, _minStarMagnitude;
            osg::ref_ptr<osg::Node> _sun, _stars, _atmosphere, _moon;
            osg::ref_ptr<osg::Uniform> _starAlpha;
            osg::ref_ptr<osg::Uniform> _starPointSize;

            osg::ref_ptr<PhongLightingEffect> _phong;

            Ellipsoid _ellipsoid;

            SimpleSkyOptions _options;

            bool _useBruneton;
            bool _useONeil;
            bool _usePBR;
            bool _usePhong;

            mutable Mutex _eb_mutex;
            mutable osg::ref_ptr<osg::Drawable> _eb_drawable;
            bool _eb_initialized;

            void construct();

            void makeSceneLighting();
            void makeAtmosphere(const Ellipsoid&);
            void makeSun();
            void makeMoon();

            void makeStars();
            osg::Node* buildStarGeometry(const std::vector<StarData>& stars);
            osg::Node* buildStarImageGeometry(const Ellipsoid& em, osg::Image* image);
            void getDefaultStars(std::vector<StarData>& out_stars);
            bool parseStarFile(const std::string& starFile, std::vector<StarData>& out_stars);

            void setSunPosition(const osg::Vec3d& pos);
            void setMoonPosition(const osg::Vec3d& pos);
        };

    }
} // namespace osgEarth::SimpleSky
