#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "Kml.hpp"

//#define WITH_FANCY_CAMERA

Kml::Kml(const char *pDocRoot)
: m_pKml(nullptr)
, m_pTrackPlaceMark(nullptr)
, m_docRoot(pDocRoot)
, m_altitudeModeString("absolute")
, m_pathColorString("0xff00ffff")
, m_cameraAlt(1000)
, m_cameraTilt(0)
, m_cameraAutoHeading(false)
, m_cameraAutoRoll(false)
, m_cameraFlyToView(false)
{
	memset(m_path, 0, sizeof(m_path));
	m_pKml = new XmlElement("kml");

	// Create an empty kml
	m_pKml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
	m_pKml->setAttribute("xmlns:gx", "http://www.google.com/kml/ext/2.2");
	
}

Kml::~Kml(void)
{
	m_pKml = nullptr;
}

void Kml::updateFlightData(FlightData const &flightData)
{
	const ScopedLock sl (m_lock);
	m_flightData = flightData;
}

const char* Kml::getPath()
{
	return m_path;
}

void Kml::createNetworkLinkFile(String const &path, String const &hostIp)
{
	File file(path);
	
	if (file.exists())
	{
		return;
	}
	
	ScopedPointer<XmlElement> pKml = new XmlElement("kml");
	pKml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");

	XmlElement *pDoc = pKml->createNewChildElement("Document");
	pDoc->createNewChildElement("visibility")->addTextElement("1");
	
	XmlElement *pNetworkLink = pDoc->createNewChildElement("NetworkLink");
	pNetworkLink->setAttribute("id", "NetworkLink_0");
	pNetworkLink->createNewChildElement("name")->addTextElement("Link");


	XmlElement *pFlyToView = pNetworkLink->createNewChildElement("flyToView");
	if (m_cameraFlyToView)
	{
		pFlyToView->addTextElement("1");
	}
	else
	{
		pFlyToView->addTextElement("0");
	}

	XmlElement *pLink = pNetworkLink->createNewChildElement("Link");
	pLink->createNewChildElement("href")->addTextElement(String("http://") + hostIp + String(":5001/flight.kml"));
	pLink->createNewChildElement("refreshMode")->addTextElement("onInterval");
	pLink->createNewChildElement("refreshInterval")->addTextElement("1");
	pLink->createNewChildElement("viewRefreshMode")->addTextElement("onStop");
	pLink->createNewChildElement("viewRefreshInterval")->addTextElement("0");
	pLink->createNewChildElement("viewFormat")->addTextElement("BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth];CAMERA=[lookatLon],[lookatLat],[lookatRange],[lookatTilt],[lookatHeading];VIEW=[horizFov],[vertFov],[horizPixels],[vertPixels],[terrainEnabled]");
	
	pKml->writeToFile(file, StringRef(""));
	
}

void Kml::create()
{
	time_t time_departure;
	struct tm *time_now;

	XmlElement *pDoc = m_pKml->createNewChildElement("Document");

	time(&time_departure);
	time_now = localtime(&time_departure);
	char time_str[256];

	// Create an outer XML element..
	m_pKml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
	m_pKml->setAttribute("xmlns:gx", "http://www.google.com/kml/ext/2.2");
	sprintf(m_path, "%s\\flight_%4.4d%2.2d%2.2d_%2.2d%2.2d%2.2d.kml", m_docRoot, 1900+time_now->tm_year, time_now->tm_mon+1, time_now->tm_mday, time_now->tm_hour, time_now->tm_min, time_now->tm_sec);
	pDoc->createNewChildElement("filename")->addTextElement(m_path);
	
	sprintf(time_str, "Created on %4.4d-%2.2d-%2.2d at %2.2d:%2.2d:%2.2d", 1900+time_now->tm_year, time_now->tm_mon+1, time_now->tm_mday, time_now->tm_hour, time_now->tm_min, time_now->tm_sec);
	XmlElement *pSnippet = pDoc->createNewChildElement("Snippet");
	pSnippet->setAttribute("maxLines", "2");
	pSnippet->addTextElement(time_str);
	
	m_pTrackPlaceMark = createTrack();
			
}

