#include "FileZilla.h"
#include "import.h"
#include "xmlfunctions.h"
#include "ipcmutex.h"
#include "Options.h"
#include "queue.h"

CImportDialog::CImportDialog(wxWindow* parent, CQueueView* pQueueView)
	: m_parent(parent), m_pQueueView(pQueueView)
{
}

void CImportDialog::Show()
{
	wxFileDialog dlg(m_parent, _("Select file to import settings from"), _T(""), 
					_T("FileZilla.xml"), _T("XML files (*.xml)|*.xml"), 
					wxFD_OPEN | wxFD_FILE_MUST_EXIST);

	if (dlg.ShowModal() != wxID_OK)
		return;

	TiXmlDocument xmlDocument;
	xmlDocument.SetCondenseWhiteSpace(false);
	if (!xmlDocument.LoadFile(dlg.GetPath().mb_str()))
	{
		wxMessageBox(_("Cannot load file, not a valid XML file."), _("Error importing"), wxICON_ERROR, m_parent);
		return;
	}

	TiXmlElement* fz3Root = xmlDocument.FirstChildElement("FileZilla3");
	TiXmlElement* fz2Root = xmlDocument.FirstChildElement("FileZilla");

	if (fz3Root)
	{
		bool settings = fz3Root->FirstChildElement("Settings") != 0;
		bool queue = fz3Root->FirstChildElement("Queue") != 0;
		bool sites = fz3Root->FirstChildElement("Servers") != 0;

		if (settings || queue || sites)
		{
			Load(m_parent, _T("ID_IMPORT"));
			if (!queue)
				XRCCTRL(*this, "ID_QUEUE", wxCheckBox)->Hide();
			if (!sites)
				XRCCTRL(*this, "ID_SITEMANAGER", wxCheckBox)->Hide();
			if (!settings)
				XRCCTRL(*this, "ID_SETTINGS", wxCheckBox)->Hide();
			Fit();

			if (ShowModal() != wxID_OK)
				return;

			if (queue && XRCCTRL(*this, "ID_QUEUE", wxCheckBox)->IsChecked())
			{
				m_pQueueView->ImportQueue(fz3Root->FirstChildElement("Queue"), true);
			}

			if (sites && XRCCTRL(*this, "ID_SITEMANAGER", wxCheckBox)->IsChecked())
			{
				ImportSites(fz3Root->FirstChildElement("Servers"));
			}

			if (settings && XRCCTRL(*this, "ID_SETTINGS", wxCheckBox)->IsChecked())
			{
				COptions::Get()->Import(fz3Root->FirstChildElement("Settings"));
				wxMessageBox(_("The settings have been imported. You have to restart FileZilla for all settings to have effect."), _("Import successful"), wxOK, this);
			}
			
			wxMessageBox(_("The selected categories have been imported."), _("Import successful"), wxOK, this);

			return;
		}
	}
	else if (fz2Root)
	{
		bool sites_fz2 = fz2Root->FirstChildElement("Sites") != 0;
		if (sites_fz2)
		{
			int res = wxMessageBox(_("The file you have selected contains site manager data from a previous version of FileZilla.\nDue to differences in the storage format, only host, port, username and password will be imported.\nContinue with the import?"),
				_("Import data from older version"), wxICON_QUESTION | wxYES_NO);
			
			if (res == wxYES)
				ImportLegacySites(fz2Root->FirstChildElement("Sites"));

			return;
		}
	}

	wxMessageBox(_("File does not contain any importable data."), _("Error importing"), wxICON_ERROR, m_parent);
}

