/***********************************************************************
**
**   wpinfowidget.cpp
**
**   This file is part of Cumulus.
**
************************************************************************
**
**   Copyright (c):  2002 by Andr�Somers
**
**   This file is distributed under the terms of the General Public
**   Licence. See the file COPYING for more information.
**
**   $Id: wpinfowidget.cpp,v 1.52 2005/02/23 13:55:38 andre Exp $
**
***********************************************************************/

#include "wpinfowidget.h"
#include "generalconfig.h"
#include "cucalc.h"
#include "translationlist.h"
#include "mapcalc.h"
#include "wgspoint.h"
#include "altitude.h"
#include "mapcontents.h"
#include "waypointcatalog.h"
#include "reachablelist.h"
#include <qmessagebox.h>
#include <qfont.h>

extern MapConfig * _globalMapConfig;

WPInfoWidget::WPInfoWidget(CumulusApp *parent, const char *name ): QWidget(parent,name) {
    cuApp=parent;
    _wp=0;
    resize(parent->size());
    QBoxLayout *topLayout = new QVBoxLayout( this );
    text= new QTextView(this, "waypointinfo");
    text->setFocus();
    topLayout->addWidget(text,10);

    buttonrow2=new QHBoxLayout(topLayout);

    QFont font( "Helvetica", 12, QFont::Bold  );

    cmdAddWaypoint = new QPushButton(tr("Add Waypoint"), this);
    cmdAddWaypoint->setFont(font);
    buttonrow2->addWidget(cmdAddWaypoint);
    connect(cmdAddWaypoint, SIGNAL(clicked()), this, SLOT(slot_addAsWaypoint()));

    cmdSetHome = new QPushButton(tr("New Home"), this);
    cmdSetHome->setFont(font);
    buttonrow2->addWidget(cmdSetHome);
    connect(cmdSetHome, SIGNAL(clicked()), this, SLOT(slot_setAsHome()));

    buttonrow1=new QHBoxLayout(topLayout);

    cmdClose = new QPushButton(tr("Close"), this);
    cmdClose->setFont(font);
    buttonrow1->addWidget(cmdClose);
    connect(cmdClose, SIGNAL(clicked()), this, SLOT(slotSwitchBack()));

    cmdKeep = new QPushButton(tr("Keep"), this);
    cmdKeep->setFont(font);
    buttonrow1->addWidget(cmdKeep);
    connect(cmdKeep, SIGNAL(clicked()), this, SLOT(slot_KeepOpen()));

    cmdUnselectWaypoint = new QPushButton(tr("Unselect"), this);
    cmdUnselectWaypoint->setFont(font);
    buttonrow1->addWidget(cmdUnselectWaypoint);
    connect(cmdUnselectWaypoint, SIGNAL(clicked()), this, SLOT(slot_unselectWaypoint()));

    cmdSelectWaypoint = new QPushButton(tr("Select"), this);
    cmdSelectWaypoint->setFont(font);
    buttonrow1->addWidget(cmdSelectWaypoint);
    connect(cmdSelectWaypoint, SIGNAL(clicked()), this, SLOT(slot_selectWaypoint()));

    timer=new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(slot_timeout()));
}


WPInfoWidget::~WPInfoWidget()
{
  if( _wp )
    {
      delete _wp;
    }
}

/** This slot get called on the timer timeout. */
void WPInfoWidget::slot_timeout(){
    if(--_timerCount==0) {
        timer->stop();
        slotSwitchBack();
    } else {
        QString txt;
        txt.sprintf(tr("Close (%d)"),_timerCount);
        cmdClose->setText(txt);
    }
}