void Kml::destroy()
{
	m_pKml->removeChildElement(m_pKml->getChildByName("Document"), true);
	m_pTrackPlaceMark = nullptr;
}

XmlElement* Kml::createNetworkLinkControl()
{
	XmlElement *pNetworkLinkControl = new XmlElement("NetworkLinkControl");
	pNetworkLinkControl->createNewChildElement("LinkName")->addTextElement("Link");
	
	XmlElement *pCamera = pNetworkLinkControl->createNewChildElement("Camera");

	pCamera->createNewChildElement("longitude")->addTextElement(String(m_flightData.lon));
	pCamera->createNewChildElement("latitude")->addTextElement(String(m_flightData.lat));
	pCamera->createNewChildElement("altitude")->addTextElement(String(m_flightData.alt_meter+m_cameraAlt));

	if (m_cameraAutoHeading)
	{
		pCamera->createNewChildElement("heading")->addTextElement(String(m_flightData.hdgTrue_deg));
	}
	else
	{
		pCamera->createNewChildElement("heading")->addTextElement("0");
	}

	pCamera->createNewChildElement("tilt")->addTextElement(String(90.0 - (m_flightData.pitch_deg + m_cameraTilt)));

	if (m_cameraAutoRoll)
	{
		pCamera->createNewChildElement("roll")->addTextElement(String(m_flightData.bank_deg));
	}
	else
	{
		pCamera->createNewChildElement("roll")->addTextElement("0");
	}

	pCamera->createNewChildElement("altitudeMode")->addTextElement(m_altitudeModeString);
	
#if 0	
	XmlElement *pUpdate = pNetworkLinkControl->createNewChildElement("Update");
	pUpdate->createNewChildElement("targetHref")->addTextElement("FsTrack.kml");
	XmlElement *pChange = pUpdate->createNewChildElement("Change");
	XmlElement *pNetworkLink = pChange->createNewChildElement("NetworkLink");
	pNetworkLink->setAttribute("targetId", "NetworkLink_0");
	XmlElement *pFlyToView = pNetworkLink->createNewChildElement("flyToView");

	if (m_cameraFlyToView)
	{
		pFlyToView->addTextElement("0");
	}
	else
	{
		pFlyToView->addTextElement("1");
	}
#endif
	return pNetworkLinkControl;
}

XmlElement* Kml::createPlane()
{
	const ScopedLock sl (m_lock);
	char temp_str[1024];

	XmlElement *pPlacemark;
	XmlElement *pName;
	XmlElement *pStyle;
	XmlElement *pIconStyle;
	XmlElement *pIcon;

	XmlElement *pHref;
	XmlElement *pHeading;
	XmlElement *pY, *pW, *pH;
	XmlElement *pDescr;
	XmlElement *pPoint;
	XmlElement *pExtrude;
	XmlElement *pTesselate;
	XmlElement *pAltitudeMode;
	XmlElement *pCoords;

	// Placemark
	pPlacemark = new XmlElement("Placemark");
	pPlacemark->setAttribute("id", "Plane_0");

	// Name
	sprintf(temp_str, "Unknown flight");
	if (strlen(m_flightData.atcFlightIDStr))
	{
		if (strlen(m_flightData.atcFlightNumStr) == 0)
		{
			sprintf(temp_str, "%s", m_flightData.atcFlightIDStr);
		}
		else
		{
			sprintf(temp_str, "%s/%s", m_flightData.atcFlightIDStr, m_flightData.atcFlightNumStr);
		}
	}
	pName = pPlacemark->createNewChildElement("name");
	pName->addTextElement(temp_str);

	// Description
	pDescr = pPlacemark->createNewChildElement("description");
	sprintf(temp_str, "Altitude: %d ft, Heading: %d\nGround speed: %d kts", (int)(m_flightData.alt_feet+0.5), (int)(m_flightData.hdgTrue_deg+0.5), (int)(m_flightData.gs_knots+0.5));
	pDescr->addTextElement(temp_str);

	// Style
	pStyle = pPlacemark->createNewChildElement("Style");

	// IconStyle
	pIconStyle = pStyle->createNewChildElement("IconStyle");
	pHeading = pIconStyle->createNewChildElement("heading");
	pHeading->addTextElement(String(m_flightData.hdgTrue_deg));

	// Icon
	pIcon = pIconStyle->createNewChildElement("Icon");
	pHref = pIcon->createNewChildElement("href");
	pHref->addTextElement("root://icons/palette-2.png");
	pY = pIcon->createNewChildElement("y");
	pY->addTextElement("0");
	pW = pIcon->createNewChildElement("w");
	pW->addTextElement("32");
	pH = pIcon->createNewChildElement("h");
	pH->addTextElement("32");

	// Point
	pPoint = pPlacemark->createNewChildElement("Point");
	pPoint->setAttribute("id", "Pointy");
	pExtrude = pPoint->createNewChildElement("extrude");
	pExtrude->addTextElement("1");
	pTesselate = pPoint->createNewChildElement("tesselate");
	pTesselate->addTextElement("1");
	pAltitudeMode = pPoint->createNewChildElement("altitudeMode");
	pAltitudeMode->addTextElement(m_altitudeModeString);
	pCoords = pPoint->createNewChildElement("coordinates");
	sprintf(temp_str, "%9.6f,%9.6f,%9.6f", m_flightData.lon,  m_flightData.lat,  m_flightData.alt_meter);
	pCoords->addTextElement(temp_str);

	return pPlacemark;
}

