/***************************************************************************
 *   Copyright (C) 2010 by Volker Lanz <vl@fidra.de>                       *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
 ***************************************************************************/

#include "fileimport/fitnesslogimporter.h"

#include "core/trackpoint.h"
#include "core/activity.h"
#include "core/lap.h"
#include "core/track.h"

#include "util/xml.h"

#include <QString>
#include <QFile>
#include <QXmlStreamReader>
#include <QDateTime>
#include <QDebug>

FitnessLogImporter::FitnessLogImporter() :
	ImporterInterface("FitnessWorkbook")
{
}

static void parseTrack(QXmlStreamReader& xml, Laps& laps)
{
	bool defaultLap = laps.isEmpty();

	if (defaultLap)
		laps.append(new Lap());

	const QDateTime trackStart = QDateTime::fromString(xml.attributes().value("StartTime").toString(), Qt::ISODate);

	Laps::iterator currentLap = laps.begin();
	quint32 currentLapEnd = (*currentLap)->duration();
	Track* currentTrack = new Track();
	(*currentLap)->addTrack(currentTrack);

	while (xmlReadNextStartElement(xml))
	{
		if (xml.name() == "pt")
		{
			TrackPoint* tp = new TrackPoint();

			if (xml.attributes().hasAttribute("lat") && xml.attributes().hasAttribute("lon"))
				tp->setPosition(Position(xml.attributes().value("lat").toString().toDouble(), xml.attributes().value("lon").toString().toDouble()));

			if (xml.attributes().hasAttribute("hr"))
				tp->setHeartRate(xml.attributes().value("hr").toString().toUInt());

			if (xml.attributes().hasAttribute("ele"))
				tp->setAltitude(xml.attributes().value("ele").toString().toDouble() * 1000);

			if (xml.attributes().hasAttribute("cadence"))
				tp->setCadence(xml.attributes().value("cadence").toString().toUInt());

			quint32 tm = xml.attributes().value("tm").toString().toUInt();

			tp->setTimeStamp(trackStart.addSecs(tm));

			if (!defaultLap && tm > currentLapEnd)
			{
				currentLap++;

				if (currentLap == laps.end())
				{
					qDebug() << "trackpoint at tm " << tm << " does not belong to any lap.";
					currentLap--;
				}
				else
				{
					currentLapEnd += (*currentLap)->duration();
					currentTrack = new Track();
					(*currentLap)->addTrack(currentTrack);
				}
			}

			// ignore attribute: power

			currentTrack->addTrackPoint(tp);

			xmlSkipCurrentElement(xml);
		}
		else
		{
			qDebug() << "unknown tag in <Track>: " << xml.name();
			xmlSkipCurrentElement(xml);
		}
	}

	foreach(Lap* lap, laps)
	{
		quint32 avgHR = 0;
		quint32 maxHR = 0;
		lap->calcHeartRates(avgHR, maxHR);

		if (lap->averageHeartRate() == 0)
			lap->setAverageHeartRate(avgHR);

		lap->setMaximumHeartRate(maxHR);
		lap->setMaximumSpeed(Lap::calcMaxSpeed(*lap));
	}
}

static void parseLap(QXmlStreamReader& xml, Lap& lap)
{
	lap.setStartTime(QDateTime::fromString(xml.attributes().value("StartTime").toString(), Qt::ISODate));
	lap.setDuration(xml.attributes().value("DurationSeconds").toString().toDouble() * 1000);

	// ignore attribute: Notes
	// ignore attribute: Rest

	while (xmlReadNextStartElement(xml))
	{
		if (xml.name() == "Distance")
		{
			lap.setDistance(xml.attributes().value("TotalMeters").toString().toDouble() * 1000);
			xmlSkipCurrentElement(xml);
		}
		else if (xml.name() == "HeartRate")
		{
			lap.setAverageHeartRate(xml.attributes().value("AverageBPM").toString().toUInt());
			lap.setMaximumHeartRate(xml.attributes().value("MaximumBPM").toString().toUInt());
			xmlSkipCurrentElement(xml);
		}
		else if (xml.name() == "Cadence")
		{
			lap.setAverageCadence(xml.attributes().value("AverageRPM").toString().toUInt());
			xmlSkipCurrentElement(xml);
		}
		else if (xml.name() == "Calories")
		{
			lap.setEnergyConsumption(4.1858 * xml.attributes().value("TotalCal").toString().toUInt());
			xmlSkipCurrentElement(xml);
		}
		else if (xml.name() == "Power")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Elevation")
			xmlSkipCurrentElement(xml);
		else
		{
			qDebug() << "unknown tag in <Lap>: " << xml.name();
			xmlSkipCurrentElement(xml);
		}
	}
}

