/***************************************************************************
                          weatherlib.cpp  -  description
                             -------------------
    begin                : Wed Jul 5 2000
    copyright            : (C) 2000 by Ian Reinhart Geiser
    email                : geiseri@msoe.edu
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 <iostream.h>
#include <klocale.h>
#include <qdatetime.h>
#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>
#include "weatherlib.h"

weatherlib::weatherlib(QString location)
{
    reportLocation = location;
    CoverRegExp    = QRegExp("^(FEW|SCT|BKN|OVC|SKC|CLR|CAVOK)([0-9]{3})?");
    CurrentRegExp  = QRegExp("^(\\+|-|VC)?([A-Z]{2,4})$");
    WindRegExp     = QRegExp("^([0-9]{3}|VRB)([0-9]{2,3})(?:G([0-9]{2,3}))?(KT|KMH|MPS)$");
    // VisRegExp does not work with fractions...yet
    VisRegExp      = QRegExp("^([0-9]{1,2})SM$");
    TempRegExp     = QRegExp("^(M)?([0-9]{2})/(?:(M)?([0-9]{2}))?$");
    TimeRegExp     = QRegExp("^([0-9]{2}:[0-9]{2})$");
    DateRegExp     = QRegExp("^([0-9]{4}/[0-9]{2}/[0-9]{2})$");
    PressRegExp    = QRegExp("^([AQ])([0-9]{4})$");
}

weatherlib::~weatherlib(){
}

/** Parse the current cover type */
void weatherlib::parseCover( QString s){
    if (CoverRegExp.search(s) > -1)
    {
        QString sCode = CoverRegExp.cap(1);
        float fFeet = CoverRegExp.cap(2).toFloat()/10;
        QString skycondition = "";

        kdDebug() << "Cover: " << CoverRegExp.capturedTexts().join("-") << endl;

        if (sCode == "FEW")
        {
            skycondition = i18n( "Few clouds at %1 thousand feet" ).arg(fFeet);
            clouds += 2;
        }
        else if (sCode == "SCT")
        {
            skycondition = i18n( "Scattered clouds at %1 thousand feet" ).arg(fFeet);
            clouds += 4;
        }
        else if (sCode == "BKN")
        {
            skycondition = i18n( "Broken clouds at %1 thousand feet" ).arg(fFeet);
            clouds += 8;
        }
        else if (sCode == "OVC")
        {
            skycondition = i18n( "Overcast clouds at %1 thousand feet" ).arg(fFeet);
            clouds += 64;
        }
        else if ((sCode == "CLR") || (sCode == "SKC") || (sCode == "CAVOK"))
        {
            skycondition = i18n("No clouds");
        }
        else if (sCode == "XX")
        {
          // This condition should be removed.
            skycondition = i18n("Overcast clouds");
            clouds = 16;
        }

        kdDebug() << "*** Clouds: " << clouds << endl;
        qsCoverList << skycondition;
    }
}