XmlElement* Kml::createTrack()
{
	const ScopedLock sl (m_lock);

	char temp_str[1024];

	XmlElement *pPlacemark;
	XmlElement *pName;
	XmlElement *pStyle;
	XmlElement *pLineStyle;
	XmlElement *pLineColor;
	XmlElement *pLineWidth;
	XmlElement *pPolyStyle;
	XmlElement *pPolyColor;

	XmlElement *pLineString;
	XmlElement *pExtrude;
	XmlElement *pTesselate;
	XmlElement *pAltitudeMode;
	XmlElement *pCoords;

	// Placemark
	pPlacemark = new XmlElement("Placemark");
	pPlacemark->setAttribute("id", "Track_0");

	// Name
	pName = pPlacemark->createNewChildElement("name");
	pName->addTextElement("Track");

	// Style
	pStyle = pPlacemark->createNewChildElement("Style");

	// LineStyle
	pLineStyle = pStyle->createNewChildElement("LineStyle");
	pLineColor = pLineStyle->createNewChildElement("color");
	pLineColor->addTextElement(m_pathColorString);

	pLineWidth = pLineStyle->createNewChildElement("width");
	pLineWidth->addTextElement("4");

	// PolyStyle
	pPolyStyle = pStyle->createNewChildElement("PolyStyle");
	pPolyColor = pPolyStyle->createNewChildElement("color");
	sprintf(temp_str, "%4.4X", 0x7f00ff00);
	pPolyColor->addTextElement(temp_str);

	// LineString
	pLineString = pPlacemark->createNewChildElement("LineString");
	pExtrude = pLineString->createNewChildElement("extrude");
	pExtrude->addTextElement("1");
	pTesselate = pLineString->createNewChildElement("tesselate");
	pTesselate->addTextElement("1");
	pAltitudeMode = pLineString->createNewChildElement("altitudeMode");
	pAltitudeMode->addTextElement(m_altitudeModeString);

	sprintf(temp_str, "\n%9.6f,%9.6f,%9.6f", m_flightData.lon,  m_flightData.lat,  m_flightData.alt_meter);
	pCoords = pLineString->createNewChildElement("coordinates");
	pCoords = pPlacemark->getChildByName("LineString")->getChildByName("coordinates");
	pCoords->addTextElement(temp_str);

	return pPlacemark;
}