/** This method is called by CumulusApp to set the view to which there must be returned and the waypoint to view. */
bool WPInfoWidget::showWP(int lastView, const wayPoint * wp)
{
    extern MapContents* _globalMapContents;
    extern MapMatrix*   _globalMapMatrix;

    if( wp == 0 )
      {
        return false;
      }

    // Check if new point is in waypoint list, so make sure we can add it.
    if (_globalMapContents->getIsInWaypointList(wp))
          cmdAddWaypoint->hide();
    else
          cmdAddWaypoint->show();

    // check, if current home position is different from waypoint

    QPoint home = _globalMapMatrix->getHomeCoord();

    if( home == wp->origP || inFlight() )
      {
	cmdSetHome->hide();
      }
    else
      {
	cmdSetHome->show();
      }

    // Check if Waypoint is not selected, so make sure we can select
    // it. We include in the comparison the icao item and the position
    // to get a unique criteria.

    const wayPoint *calcWp = calculator->getselectedWp();

    if( calcWp )
      {
	if( wp->name  == calcWp->name &&
	    wp->icao  == calcWp->icao &&
	    wp->origP == calcWp->origP )
	  {
	    cmdUnselectWaypoint->show();
	    cmdSelectWaypoint->hide();
	  }
	else
	  {
	    cmdSelectWaypoint->show();
	    cmdUnselectWaypoint->hide();
	  }
      }
    else
      {
        cmdSelectWaypoint->show();
        cmdUnselectWaypoint->hide();
      }

    // save new values. We make a deep copy to prevent problems with constant methods elsewhere.
    if (_wp!=0)
    {
      delete _wp;
    }
    _wp = new wayPoint(*wp);
    _lastView=lastView;

    writeText();

    // @AP: load the time from user configuration
    GeneralConfig *conf = GeneralConfig::instance();
    _timerCount = conf->getInfoDisplayTime();

    if( _timerCount > 0 )
      {
	timer->start(1000);
	QString txt;
	txt.sprintf(tr("Close (%d)"), _timerCount);
	cmdClose->setText(txt);
	cmdKeep->show();
      }
    else
      {
	// Timer is set to zero, no automatic window close
	cmdClose->setText(tr("Close"));
	cmdKeep->hide();
      }

    return true;
}


/** This method actually fills the widget with the info. */
void WPInfoWidget::writeText(){
    extern TranslationList surfaces;
    extern TranslationList waypointTypes;
    extern CuCalc* calculator;

    Distance distance;
    //Altitude alt;
    Altitude arrivalAlt;
    reachable reach;

    if (_wp==0) {
        text->setText("<qt><big><center><b>"+tr("No waypoint selected.")+"</b></center></big></qt>");
    } else {
        //display info on waypoint
        QString itxt;
        QString tmp;
        int iTmp;
        itxt+=  "<font size=\"+1\">";
        itxt+= "<qt><big><center><u><b>" + _wp->description + " (" + _wp->name;
        if (!_wp->icao.isEmpty())
            itxt+=",&nbsp;"+ _wp->icao;
        itxt+= ")</b></u></center></big><br>";
        itxt+="<b>" + waypointTypes.itemById(_wp->type)->text + "</b><br>";
        // qDebug("_wp->runway: %d",_wp->runway ); 
        if (_wp->isLandable) {
            iTmp=_wp->surface;
            if( iTmp > 3 ) 
                iTmp = 0;
            // qDebug("_wp->surface %d", _wp->surface );
            if (iTmp<0) iTmp=0;
            QString tmp2;
            if (_wp->runway<0) {
                tmp2=tr("Unknown");
            } else {
                // @AP: show runway in both directions, start with the lowest one
                int rw1 =_wp->runway;
                int rw2 = rw1 <= 180 ? rw1+180 : rw1-180;

                tmp2.sprintf("<b>%02d/%02d</b>", rw1 < rw2 ? rw1/10 : rw2/10, rw1 < rw2 ? rw2/10 : rw1/10);
            }

            itxt+=tmp.sprintf(tr("Landable, Runway: ") + tmp2 + "<br>"+tr("Surface: ")+"%s, "+tr("Length: ")+"%d m.<br>", surfaces.itemById(iTmp)->text.latin1(), _wp->length);
        } else {
            itxt+="<font color=\"#FF0000\"><b>"+tr("NOT LANDABLE")+"</b></font>";
        }

#warning FIXME: this should not be necessary!
        if (_wp->frequency>100 && _wp->frequency<200) {
            itxt+=tmp.sprintf("<table><tr><td>"+tr("Frequency:")+"</td><td><b>%1.3f</b></td></tr><br>",_wp->frequency);
        } else {
            itxt+="<table><tr><td>"+tr("Frequency:")+"</td><td><b>"+tr("(Unknown)")+"</b></td></tr>";
        }

        distance=ReachableList::getDistance(_wp->origP);
        if( !distance.isValid() ) {
            //calculate distance and arrival altitude if we don't have that allready
        
            WGSPoint pt = _wp->origP;
            QPoint lastPosition=calculator->getlastPosition();
            distance.setKilometers(double(dist(&lastPosition,&pt)));
            // recalculate Bearing
            int bearing= int(getBearing(lastPosition, pt) * 57.2958);
            // glide path
            Speed bestSpeed;
            calculator->glidePath(bearing, distance,
                                  _wp->elevation,
                                  arrivalAlt, bestSpeed );
            reach=no;
        } else {
            //since a distance was found in the reachable list, there will be an arrival altitude and reachable flag also.
            arrivalAlt=ReachableList::getArrivalAltitude(_wp->origP);
            reach = ReachableList::getReachable(_wp->origP);
        }        

        if( distance.isValid() ) {
          itxt +=
                "<tr><td>Distance:</td><td><b>" + distance.getText(true,2) + "</b></td></tr>";
          // qDebug(" DInfo _wp->name %s", (const char *)_wp->description );
        }
        if( arrivalAlt.isValid() ) {
          switch (reach)
          {
            case yes:
              itxt += "<tr><td>Arrival Alt:</td><td><b>+" + arrivalAlt.getText(true,0) + "</b></td><tr>";
              break;
            case no:
              itxt += "<tr><td>Arrival Alt:</td><td><font color=\"#FF0000\"><b>" + arrivalAlt.getText(true,0) + "</font></b></td><tr>";
              break;
            case belowSavety:
              itxt += "<tr><td>Arrival Alt:</td><td><b><font color=\"#FF00FF\">" + arrivalAlt.getText(true,0) + "</font></b></td><tr>";
          }
        }
        itxt+=tmp.sprintf("<tr><td>"+tr("Elevation:") +
          "</td><td>%s</td></tr>",
          Altitude::getText(_wp->elevation, true, 0).latin1());
//        itxt += "<tr><td>&nbsp;</td><td>&nbsp;</td></tr></table>";

        QPoint wppnt=_wp->origP;
        itxt += "<font size=\"-0\"><tr><td>" + tr("Coord:")+"</td><td>" +
          WGSPoint::printPos(wppnt.x(),true) +
          "</td></tr> <tr><td>&nbsp;</td><td>" + 
          WGSPoint::printPos(wppnt.y(),false) + "</td></tr>" +
          "</font></table>";

        if ((_wp->comment!=QString::null) && (_wp->comment!="")) {
            itxt+="<u>"+tr("Comments")+"</u><br>" + _wp->comment;
        }

        itxt+="</qt>";

        text->setText(itxt);
    }
}