static void parseLaps(QXmlStreamReader& xml, Activity& activity)
{
	while (xmlReadNextStartElement(xml))
	{
		if (xml.name() == "Lap")
		{
			Lap* lap = new Lap();
			parseLap(xml, *lap);
			activity.addLap(lap);
		}
		else
		{
			qDebug() << "unknown tag in <Laps>: " << xml.name();
			xmlSkipCurrentElement(xml);
		}
	}
}

static void parseActivity(QXmlStreamReader& xml, Activities& activities)
{
	Activity* activity = new Activity();
	activity->setTimeStamp(QDateTime::fromString(xml.attributes().value("StartTime").toString(), Qt::ISODate));

	while (xmlReadNextStartElement(xml))
	{
		if (xml.name() == "Metadata")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Duration")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Distance")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Elevation")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "HeartRate")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Cadence")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Power")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Calories")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Notes")
			activity->setNotes(xml.readElementText());
		else if (xml.name() == "Name")
			activity->setName(xml.readElementText());
		else if (xml.name() == "Laps")
			parseLaps(xml, *activity);
		else if (xml.name() == "Intensity")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Weather")
		{
			const QString weather = xml.attributes().value("Conditions").toString();

			if (weather == "Clear")
				activity->setWeather(Weather::clear);
			else if (weather == "ScatterClouds" || weather == "PartClouds")
				activity->setWeather(Weather::cloudy);
			else if (weather == "Overcast" || weather == "MostClouds" || weather == "Clouds")
				activity->setWeather(Weather::overcast);
			else if (weather == "ChanceRain" || weather == "LightDrizzle" || weather == "LightRain")
				activity->setWeather(Weather::showers);
			else if(weather == "Rain" || weather == "HeavyRain")
				activity->setWeather(Weather::rain);
			else if (weather == "ChanceThunder" || weather == "Thunder")
				activity->setWeather(Weather::storm);
			else if (weather == "Snow")
				activity->setWeather(Weather::snow);
			else if (weather == "Haze")
				activity->setWeather(Weather::hazy);
			else
				qDebug() << "unknown weather string: " << weather;

			activity->setTemperature(xml.attributes().value("Temp").toString().toInt() * 10);
			activity->setWeatherDescription(xml.readElementText());
		}
		else if (xml.name() == "Category")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Location")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Route")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "EquipmentUsed")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Track")
			parseTrack(xml, activity->laps());
		else if (xml.name() == "TrackClock")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "DistanceMarkers")
			xmlSkipCurrentElement(xml);
		else
		{
			qDebug() << "unknown tag in <Activity>: " << xml.name();
			xmlSkipCurrentElement(xml);
		}
	}

	activities.append(activity);
}

void FitnessLogImporter::parseAthleteLog(QXmlStreamReader& xml, Activities& activities)
{
	int count = 0;

	while (xmlReadNextStartElement(xml))
	{
		if (xml.name() == "Athlete")
			xmlSkipCurrentElement(xml);
		else if (xml.name() == "Activity")
		{
			parseActivity(xml, activities);
			emit progress(++count);
		}
		else if (xml.name() == "History")
			xmlSkipCurrentElement(xml);
		else
		{
			qDebug() << "unknown tag in <AthleteLog>: " << xml.name();
			xmlSkipCurrentElement(xml);
		}
	}
}

void FitnessLogImporter::parseRootElement(QXmlStreamReader& xml, Activities& activities)
{
	while (xmlReadNextStartElement(xml))
	{
		if (xml.name() == "AthleteLog")
			parseAthleteLog(xml, activities);
		else
		{
			qDebug() << "unknown tag in <FitnessWorkbook>: " << xml.name();
			xmlSkipCurrentElement(xml);
		}
	}
}

bool FitnessLogImporter::canImport(const QString& rootName)
{
	return rootName == m_RootElementTagName;
}

QString FitnessLogImporter::fileFormat() const
{
	return tr("*.fitlog|SportTracks Fitness Log (*.fitlog)");
}