/** Parse the current weather conditions */
void weatherlib::parseCurrent(QString s){
    if (CurrentRegExp.search(s) > -1)
    {
        QString sIntensity = CurrentRegExp.cap(1);
        QString sCode = CurrentRegExp.cap(2);
        QString currentWeather = "";

        kdDebug() << "Current: " << CurrentRegExp.capturedTexts().join("-") << endl;

        // Decode the intensity
        if (sIntensity == "+")
            currentWeather = i18n("Heavy");
        else if (sIntensity == "-")
            currentWeather = i18n("Light");

        // Decode the descriptor
        if (sCode.contains("MI"))
            currentWeather += i18n(" Shallow");
        else if (sCode.contains("PR"))
            currentWeather += i18n(" Partial");
        else if (sCode.contains("BC"))
            currentWeather += i18n(" Patches");
        else if (sCode.contains("DR"))
            currentWeather += i18n(" Low Drifting");
        else if (sCode.contains("BL"))
            currentWeather += i18n(" Blowing");
        else if (sCode.contains("SH"))
        {
            currentWeather += i18n(" Showers");
            theWeather = "shower";
        }
        else if (sCode.contains("TS"))
        {
            currentWeather += i18n(" Thunder Storm");
            theWeather = "tstorm";
        }
        else if (sCode.contains("FZ"))
        {
            currentWeather += i18n(" Freezing");
        }

        // Decode weather phenomena
        if (sCode.contains("DZ"))
        {
            currentWeather += i18n(" Drizzle");
            theWeather = "light_rain";
        }
        else if (sCode.contains("RA"))
        {
            currentWeather += i18n(" Rain");
            theWeather = "shower";
        }
        else if (sCode.contains("SN"))
        {
            currentWeather += i18n(" Snow");
            theWeather = "snow";
        }
        else if (sCode.contains("SG"))
        {
            currentWeather += i18n(" Snow Grains");
            theWeather = "snow4";
        }
        else if (sCode.contains("IC"))
        {
            currentWeather += i18n(" Ice Crystals");
            theWeather = "hail";
        }
        else if (sCode.contains("PE"))
        {
            currentWeather += i18n(" Ice Pellets");
            theWeather = "hail";
        }
        else if (s.contains("GR"))
        {
            currentWeather += i18n(" Hail");
            theWeather = "hail";
        }
        else if (sCode.contains("GS"))
        {
            currentWeather += i18n(" Small Hail Pellets");
            theWeather = "hail";
        }
        else if (s.contains("UP"))
        {
            currentWeather += i18n(" Unknown Precipitation");
            theWeather = "shower1";
        }
        else if (sCode.contains("BR"))
        {
            currentWeather += i18n(" Mist");
            theWeather = "mist";
        }
        else if (sCode.contains("FG"))
        {
            currentWeather += i18n(" Fog");
            theWeather = "fog";
        }
        else if (sCode.contains("FU"))
            currentWeather += i18n(" Smoke");
        else if (sCode.contains("VA"))
            currentWeather += i18n(" Volcanic Ash");
        else if (sCode.contains("DU"))
            currentWeather += i18n(" Widespread Dust");
        else if (sCode.contains("SA"))
            currentWeather += i18n(" Sand");
        else if (sCode.contains("HZ"))
            currentWeather += i18n(" Haze");
        else if (sCode.contains("PY"))
            currentWeather += i18n(" Spray");
        else if (sCode.contains("PO"))
            currentWeather += i18n(" Well Developed \n Dust Swirls");
        else if (sCode.contains("SQ"))
            currentWeather += i18n(" Squall");
        else if (sCode.contains("FC"))
            currentWeather += i18n(" Funnel Cloud");
        else if (sCode.contains("SS"))
            currentWeather += i18n(" Sand Storm");
        else if (sCode.contains("DS"))
            currentWeather += ""; // FIXME: Add Dust Storm after message freeze

        if (!currentWeather.isEmpty())
            qsCurrentList << currentWeather;
    }
}

/** Parse out the current temperature */
void weatherlib::parseTemperature(QString s){
    if (TempRegExp.search(s) > -1)
    {
        kdDebug() << "Temp: " << TempRegExp.capturedTexts().join("-") << endl;

        float fTemp = TempRegExp.cap(2).toFloat();
        if (TempRegExp.cap(1) == "M")
            fTemp *= -1;
        float fDew = TempRegExp.cap(4).toFloat();
        if (TempRegExp.cap(3) == "M")
            fDew *= -1;

        if (MetricMode)
        {
            qsTemperature.setNum(fTemp);
            qsTemperature += i18n("C");
        }
        else
        {
            qsTemperature.setNum((fTemp * 9 / 5) + 32);
            qsTemperature += i18n("F");
        }
    }
}

/** Parse out the current date. */
void weatherlib::parseDate(QString s){
    if (DateRegExp.search(s) > -1)
    {
        kdDebug() << "Date: " << DateRegExp.capturedTexts().join("-") << endl;

        qsDate = DateRegExp.cap(1);
    }
}

/** Parse out the current time. */
void weatherlib::parseTime(QString s){
    if (TimeRegExp.search(s) > -1)
    {
        kdDebug() << "Time: " << TimeRegExp.capturedTexts().join("-") << endl;

        qsTime = TimeRegExp.cap(1);
    }
}