bool CImportDialog::ImportLegacySites(TiXmlElement* pSites)
{
	CInterProcessMutex mutex(MUTEX_SITEMANAGER);

	CXmlFile file(_T("sitemanager"));
	TiXmlElement* pDocument = file.Load();
	if (!pDocument)
	{
		wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nAny changes made in the Site Manager will not be saved."), file.GetFileName().GetFullPath().c_str());
		wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR);

		return false;
	}

	TiXmlElement* pCurrentSites = pDocument->FirstChildElement("Servers");
	if (!pCurrentSites)
		pCurrentSites = pDocument->InsertEndChild(TiXmlElement("Servers"))->ToElement();

	if (!ImportLegacySites(pSites, pCurrentSites))
		return false;

	return file.Save();
}

wxString CImportDialog::DecodeLegacyPassword(wxString pass)
{
	wxString output;
	const char* key = "FILEZILLA1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";

	int pos = (pass.Length() / 3) % strlen(key);
	for (unsigned int i = 0; i < pass.Length(); i += 3)
	{
		if (pass[i] < '0' || pass[i] > '9' ||
			pass[i + 1] < '0' || pass[i + 1] > '9' ||
			pass[i + 2] < '0' || pass[i + 2] > '9')
			return _T("");
		int number = (pass[i] - '0') * 100 +
						(pass[i + 1] - '0') * 10 +
						pass[i + 2] - '0';
		wxChar c = number ^ key[(i / 3 + pos) % strlen(key)];
		output += c;
	}

	return output;
}

bool CImportDialog::ImportLegacySites(TiXmlElement* pSitesToImport, TiXmlElement* pExistingSites)
{
	for (TiXmlElement* pImportFolder = pSitesToImport->FirstChildElement("Folder"); pImportFolder; pImportFolder = pImportFolder->NextSiblingElement("Folder"))
	{
		wxString name = GetTextAttribute(pImportFolder, "Name");
		if (name == _T(""))
			continue;

		wxString newName = name;
		int i = 2;
		TiXmlElement* pFolder;
		while (!(pFolder = GetFolderWithName(pExistingSites, newName)))
		{
			newName = wxString::Format(_T("%s %d"), name.c_str(), i++);
		}
		
		ImportLegacySites(pImportFolder, pFolder);
	}

	for (TiXmlElement* pImportSite = pSitesToImport->FirstChildElement("Site"); pImportSite; pImportSite = pImportSite->NextSiblingElement("Site"))
	{
		wxString name = GetTextAttribute(pImportSite, "Name");
		if (name == _T(""))
			continue;

		wxString host = GetTextAttribute(pImportSite, "Host");
		if (host == _T(""))
			continue;

		int port = GetAttributeInt(pImportSite, "Port");
		if (port < 1 || port > 65535)
			continue;

		int serverType = GetAttributeInt(pImportSite, "ServerType");
		if (serverType < 0 || serverType > 4)
			continue;

		int protocol;
		switch (serverType)
		{
		default:
		case 0:
			protocol = 0;
			break;
		case 1:
			protocol = 3;
			break;
		case 2:
		case 4:
			protocol = 4;
			break;
		case 3:
			protocol = 1;
			break;
		}

		bool dontSavePass = GetAttributeInt(pImportSite, "DontSavePass") == 1;

		int logontype = GetAttributeInt(pImportSite, "Logontype");
		if (logontype < 0 || logontype > 2)
			continue;
		if (logontype == 2)
			logontype = 4;
		if (logontype == 1 && dontSavePass)
			logontype = 2;

		wxString user = GetTextAttribute(pImportSite, "User");
		wxString pass = DecodeLegacyPassword(GetTextAttribute(pImportSite, "Pass"));
		wxString account = GetTextAttribute(pImportSite, "Account");
		if (logontype && user == _T(""))
			continue;

		// Find free name
		wxString newName = name;
		int i = 2;
		while (HasEntryWithName(pExistingSites, newName))
		{
			newName = wxString::Format(_T("%s %d"), name.c_str(), i++);
		}

		TiXmlElement* pServer = pExistingSites->InsertEndChild(TiXmlElement("Server"))->ToElement();
		AddTextElement(pServer, newName);

		AddTextElement(pServer, "Host", host);
		AddTextElement(pServer, "Port", port);
		AddTextElement(pServer, "Protocol", protocol);
		AddTextElement(pServer, "Logontype", logontype);
		AddTextElement(pServer, "User", user);
		AddTextElement(pServer, "Pass", pass);
		AddTextElement(pServer, "Account", account);
	}

	return true;
}

