// Copyright (c) 2008-2012 Sundog Software, LLC. All rights reserved worldwide.

#include "SkyDrawable.h"
#include "SilverLining.h"
#include "AtmosphereReference.h"

#include <assert.h>

#ifdef ANDROID
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "silverlining", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "silverlining", __VA_ARGS__))
#define printf LOGW
#endif

using namespace SilverLining;

SkyDrawable::SkyDrawable()
        : osg::Drawable()
		, _view(0)
		, _skyboxSize(0)
{
    setDataVariance(osg::Object::DYNAMIC);
    setUseVertexBufferObjects(false);
    setUseDisplayList(false);
}

SkyDrawable::SkyDrawable(osgViewer::Viewer* view)
        : osg::Drawable()
        , _view(view)
		, _skyboxSize(0)
{
    setDataVariance(osg::Object::DYNAMIC);
    setUseVertexBufferObjects(false);
    setUseDisplayList(false);
}

void SkyDrawable::setLighting(SilverLining::Atmosphere *atmosphere) const
{
    osg::Light *light = _view->getLight();
    osg::Vec4 ambient, diffuse;
    osg::Vec3 direction;

    if (atmosphere && light)
    {
        float ra, ga, ba, rd, gd, bd, x, y, z;
        atmosphere->GetAmbientColor(&ra, &ga, &ba);
        atmosphere->GetSunOrMoonColor(&rd, &gd, &bd);
        atmosphere->GetSunOrMoonPosition(&x, &y, &z);

        direction = osg::Vec3(x, y, z);
        ambient = osg::Vec4(ra, ga, ba, 1.0);
        diffuse = osg::Vec4(rd, gd, bd, 1.0);

        direction.normalize();

        light->setAmbient(ambient);
        light->setDiffuse(diffuse);
        light->setSpecular(osg::Vec4(0,0,0,1));
        light->setPosition(osg::Vec4(direction.x(), direction.y(), direction.z(), 0));
    }
}

void SkyDrawable::initializeSilverLining(AtmosphereReference *ar) const
{
    if (ar && !ar->atmosphereInitialized)
    {
        ar->atmosphereInitialized = true; // only try once.
		SilverLining::Atmosphere *atmosphere = ar->atmosphere;

        if (atmosphere)
        {
            srand(1234); // constant random seed to ensure consistent clouds across windows

#ifdef ANDROID
            std::string resPath("Resources/");
            int ret = atmosphere->Initialize(SilverLining::Atmosphere::OPENGLES2, resPath.c_str(),
                                             true, 0);

#else
            // Update the path below to where you installed SilverLining's resources folder.
            const char *slPath = getenv("SILVERLINING_PATH");
            if (!slPath)
            {
                printf("Can't find SilverLining; set the SILVERLINING_PATH environment variable ");
                printf("to point to the directory containing the SDK.\n");
                exit(0);
            }

            std::string resPath(slPath);
            resPath += "\\Resources\\";

            int ret = atmosphere->Initialize(SilverLining::Atmosphere::OPENGL, resPath.c_str(),
                                             true, 0);
#endif
            if (ret != SilverLining::Atmosphere::E_NOERROR)
            {
                printf("SilverLining failed to initialize; error code %d.\n", ret);
                printf("Check that the path to the SilverLining installation directory is set properly ");
                printf("in SkyDrawable.cpp (in SkyDrawable::initializeSilverLining)\n");
                exit(0);
            }

            // Let SilverLining know which way is up. OSG usually has Z going up.
            atmosphere->SetUpVector(0, 0, 1);
            atmosphere->SetRightVector(1, 0, 0);

            // Set our location (change this to your own latitude and longitude)
            SilverLining::Location loc;
            loc.SetAltitude(0);
            loc.SetLatitude(45);
            loc.SetLongitude(-122);
            atmosphere->GetConditions()->SetLocation(loc);

            // Set the time to noon in PST
            SilverLining::LocalTime t;
            t.SetFromSystemTime();
			t.SetHour(12);
			t.SetTimeZone(PST);
            atmosphere->GetConditions()->SetTime(t);

            // Center the clouds around the camera's initial position
            osg::Vec3d pos = _view->getCameraManipulator()->getMatrix().getTrans();

            SilverLining::CloudLayer *cumulusCongestusLayer;
            cumulusCongestusLayer = SilverLining::CloudLayerFactory::Create(CUMULUS_CONGESTUS);
            cumulusCongestusLayer->SetIsInfinite(true);
            cumulusCongestusLayer->SetBaseAltitude(4000);
            cumulusCongestusLayer->SetThickness(500);
            cumulusCongestusLayer->SetBaseLength(40000);
            cumulusCongestusLayer->SetBaseWidth(40000);
            cumulusCongestusLayer->SetDensity(0.3);

            // Note, we pass in X and -Y since this accepts "east" and "south" coordinates.
            cumulusCongestusLayer->SetLayerPosition(pos.x(), -pos.y());
            cumulusCongestusLayer->SeedClouds(*atmosphere);
            cumulusCongestusLayer->GenerateShadowMaps(false);

            atmosphere->GetConditions()->AddCloudLayer(cumulusCongestusLayer);

        }
    }
}
void SkyDrawable::setMatrices(osg::Camera *camera, SilverLining::Atmosphere *atm) const
{
	osg::Matrixd projection = camera->getProjectionMatrix();
	osg::Matrixd view = camera->getViewMatrix();

	double slProj[16], slView[16];
	int idx = 0;
	for (int row = 0; row < 4; row++) {
		for (int col = 0; col < 4; col++) {
			slProj[idx] = projection(row, col);
			slView[idx] = view(row, col);
			idx++;
		}
	}

	atm->SetCameraMatrix(slView);
	atm->SetProjectionMatrix(slProj);
}

void SkyDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
{
	SilverLining::Atmosphere *atmosphere = 0;

	AtmosphereReference *ar = dynamic_cast<AtmosphereReference *>(renderInfo.getCurrentCamera()->getUserData());
	if (ar) atmosphere = ar->atmosphere;

	renderInfo.getState()->disableAllVertexArrays();

    if (atmosphere)
    {
        initializeSilverLining(ar);
        setMatrices(renderInfo.getCurrentCamera(), atmosphere);
        atmosphere->DrawSky(true, false, _skyboxSize, true, false);
        setLighting(atmosphere);
    }

	renderInfo.getState()->dirtyAllVertexArrays();
}
