/***********************************************************************
**
**   airspace.cpp
**
**   This file is part of KFLog
**
************************************************************************
**
**   Copyright (c):  2000 by Heiner Lamprecht, Florian Ehinger
**
**   This file is distributed under the terms of the General Public
**   Licence. See the file COPYING for more information.
**
**   $Id: airspace.cpp,v 1.14 2004/03/14 17:50:46 andre Exp $
**
***********************************************************************/

#include <qobject.h>
#include "targets.h"
#include "airspace.h"

Airspace::Airspace(QString n, BaseMapElement::objectType t, QPointArray pA,
      int u, BaseMapElement::elevationType uType,
      int l, BaseMapElement::elevationType lType)
  : LineElement(n, t, pA),
    lLimitType(lType), uLimitType(uType)
{
  type = t;
  // All Airspaces are closed regions ...
  closed = true;
  // Normalize values 
  double lLim;
  switch( lLimitType ) {
  case GND: 
  case MSL: 
     lLim = mFromFeet*l;
     break;
  case FL: lLim = mFromFeet*100.0*l;
     break;
  case UNLTD: lLim=99999;
     break;
  case NotSet: lLim=0;
     break;
  default:
     lLim=0;
  };
  lLimit.setMeters( lLim );
  double uLim=0.0;
  switch( uLimitType ) {
  case GND: 
  case MSL: 
     uLim = mFromFeet*u;
     break;
  case FL: uLim = mFromFeet*100.0*u;
     break;
  case UNLTD: lLim=99999;
     break;
  case NotSet: lLim=0;
     break;
  default:
     uLim=0.0;
  };
  uLimit.setMeters( uLim );
}

Airspace::~Airspace()
{

}

QRegion* Airspace::drawRegion(QPainter* targetP, QPainter* /*maskP*/)
{
  if(!glConfig->isBorder(typeID) || !__isVisible()) return 0;

  QPointArray tA = glMapMatrix->map(projPointArray);

  //temporary hack because of a bug in QT/Embedded. Airspaces are not filled anymore :-(
  QBrush drawB = QBrush::NoBrush; //glConfig->getDrawBrush(typeID); 
  QPen drawP = glConfig->getDrawPen(typeID);
  drawP.setJoinStyle(Qt::RoundJoin);

  //maskP->setBrush(QBrush(Qt::color1, drawB.style()));
  //maskP->setPen(QPen(Qt::color1, drawP.width(), drawP.style(),drawP.capStyle(), drawP.joinStyle()));
  //maskP->drawPolygon(tA);
  /*qDebug("Airspace %s; Pointarray:", getInfoString().latin1());
  for (int i=0;i < tA.count()-1; i++) {
    qDebug("  (%d, %d)",tA[i].x(),tA[i].y());
  }  */
  targetP->setPen(drawP);
  targetP->setBrush(drawB);
  targetP->drawPolygon(tA);
  drawB.setStyle(QBrush::NoBrush);
  targetP->setBrush(drawB);
  targetP->drawPolygon(tA);

  /*#warning: FIXME: we should change to use the plain pointarray (projPointArray), so our precision isn't
       depending on the current scale */
  return (new QRegion(tA));     

}

/**
 * Returns a text representing the type of the airspace
 */
QString Airspace::getTypeName (objectType type)
{
  switch(type)
  {
    case BaseMapElement::AirA:
      return QObject::tr("Class A");
    case BaseMapElement::AirB:
      return QObject::tr("Class B");
    case BaseMapElement::AirC:
      return QObject::tr("Class C");
    case BaseMapElement::AirD:
      return QObject::tr("Class D");
    case BaseMapElement::AirElow:
      return QObject::tr("Class E (low)");
    case BaseMapElement::AirEhigh:
      return QObject::tr("Class E (high)");
    case BaseMapElement::AirF:
      return QObject::tr("Class F");
    case BaseMapElement::Restricted:
      return QObject::tr("Restricted");
    case BaseMapElement::Danger:
      return QObject::tr("Danger");
    case BaseMapElement::ControlC:
      return QObject::tr("Control C");
    case BaseMapElement::ControlD:
      return QObject::tr("Control D");
    case BaseMapElement::LowFlight:
      return QObject::tr("Low Flight");
    case BaseMapElement::Tmz:
      return QObject::tr("TMZ");
    default:
      return "<B><EM>" + QObject::tr("unknown") + "</EM></B>";
  }
}
    