void Kml::updateTrack()
{
	const ScopedLock sl (m_lock);
	char temp_str[1024];

	if (!m_pTrackPlaceMark)
	{
		return;
	}
	// Coordinates
	sprintf(temp_str, "\n%9.6f,%9.6f,%9.6f", m_flightData.lon,  m_flightData.lat,  m_flightData.alt_meter);
	XmlElement *pCoords = m_pTrackPlaceMark->getChildByName("LineString")->getChildByName("coordinates");
	pCoords->addTextElement(temp_str);
}

void Kml::setAltitudeMode(String const &modeString)
{
	m_altitudeModeString = modeString;
}

void Kml::setPathColor(String const &colorString)
{
	const ScopedLock sl (m_lock);

	m_pathColorString = colorString;

	if (m_pTrackPlaceMark)
	{
		m_pTrackPlaceMark->getChildByName("Style")->getChildByName("LineStyle")->getChildByName("color")->getFirstChildElement()->setText(colorString);
	}

}

void Kml::setCameraAltitude(float alt_meters)
{
	m_cameraAlt = alt_meters;
}

void Kml::setCameraTilt(float tilt_deg)
{
	m_cameraTilt = tilt_deg;
}

void Kml::setCameraFlyToView(bool enable)
{
	m_cameraFlyToView = enable;
}

void Kml::setCameraAutoHeading(bool enable)
{
	m_cameraAutoHeading = enable;
}

void Kml::setCameraAutoRoll(bool enable)
{
	m_cameraAutoRoll = enable;
}

void Kml::exportKml(const char *pPath)
{
	const ScopedLock sl (m_lock);

	if (pPath && m_pKml)
	{
		XmlElement *pDoc;
		File file(pPath);
		ScopedPointer<XmlElement> pPlane = createPlane();
		ScopedPointer<XmlElement> pNetworkLinkcontrol = createNetworkLinkControl();
		m_pKml->addChildElement(pNetworkLinkcontrol);
		
		pDoc = m_pKml->getChildByName("Document");
		if (pDoc)
		{
			pDoc->addChildElement(pPlane);
			pDoc->addChildElement(m_pTrackPlaceMark);
		}
		
		m_pKml->writeToFile(file, StringRef(""));
		m_pKml->removeChildElement(pNetworkLinkcontrol, false);

		if (pDoc)
		{
			pDoc->removeChildElement(pPlane, false);
			pDoc->removeChildElement(m_pTrackPlaceMark, false);
		}
	}
}

void Kml::exportKmz(const char *pPath)
{
	// ToDo: add Compression if available in Juce
	const ScopedLock sl (m_lock);

	if (pPath && m_pKml)
	{
		File file(pPath);

		file.deleteFile();
		ScopedPointer<OutputStream> pStream = file.createOutputStream();

		m_pKml->writeToStream(*pStream, StringRef(""));
		pStream = nullptr;
	}
}

void Kml::onUpdate(const char *pPath, ViewFormat const &viewFormat)
{
	// LookAt
//	XmlElement *pCamera;

//	pCamera = m_pKml->getChildByName("NetworkLinkControl")->getChildByName("Camera");

	if (viewFormat.has_camera)
	{
//		pCamera->getChildByName("longitude")->getFirstChildElement()->setText(String(viewFormat.camera.lookatLon));
//		pCamera->getChildByName("latitude")->getFirstChildElement()->setText(String(viewFormat.camera.lookatLat));
//		pCamera->getChildByName("altitude")->getFirstChildElement()->setText(String(viewFormat.camera.lookatRange));
//		pCamera->getChildByName("heading")->getFirstChildElement()->setText(String(viewFormat.camera.lookatHeading));
//		pCamera->getChildByName("heading")->getFirstChildElement()->setText(String(0));
//		pCamera->getChildByName("tilt")->getFirstChildElement()->setText(String(viewFormat.camera.lookatTilt));
//		pCamera->getChildByName("tilt")->getFirstChildElement()->setText(String(0));
//		pCamera->getChildByName("range")->getFirstChildElement()->setText(String(viewFormat.camera.lookatRange));
//		pCamera->getChildByName("range")->getFirstChildElement()->setText(String("10000"));
//		pCamera->getChildByName("altitudeMode")->getFirstChildElement()->setText(m_altitudeModeString);
	}

	exportKml(pPath);
}