/** Parse out the current visibility */
void weatherlib::parseVisibility( QString s){
    if (VisRegExp.search(s) > -1)
    {
        float fVisibility = VisRegExp.cap(1).toFloat();

        kdDebug() << "Visibility: " << VisRegExp.capturedTexts().join("-") << endl;

        if (MetricMode)
        {
            fVisibility *= 1.6;
            qsVisibility.setNum(fVisibility);
            qsVisibility += i18n("KM");
        }
        else
        {
            qsVisibility.setNum(fVisibility);
            qsVisibility += i18n("M");
        }
    }
}

/** Parse out the current pressure. */
void weatherlib::parsePressure( QString s){
    if (PressRegExp.search(s) > -1)
    {
        QString type = PressRegExp.cap(1);
        float fPressure = PressRegExp.cap(2).toFloat();

        kdDebug() << "Pressure: " << PressRegExp.capturedTexts().join("-") << endl;

        if (MetricMode)
        {
            if (type == "A")
                fPressure *= (33.8639/100);
            qsPressure.setNum(fPressure, 'f', 0);
            qsPressure += i18n(" hPa");
        }
        else
        {
            if (type == "Q")
                fPressure /= 33.8639;
            else
                fPressure /= 100;
            qsPressure.setNum(fPressure, 'f', 2);
            qsPressure += i18n("\" Hg");
        }
    }
}

/** Parse the wind speed */
void weatherlib::parseWindSpeed( QString s)
{
    if (WindRegExp.search(s) > -1)
    {
        int iDirection = WindRegExp.cap(1).toInt();
        int iWindSpeed = WindRegExp.cap(2).toInt();
        int iGustSpeed = WindRegExp.cap(3).toInt();
        QString sWindUnit = WindRegExp.cap(4);

        kdDebug() << "Wind: " << WindRegExp.capturedTexts().join("-") << endl;

        if (MetricMode)
        {
            if (sWindUnit == "KT")
            {
                iWindSpeed = (int) (iWindSpeed * 3.6/1.94);
                iGustSpeed = (int) (iGustSpeed * 3.6/1.94);
            }
            else if (sWindUnit == "MPS")
            {
                iWindSpeed = (int) (iWindSpeed * 3.6);
                iGustSpeed = (int) (iGustSpeed * 3.6);
            }
            qsWindSpeed.setNum(iWindSpeed);
            qsWindSpeed += i18n(" KPH");
        }
        else
        {
            if (sWindUnit == "KT")
            {
                iWindSpeed = (int) (iWindSpeed * 2.24/1.94);
                iGustSpeed = (int) (iGustSpeed * 2.24/1.94);
            }
            else if (sWindUnit == "KMH")
            {
                iWindSpeed = (int) (iWindSpeed / 1.61);
                iGustSpeed = (int) (iGustSpeed / 1.61);
            }
            else if (sWindUnit == "MPS")
            {
                iWindSpeed = (int) (iWindSpeed * 2.24);
                iGustSpeed = (int) (iGustSpeed * 2.24);
            }
            qsWindSpeed.setNum(iWindSpeed);
            qsWindSpeed += i18n(" MPH");
        }

        if ((WindRegExp.cap(1) != "VRB") && (iWindSpeed > 0))
        {
            if ((iDirection >=0 && iDirection <= 22) || ( iDirection >= 338 && iDirection <= 360))
                qsWindDirection = i18n("N");
            else if (iDirection >= 23 && iDirection <= 67)
                qsWindDirection = i18n("NE");
            else if (iDirection >= 68 && iDirection <= 112)
                qsWindDirection = i18n("E");
            else if (iDirection >= 113 && iDirection <= 157)
                qsWindDirection = i18n("SE");
            else if (iDirection >= 158 && iDirection <= 202)
                qsWindDirection = i18n("S");
            else if (iDirection >= 203 && iDirection <= 247)
                qsWindDirection = i18n("SW");
            else if (iDirection >= 248 && iDirection <= 292)
                qsWindDirection = i18n("W");
            else if (iDirection >= 293 && iDirection <= 337)
                qsWindDirection = i18n("NW");
        }
    }
}

void weatherlib::clearData()
{
    clouds = 0;
    qsCoverList.clear();
    qsCurrentList.clear();
    qsWindDirection = "";
    qsTime = "";
    qsTemperature = "";
    theWeather = "";
    qsWindSpeed = "";
    qsDate = "";
    qsPressure = "";
    qsVisibility = "";
}