/** No descriptions */
void WPInfoWidget::slotSwitchBack(){
    if (_lastView==2)
      _lastView=0; //make sure lastview isn't this view

    cuApp->setView((CumulusApp::appView)_lastView);
    timer->stop();
}

/** This slot is called by the KeepOpen button to... yes... keep the dialog open. :-) */
void WPInfoWidget::slot_KeepOpen(){
    timer->stop();
    cmdClose->setText(tr("Close"));
    cmdKeep->hide();
}

/** This slot is called if the unselect Waypoint button is clicked. */
void WPInfoWidget::slot_unselectWaypoint(){
    cmdUnselectWaypoint->hide();
    cmdSelectWaypoint->show();
    slot_KeepOpen();
    emit waypointSelected(0);

    if( inFlight() )
    {
      slotSwitchBack();
    }
}

/** This slot is called if the Select Waypoint button is clicked. */
void WPInfoWidget::slot_selectWaypoint() {
    cmdUnselectWaypoint->show();
    cmdSelectWaypoint->hide();
    //cuApp->setView(CumulusApp::mapView);
    myWp = *_wp;  // copy waypoint
    slot_KeepOpen();
    emit waypointSelected(&myWp);

    slotSwitchBack();
}

/** This slot is called if the Add as Waypoint button is clicked. */
void WPInfoWidget::slot_addAsWaypoint(){
    _wp->importance=2; //importance is high
    cmdAddWaypoint->hide();
    slot_KeepOpen();
    emit waypointAdded(_wp);

    if( inFlight() )
    {
      slotSwitchBack();
    }
}

/** This slot is called if the Home button is clicked. */
void WPInfoWidget::slot_setAsHome()
{
  if( ! _wp )
  {
    return;
  }

  int answer= QMessageBox::warning(this,tr("Set homesite?"),
      tr("Do you want to use site\n%1\nas your homesite?").arg(_wp->name),
      QMessageBox::Ok | QMessageBox::Default,
      QMessageBox::Cancel | QMessageBox::Escape );
  if (answer == 1)
  { //ok was chosen
      // Save new data as home position
    GeneralConfig *conf = GeneralConfig::instance();
    wayPoint * w = new wayPoint(*_wp);
    conf->setHomeWp(w);
 //   conf->setHomeLat(_wp->origP.lat());
 //   conf->setHomeLon(_wp->origP.lon());
    conf->save();
    slot_KeepOpen();
    cmdSetHome->hide();

    QPoint newPos( _wp->origP.lat(), _wp->origP.lon() );
    emit newHomePosition( &newPos );

  }
  
  if( inFlight() )
  {
    slotSwitchBack();
  }
}

/** get back the current state of cumlus. In flight true, otherwise false */
bool WPInfoWidget::inFlight()
{
  extern CuCalc* calculator;
  extern GPSNMEA* gps;

  if( calculator->currentFlightMode() == CuCalc::unknown ||
      calculator->currentFlightMode() == CuCalc::standstill ||
      ! gps->getConnected() )
    {
      return false;
    }

  return true;
}