bool CImportDialog::HasEntryWithName(TiXmlElement* pElement, const wxString& name)
{
	TiXmlElement* pChild;
	for (pChild = pElement->FirstChildElement("Server"); pChild; pChild = pChild->NextSiblingElement("Server"))
	{
		wxString childName = GetTextElement(pChild);
		if (!name.CmpNoCase(childName))
			return true;
	}
	for (pChild = pElement->FirstChildElement("Folder"); pChild; pChild = pChild->NextSiblingElement("Folder"))
	{
		wxString childName = GetTextElement(pChild);
		if (!name.CmpNoCase(childName))
			return true;
	}

	return false;
}

TiXmlElement* CImportDialog::GetFolderWithName(TiXmlElement* pElement, const wxString& name)
{
	TiXmlElement* pChild;
	for (pChild = pElement->FirstChildElement("Server"); pChild; pChild = pChild->NextSiblingElement("Server"))
	{
		wxString childName = GetTextElement(pChild);
		if (!name.CmpNoCase(childName))
			return 0;
	}

	for (pChild = pElement->FirstChildElement("Folder"); pChild; pChild = pChild->NextSiblingElement("Folder"))
	{
		wxString childName = GetTextElement(pChild);
		if (!name.CmpNoCase(childName))
			return pChild;
	}

	pChild = pElement->InsertEndChild(TiXmlElement("Folder"))->ToElement();
	AddTextElement(pChild, name);

	return pChild;
}

bool CImportDialog::ImportSites(TiXmlElement* pSites)
{
	CInterProcessMutex mutex(MUTEX_SITEMANAGER);

	CXmlFile file(_T("sitemanager"));
	TiXmlElement* pDocument = file.Load();
	if (!pDocument)
	{
		wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nAny changes made in the Site Manager will not be saved."), file.GetFileName().GetFullPath().c_str());
		wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR);

		return false;
	}

	TiXmlElement* pCurrentSites = pDocument->FirstChildElement("Servers");
	if (!pCurrentSites)
		pCurrentSites = pDocument->InsertEndChild(TiXmlElement("Servers"))->ToElement();

	if (!ImportSites(pSites, pCurrentSites))
		return false;

	return file.Save();
}

bool CImportDialog::ImportSites(TiXmlElement* pSitesToImport, TiXmlElement* pExistingSites)
{
	for (TiXmlElement* pImportFolder = pSitesToImport->FirstChildElement("Folder"); pImportFolder; pImportFolder = pImportFolder->NextSiblingElement("Folder"))
	{
		wxString name = GetTextElement(pImportFolder);
		if (name == _T(""))
			continue;

		wxString newName = name;
		int i = 2;
		TiXmlElement* pFolder;
		while (!(pFolder = GetFolderWithName(pExistingSites, newName)))
		{
			newName = wxString::Format(_T("%s %d"), name.c_str(), i++);
		}
		
		ImportSites(pImportFolder, pFolder);
	}

	for (TiXmlElement* pImportSite = pSitesToImport->FirstChildElement("Server"); pImportSite; pImportSite = pImportSite->NextSiblingElement("Server"))
	{
		wxString name = GetTextElement(pImportSite);
		if (name == _T(""))
			continue;

		// Find free name
		wxString newName = name;
		int i = 2;
		while (HasEntryWithName(pExistingSites, newName))
		{
			newName = wxString::Format(_T("%s %d"), name.c_str(), i++);
		}

		TiXmlElement* pServer = pExistingSites->InsertEndChild(*pImportSite)->ToElement();
		AddTextElement(pServer, newName);
	}

	return true;
}


syntax highlighted by Code2HTML, v. 0.9.1