/** This is will tokenize the data and process it. */
void weatherlib::processData(QString newData, bool mode){
    clearData();
    MetricMode = mode;

    kdDebug() << "Processing data: " << newData << endl;

    // Split at whitespace into tokens
    QStringList dataList = QStringList::split(QRegExp("\\s+"), newData);
    bool found = false;

    for (QStringList::ConstIterator it = dataList.begin(); it != dataList.end(); ++it)
    {
         // Abort if ReMarK code
        if (*it == "RMK")
            break;

        // Don't try to parse the ICAO location code
        if ((!found) && (*it == reportLocation))
        {
            found = true;
            continue;
        }

        kdDebug() << "Processing Token: " << *it << endl;
        parseDate(*it);
        parseTime(*it);
        parseWindSpeed(*it);
        parseVisibility(*it);
        parseTemperature(*it);
        parsePressure(*it);
        parseCover(*it);
        parseCurrent(*it);
    }

    calcCurrentIcon();
}

/** Will return the current airpressure */
QString weatherlib::pressure(){
    return qsPressure;
}

/** Will return the wind data  as a QString */
QString weatherlib::wind(){
    return (qsWindSpeed + " " + qsWindDirection);
}

/**  */
QString weatherlib::temp(){
    return qsTemperature;
}

/** This will return the name of the icon suitable for current weather */
QString weatherlib::currentIcon(){
    return theWeather;
}

void weatherlib::calcCurrentIcon(){
    if (theWeather.isEmpty())
    {
        if (clouds == 0)
            theWeather = "sunny";
        else if ( clouds > 0 && clouds <= 2)
            theWeather = "cloudy1";
        else if ( clouds > 2 && clouds <= 4)
            theWeather = "cloudy2";
        else if ( clouds > 4 && clouds <= 8)
            theWeather = "cloudy3";
        else if ( clouds > 8 && clouds < 64)
            theWeather = "cloudy4";
        else
            theWeather = "cloudy5";
    }
    else if (theWeather == "tstorm")
    {
        if ( clouds >= 0 && clouds <= 10)
            theWeather = "tstorm1";
        else if ( clouds > 10 && clouds <= 20)
            theWeather = "tstorm2";
        else
            theWeather = "tstorm3";
    }
    else if (theWeather == "shower")
    {
        if ( clouds >= 0 && clouds <= 10)
            theWeather = "shower1";
        else if ( clouds > 10 && clouds <= 20)
            theWeather = "shower2";
        else
            theWeather = "shower3";
    }
    else if (theWeather == "snow")
    {
        if ( clouds >= 0 && clouds <= 8)
            theWeather = "snow1";
        else if ( clouds > 8 && clouds <= 16)
            theWeather = "snow2";
        else if ( clouds > 16 && clouds <= 24)
            theWeather = "snow3";
        else
            theWeather = "snow5";
    }

    kdDebug() << "Clouds: " << clouds << ", Icon: " << theWeather << endl;
}

/** Returns the date of the weather data */
QString weatherlib::date(int gmtOffset){
    // The Date should be in format YYYY/MM/DD
    // The Time should be in format HH:MM
    QString day, month, year, hour, minute;

    day = qsDate.mid(8,2);
    month = qsDate.mid(5,2);
    year = qsDate.mid(0,4);

    hour = qsTime.mid(0,2);
    minute = qsTime.mid(3,2);

    QDate theDate(year.toInt(), month.toInt(), day.toInt());
    QTime theTime(hour.toInt(), minute.toInt());
    QDateTime gmtDateTime(theDate, theTime);
    QDateTime localDateTime = gmtDateTime.addSecs(gmtOffset*3600);

    KLocale *loc = KGlobal::locale();
    return loc->formatDateTime(localDateTime, false, false);
}

/** This method would be removed after freeze */
QString weatherlib::weatherText(){
    QString theText = i18n("Currently there are ");
    //theText += qsCover;
    //if (qsCurrent != "")
    //    theText += " With " + qsCurrent;
    return theText;
}

/** Returns the current cover */
QStringList weatherlib::cover(){
    return qsCoverList;
}

/** return the visibility */
QString weatherlib::visibility(){
    return qsVisibility;
}

/** return the weather text */
QStringList weatherlib::weather(){
    return qsCurrentList;
}