QString Airspace::getInfoString() const
{
  QString text, tempL, tempU;
  
  QString type;
  
 
  switch(lLimitType)
    {
      case MSL:
        tempL.sprintf("%s MSL", (const char *)lLimit.getText(true,0));
        break;
      case GND:
        if(lLimit.getMeters())
            tempL.sprintf("%s GND", (const char *)lLimit.getText(true,0));
        else
            tempL = "GND";
        break;
      case FL:
        tempL.sprintf("%s STANDARD", (const char *)lLimit.getText(true,0));
        break;
      case UNLTD:
        tempL = QObject::tr("Unlimited");
      default: ;
    }

  switch(uLimitType)
    {
      case MSL:
        if(uLimit.getMeters() >= 99999)
            tempU = "unlimited";
        else
            tempU.sprintf("%s MSL", (const char *)uLimit.getText(true,0));
        break;
      case GND:
        tempU.sprintf("%s GND", (const char *)uLimit.getText(true,0));
        break;
      case FL:
        tempU.sprintf("%s STANDARD", (const char *)uLimit.getText(true,0));
        break;
      case UNLTD:
        tempL = QObject::tr("Unlimited");
      default: ;
    }

  text = getTypeName(typeID);

  text += " " + name + "<BR>" +
      "<FONT SIZE=-1>" + tempL + " / " + tempU + "</FONT>";

  return text;
}

/**
 * Returns true if the given altitude conflicts with the airspace properties
 */
//bool Airspace::conflicts (const Altitude& alt) const
Airspace::ConflictType Airspace::conflicts (const AltitudeCollection& alt,  const AirspaceWarningDistance& dist) const
{
  Altitude lowerAlt(0);
  Altitude upperAlt(0);

  //set which altitude to use from our range of available altitudes, and apply uncertainty margins
  //#warning FIXME: we should take our GPS error into account
  switch (lLimitType)
  {
    case NotSet:
      break;
    case MSL:
      lowerAlt=alt.gpsAltitude;
      break;
    case GND:
      lowerAlt=alt.gndAltitude + alt.gndAltitudeError;  //we need to use a conservative estimate
      if (lLimit==0) lowerAlt.setMeters(1); //we're always above ground
      break;
    case FL:
      lowerAlt=alt.pressureAltitude;       //flightlevels are allways at pressure altitude!
      break;
    case UNLTD:
      return none;
  }

  switch (uLimitType)
  {
    case NotSet:
      upperAlt.setMeters(100000);
      break;
    case MSL:
      upperAlt=alt.gpsAltitude;
      break;
    case GND:
      upperAlt=alt.gndAltitude - alt.gndAltitudeError;  //we need to use a conservative estimate
      break;
    case FL:
      upperAlt=alt.pressureAltitude;
      break;
    case UNLTD:
      upperAlt=uLimit-1; //we are always below the upper border of an Unlimited airspace
  }

  //check to see if we're inside the airspace
  if ((lowerAlt.getMeters() >= lLimit.getMeters()) && (upperAlt.getMeters() <= uLimit.getMeters())) {
    return inside;
  }
  //not inside. Check to see if we're very near to the airspace
  if ((lowerAlt >= (lLimit - dist.verBelowVeryClose)) && (upperAlt <= (uLimit + dist.verAboveVeryClose))) {
    return veryNear;
  }
  //not very near. Just near then?
  if ((lowerAlt >= (lLimit - dist.verBelowClose)) && (upperAlt <= (uLimit + dist.verAboveClose))) {
    return near;
  }

  //nope, we're not even near.
  return none;
}
