/***************************************************************************
                          selectionpopup.cpp  -  description
                             -------------------
    begin                : 2003-05-06
    copyright            : (C) 2002 by Christophe Teyssier
    email                : chris@teyssier.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/


#include <libintl.h>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <libintl.h>

#include "selectionpopup.h"

#include <qlabel.h>

#include <klocale.h>
#include <kurl.h>
#include <krun.h>

#include "celutil/utf8.h"
#include "celestia/celestiacore.h"
#include "celengine/axisarrow.h"
#include "celengine/planetgrid.h"


SelectionPopup::SelectionPopup(QWidget* parent, CelestiaCore* _appCore, Selection _sel):
KPopupMenu(parent),
 appCore(_appCore),
 sel(_sel),
 baseId(0)
{
}

SelectionPopup::~SelectionPopup() {

}

void SelectionPopup::init()
{
    QPalette pal( palette() );
    pal.setColor( QPalette::Normal, QColorGroup::Background, QColor( "White" ) );
    pal.setColor( QPalette::Normal, QColorGroup::Foreground, QColor( "Black" ) );
    pal.setColor( QPalette::Inactive, QColorGroup::Foreground, QColor( "Black" ) );

    QFont rsFont = font();
    rsFont.setPointSize( rsFont.pointSize() - 2 );

    Simulation *sim = appCore->getSimulation();
    Vec3d v = sel.getPosition(sim->getTime()) - sim->getObserver().getPosition();

    if (sel.body() != NULL)
    {
        insertTitle(QString::fromUtf8(sel.body()->getName(true).c_str()), 0, 0);
        insert(this, sel);
    }
    else if (sel.star() != NULL)
    {
        std::string name = sim->getUniverse()->getStarCatalog()->getStarName(*sel.star());

        double distance = v.length() * 1e-6;
        char buff[50];

        ostringstream o;
        setlocale(LC_NUMERIC, "");

        if (abs(distance) >= astro::AUtoLightYears(1000.0f))
            sprintf(buff, "%'.3f %s", distance, _("ly"));
        else if (abs(distance) >= astro::kilometersToLightYears(10000000.0))
            sprintf(buff, "%'.3f %s", astro::lightYearsToAU(distance), _("au"));
        else if (abs(distance) > astro::kilometersToLightYears(1.0f))
            sprintf(buff, "%'.3f km", astro::lightYearsToKilometers(distance));
        else
            sprintf(buff, "%'.3f m", astro::lightYearsToKilometers(distance) * 1000.0f);

        o << i18n("Distance: ") << QString::fromUtf8(buff) << "\n";

        o << i18n("Abs (app) mag: ");

        sprintf(buff, "%'.2f (%.2f)",
                   sel.star()->getAbsoluteMagnitude(),
                   astro::absToAppMag(sel.star()->getAbsoluteMagnitude(),
                                      (float) distance));
        o << QString::fromUtf8(buff) << "\n";

        o << i18n("Class: ");
        sprintf(buff, "%s", sel.star()->getSpectralType());
        o << QString::fromUtf8(buff) << "\n";

        o << i18n("Surface Temp: ");
        sprintf(buff, "%'.0f K", sel.star()->getTemperature());
        o << QString::fromUtf8(buff) << "\n";

        o << i18n("Radius: ");
        sprintf(buff, "%'.2f %s", sel.star()->getRadius() / 696000.0f, _("Rsun"));
        o << QString::fromUtf8(buff);

        setlocale(LC_NUMERIC, "C");

        QLabel *starDetails = new QLabel(QString(o.str().c_str()), this);
        starDetails->setPalette(pal);
        starDetails->setFont(rsFont);

        insertTitle(QString::fromUtf8(ReplaceGreekLetterAbbr(name).c_str()), 0, 0);
        insertItem(starDetails);
        insertSeparator();
        insert(this, sel);
    }
    else if (sel.deepsky() != NULL)
    {
        insertTitle(QString::fromUtf8(sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky()).c_str()), 0);
        insert(this, sel);
    }

    if (sim->getUniverse() != NULL)
    {
        MarkerList* markers = sim->getUniverse()->getMarkers();
        if (markers->size() > 0)
        {
            KPopupMenu *markMenu = new KPopupMenu(this);
            int j=1;
            for (std::vector<Marker>::iterator i = markers->begin(); i < markers->end(); i++)
            {
                KPopupMenu *objMenu = new KPopupMenu(this);
                insert(objMenu, (*i).object(), false);
                markMenu->insertItem(QString::fromUtf8(getSelectionName((*i).object())), objMenu);
                j++;
            }
            insertItem(i18n("Marked objects"), markMenu);
        }
    }
}

void SelectionPopup::process(int id)
{
    if (id < 0) return;

    Simulation *sim = appCore->getSimulation();

    sel = getSelectionFromId(id);
    int actionId = id - baseId;

    if (actionId == 1) {
        sim->setSelection(sel);
        return;
    }
    if (actionId == 2) {
        sim->setSelection(sel);
        appCore->charEntered('c');
        return;
    }
    if (actionId == 3) {
        sim->setSelection(sel);
        appCore->charEntered('g');
        return;
    }
    if (actionId == 4) {
        sim->setSelection(sel);
        appCore->charEntered('f');
        return;
    }
    if (actionId == 5) {
        sim->setSelection(sel);
        appCore->charEntered('y');
        return;
    }
    if (actionId == 6) {
        QString url;
        if (sel.body() != NULL)
        {
            url = QString(sel.body()->getInfoURL().c_str());
            if (url == "")
            {
                QString name = QString(sel.body()->getName().c_str()).lower();
                url = QString("http://www.nineplanets.org/") + name + ".html";
            }
        }
        else if (sel.star() != NULL)
        {
            if (sel.star()->getCatalogNumber() != 0)
                url = QString("http://simbad.u-strasbg.fr/sim-id.pl?protocol=html&Ident=HIP %1")
                      .arg(sel.star()->getCatalogNumber() & ~0xf0000000);
            else
                url = QString("http://www.nineplanets.org/sun.html");
        }
        else if (sel.deepsky() != NULL)
        {
            url = QString(sel.deepsky()->getInfoURL().c_str());
            if (url == "")
                url = QString("http://simbad.u-strasbg.fr/sim-id.pl?protocol=html&Ident=%1")
                      .arg(sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky()).c_str());
        }
        KRun::runURL(url, "text/html");
        return;
    }
    if (actionId == 7)
    {
        if (sim->getUniverse() != NULL)
            sim->getUniverse()->unmarkObject(sel, 1);
        return;
    }
    if (actionId == 8)
    {
        sim->getUniverse()->unmarkAll();
        return;
    }
    if (actionId >= 10 && actionId < 25)
    {
        if (sim->getUniverse() != NULL)
        {
            MarkerRepresentation markerRep((MarkerRepresentation::Symbol) (actionId - 1), 10.0f, Color(0.0f, 1.0f, 0.0f, 0.9f));

            sim->getUniverse()->markObject(sel, markerRep, 1);
        }
        return;
    }
    if (actionId >=25 && actionId < 31 && sel.body() != NULL) 
    {

        switch(actionId)
        {
        case 25:
		appCore->toggleReferenceMark("body axes");
            break;
        case 26:
		appCore->toggleReferenceMark("frame axes");
            break;
        case 27:
		appCore->toggleReferenceMark("sun direction");
            break;
        case 28:
		appCore->toggleReferenceMark("velocity vector");
            break;
	case 29:
		appCore->toggleReferenceMark("planetographic grid");
	    break;
	case 30:
		appCore->toggleReferenceMark("terminator");
	    break;
        }

    }
    if (actionId == 31) {
        sim->getActiveObserver()->setDisplayedSurface("");
        return;
    }
    if (actionId > 31) {
        std::vector<std::string>* altSurfaces = sel.body()->getAlternateSurfaceNames();
        if (altSurfaces != NULL && (int) altSurfaces->size() > actionId - 32)
        {
            sim->getActiveObserver()->setDisplayedSurface((*altSurfaces)[actionId - 32]);
        }
    }
}

const char* SelectionPopup::getSelectionName(const Selection& sel) const
{
    if (sel.body() != NULL)
        return sel.body()->getName(true).c_str();
    else if (sel.star() != NULL)
        return ReplaceGreekLetterAbbr(appCore->getSimulation()->getUniverse()->getStarCatalog()->getStarName(*(sel.star()))).c_str();
    else if (sel.deepsky() != NULL)
        return appCore->getSimulation()->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky()).c_str();

    return "";
}

Selection SelectionPopup::getSelectionFromId(int id)
{
    for(std::vector< std::pair<int, Selection> >::const_iterator i = baseIds.begin() + 1;
        i != baseIds.end();
        ++i) {
        if ((*i).first > id) {
            baseId = (*(i-1)).first;
            return (*(i-1)).second;
        }
    }
    baseId = baseIds.back().first;
    return baseIds.back().second;
}

void SelectionPopup::insert(KPopupMenu *popup, Selection sel, bool showSubObjects)
{
    baseIds.push_back(make_pair(baseId, sel));
    int locBaseId = baseId;

    popup->insertItem(i18n("&Select"), baseId + 1);
    popup->insertItem(i18n("&Center"), baseId + 2);
    popup->insertItem(i18n("&Goto"), baseId + 3);
    popup->insertItem(i18n("&Follow"), baseId + 4);
    if (sel.star() == NULL && sel.deepsky() == NULL)
        popup->insertItem(i18n("S&ynch Orbit"), baseId + 5);
    popup->insertItem(i18n("&Info"), baseId + 6);
    if (baseId == 0)
        popup->insertItem(i18n("Unmark &All"), baseId + 8);

    if (appCore->getSimulation()->getUniverse()->isMarked(sel, 1))
    {
        popup->insertItem(i18n("&Unmark"), baseId + 7);
    }
    else
    {
        KPopupMenu *markMenu = new KPopupMenu(this);
        markMenu->insertItem(i18n("Diamond"), baseId + 10);
        markMenu->insertItem(i18n("Triangle"), baseId + 11);
        markMenu->insertItem(i18n("Square"), baseId + 12);
        markMenu->insertItem(i18n("Filled Square"), baseId + 13);
        markMenu->insertItem(i18n("Plus"), baseId + 14);
        markMenu->insertItem(i18n("X"), baseId + 15);
        markMenu->insertItem(i18n("Left Arrow"), baseId + 16);
        markMenu->insertItem(i18n("Right Arrow"), baseId + 17);
        markMenu->insertItem(i18n("Up Arrow"), baseId + 18);
        markMenu->insertItem(i18n("Down Arrow"), baseId + 19);
        markMenu->insertItem(i18n("Circle"), baseId + 20);
        markMenu->insertItem(i18n("Disk"), baseId + 21);
        popup->insertItem(i18n("&Mark"), markMenu);
    }

    if (sel.body() != NULL) {
        KPopupMenu *refVectorMenu = new KPopupMenu(this);
        refVectorMenu->setCheckable(true);
        popup->insertItem(i18n("&Reference Marks"), refVectorMenu);
        refVectorMenu->insertItem(i18n("Show Body Axes"), baseId + 25);
        refVectorMenu->setItemChecked(baseId + 25, sel.body()->findReferenceMark("body axes"));
        refVectorMenu->insertItem(i18n("Show Frame Axes"), baseId + 26);
        refVectorMenu->setItemChecked(baseId + 26, sel.body()->findReferenceMark("frame axes"));
        refVectorMenu->insertItem(i18n("Show Sun Direction"), baseId + 27);
        refVectorMenu->setItemChecked(baseId + 27, sel.body()->findReferenceMark("sun direction"));
        refVectorMenu->insertItem(i18n("Show Velocity Vector"), baseId + 28);
        refVectorMenu->setItemChecked(baseId + 28, sel.body()->findReferenceMark("velocity vector"));
        refVectorMenu->insertItem(i18n("Show Planetographic Grid"), baseId + 29);
        refVectorMenu->setItemChecked(baseId + 29, sel.body()->findReferenceMark("planetographic grid"));
        refVectorMenu->insertItem(i18n("Show Terminator"), baseId + 30);
        refVectorMenu->setItemChecked(baseId + 30, sel.body()->findReferenceMark("terminator"));
    }

    baseId += 31;

    if (showSubObjects && sel.body() != NULL)
    {
        std::vector<std::string>* altSurfaces = sel.body()->getAlternateSurfaceNames();
        if (altSurfaces != NULL)
        {
            if (!altSurfaces->empty())
            {
                KPopupMenu *surfaces = new KPopupMenu(this);
                surfaces->insertItem(i18n("Normal"), locBaseId + 31);
                int j=0;
                for (std::vector<std::string>::const_iterator i = altSurfaces->begin();
                     i < altSurfaces->end(); i++, j++)
                {
                    surfaces->insertItem(QString((*i).c_str()), locBaseId + 32 + j);
                }
                baseId += 7 + j;
                popup->insertItem(i18n("&Alternate Surfaces"), surfaces);
            }
            delete altSurfaces;
        }

        const PlanetarySystem* satellites = sel.body()->getSatellites();
        if (satellites != NULL && satellites->getSystemSize() != 0)
        {
            insertPlanetaryMenu(popup, getSelectionName(sel), satellites);
        }
    }
    else if (showSubObjects && sel.star() != NULL)
    {
        Simulation *sim = appCore->getSimulation();
        SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();
        SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel.star()->getCatalogNumber());
        if (iter != solarSystemCatalog->end())
        {
            SolarSystem* solarSys = iter->second;
            insertPlanetaryMenu(popup, getSelectionName(sel), solarSys->getPlanets());
        }
    }
}

struct BodyComparePredicate
{
    bool operator()(Body* a, Body* b)
    {
        return (a->getName(true).compare(b->getName(true)) < 0);
    }
};


void SelectionPopup::insertPlanetaryMenu(KPopupMenu* popup, const string& parentName, const PlanetarySystem* psys) {
    std::vector<Body*> asteroids;
    std::vector<Body*> comets;
    std::vector<Body*> invisibles;
    std::vector<Body*> moons;
    std::vector<Body*> planets;
    std::vector<Body*> spacecraft;
    std::vector<Body*> smallBodies;
    std::vector<Body*> dwarfPlanets;

    std::vector<std::vector<Body*>* > objects;
    std::vector<std::string> menuNames;

    // Add each vector of PlanetarySystem bodies to a vector to iterate over
    objects.push_back(&asteroids);
    menuNames.push_back(QString::fromUtf8(_("Asteroids")));
    objects.push_back(&comets);
    menuNames.push_back(QString::fromUtf8(_("Comets")));
    objects.push_back(&moons);
    menuNames.push_back(QString::fromUtf8(_("Moons")));
    objects.push_back(&planets);
    menuNames.push_back(QString::fromUtf8(_("Planets")));
    objects.push_back(&spacecraft);
    menuNames.push_back(QString::fromUtf8(_("Spacecraft")));
    objects.push_back(&smallBodies);
    menuNames.push_back(QString::fromUtf8(_("Small Bodies")));
    objects.push_back(&dwarfPlanets);
    menuNames.push_back(QString::fromUtf8(_("Dwarf Planets")));

    for (int i = 0; i < psys->getSystemSize(); i++) {
        Body* body = psys->getBody(i);
        switch(body->getClassification()) {
        case Body::Asteroid:
            asteroids.push_back(body);
            break;
        case Body::Comet:
            comets.push_back(body);
            break;
        case Body::Moon:
            moons.push_back(body);
            break;
        case Body::Planet:
            planets.push_back(body);
            break;
        case Body::Spacecraft:
            spacecraft.push_back(body);
            break;
        case Body::SmallBody:
            smallBodies.push_back(body);
            break;
        case Body::DwarfPlanet:
            dwarfPlanets.push_back(body);
            break;
        case Body::Invisible:
        case Body::Barycenter:
            break;
        }
    }

    // Count how many submenus we need to create

    int numSubMenus = 0;
    int nonEmpty = 0;
    for (std::vector<std::vector<Body*>* >::const_iterator obj = objects.begin();
         obj != objects.end();
         ++obj) {
        if ((*obj)->size() > 0) {
            numSubMenus++;
            nonEmpty = obj - objects.begin();
        }
    }

    if (numSubMenus == 0) return;

    KPopupMenu *submenu = new KPopupMenu(this);
    popup->insertSeparator();

    if (numSubMenus > 1)
        popup->insertItem(i18n("Orbiting Bodies"), submenu);
    else
        popup->insertItem(menuNames[nonEmpty], submenu);

    std::vector<std::string>::const_iterator menuName = menuNames.begin();
    BodyComparePredicate pred;

    for (std::vector<std::vector<Body*>* >::const_iterator obj = objects.begin();
         obj != objects.end();
         ++obj) {
        // Only generate a submenu if the vector is not empty
        if ((*obj)->size() > 0) {
            // Don't create a submenu for a single item
            if ((*obj)->size() == 1) {
                std::vector<Body*>::const_iterator it = (*obj)->begin();
                Selection s(*it);
                KPopupMenu *objMenu = new KPopupMenu(this);
                insert(objMenu, s);
                submenu->insertItem(QString::fromUtf8((*it)->getName(true).c_str()), objMenu);
            } else {
                // Skip sorting if we are dealing with the planets in our own Solar System.
                 if (parentName != "Sol" || *menuName != QString::fromUtf8(_("Planets")))
                     std::sort((*obj)->begin(), (*obj)->end(), pred);

                if (numSubMenus > 1) {
                    // Add items to submenu
                    KPopupMenu* objMenu = new KPopupMenu(this);
                    for(std::vector<Body*>::const_iterator it = (*obj)->begin(); it != (*obj)->end(); ++it) {
                        Selection s(*it);
                        KPopupMenu *menu = new KPopupMenu(this);
                        insert(menu, s);
                        objMenu->insertItem(QString::fromUtf8((*it)->getName(true).c_str()), menu);
                    }
                    submenu->insertItem(*menuName, objMenu);
                } else {
                    // Just add items to the popup
                    for(std::vector<Body*>::const_iterator it = (*obj)->begin(); it != (*obj)->end(); ++it) {
                        Selection s(*it);
                        KPopupMenu *menu = new KPopupMenu(this);
                        insert(menu, s);
                        submenu->insertItem(QString::fromUtf8((*it)->getName(true).c_str()), menu);
                    }
                }
            }
        }
        menuName++;
    }
}

