/*
 * Copyright (c) 2011 Paulo Zanoni
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <cstdlib>
#include <cstring>
#include <iostream>

#include "XDGMenu.h"

#include "XDGUtils.h"

static const char *mainCategories[] = {
    "AudioVideo",
    "Audio",
    "Video",
    "Development",
    "Education",
    "Game",
    "Graphics",
    "Network",
    "Office",
    "Settings",
    "System",
    "Utility",
    NULL
};

static const char *additionalCategories[] = {
    "Building",
    "Debugger",
    "IDE",
    "GUIDesigner",
    "Profiling",
    "RevisionControl",
    "Translation",
    "Calendar",
    "ContactManagement",
    "Database",
    "Dictionary",
    "Chart",
    "Email",
    "Finance",
    "FlowChart",
    "PDA",
    "ProjectManagement",
    "Presentation",
    "Spreadsheet",
    "WordProcessor",
    "2DGraphics",
    "VectorGraphics",
    "RasterGraphics",
    "3DGraphics",
    "Scanning",
    "OCR",
    "Photography",
    "Publishing",
    "Viewer",
    "TextTools",
    "DesktopSettings",
    "HardwareSettings",
    "Printing",
    "PackageManager",
    "Dialup",
    "InstantMessaging",
    "Chat",
    "IRCClient",
    "FileTransfer",
    "HamRadio",
    "News",
    "P2P",
    "RemoteAccess",
    "Telephony",
    "TelephonyTools",
    "VideoConference",
    "WebBrowser",
    "WebDevelopment",
    "Midi",
    "Mixer",
    "Sequencer",
    "Tuner",
    "TV",
    "AudioVideoEditing",
    "Player",
    "Recorder",
    "DiscBurning",
    "ActionGame",
    "AdventureGame",
    "ArcadeGame",
    "BoardGame",
    "BlocksGame",
    "CardGame",
    "KidsGame",
    "LogicGame",
    "RolePlaying",
    "Simulation",
    "SportsGame",
    "StrategyGame",
    "Art",
    "Construction",
    "Music",
    "Languages",
    "Science",
    "ArtificialIntelligence",
    "Astronomy",
    "Biology",
    "Chemistry",
    "ComputerScience",
    "DataVisualization",
    "Economy",
    "Electricity",
    "Geography",
    "Geology",
    "Geoscience",
    "History",
    "ImageProcessing",
    "Literature",
    "Math",
    "NumericalAnalysis",
    "MedicalSoftware",
    "Physics",
    "Robotics",
    "Sports",
    "ParallelComputing",
    "Amusement",
    "Archiving",
    "Compression",
    "Electronics",
    "Emulator",
    "Engineering",
    "FileTools",
    "FileManager",
    "TerminalEmulator",
    "Filesystem",
    "Monitor",
    "Security",
    "Accessibility",
    "Calculator",
    "Clock",
    "TextEditor",
    "Documentation",
    "Core",
    "KDE",
    "GNOME",
    "GTK",
    "Qt",
    "Motif",
    "Java",
    "ConsoleOnly",
    NULL
};

static const char *reservedCategories[] = {
    "Screensaver",
    "TrayIcon",
    "Applet",
    "Shell",
    NULL
};

static const char *deprecatedCategories[] = {
    "Application",
    "Applications",
    NULL
};

static const char *environments[] = {
    "GNOME",
    "KDE",
    "LXDE",
    "ROX",
    "XFCE",
    "Old",
    NULL
};

bool XDGMenu::validCategories(const char *categoriesString)
{
    char *s, *tokPtr, *tokStr;
    bool mainFound = false;
    int i;

    // Allow empty categories for now
    if (!categoriesString)
	return true;

    if (!XDGUtils::validSemicolonList(categoriesString))
	return false;

    s = strdup(categoriesString);

    ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
	if (!validCategory(tokStr)) {
	    free(s);
	    return false;
	}

	if (!mainFound) {
	    for(i = 0; mainCategories[i] != NULL; i++) {
		if (strcmp(tokStr, mainCategories[i]) == 0)
		    mainFound = true;
	    }
	}
    }

    // "Audio" or "Video" entries require "AudioVideo"
    char *s2, *tokPtr2, *tokStr2;
    s2 = strdup(categoriesString);
    ITERATE_ESCAPED_STRTOK_R(s, ":", &tokPtr, tokStr) {
	if ((strcmp(tokStr, "Audio") == 0) || (strcmp(tokStr, "Video") == 0)) {
	    ITERATE_ESCAPED_STRTOK_R(s2, ":", &tokPtr2, tokStr2) {
		if (strcmp(tokStr2, "AudioVideo") == 0)
		    goto done;
	    }
	    free(s2);
	    free(s);
	    std::cerr << "Audio or Video found, but no AudioVideo\n";
	    return false;
	}
    }

done:
    if (!mainFound)
	std::cerr << "Main category missing!\n";

    free(s);
    free(s2);
    return mainFound;
    // XXX: the spec suggests that some of the additional categories should only
    // be used with specific main categories: we should check for this!
    //
    // XXX: reserved categories can only be present if we have a OnlyShowIn=
    // entry in the desktop file!
}


bool XDGMenu::validCategory(const char *category)
{
    int i;

    for(i = 0; mainCategories[i] != NULL; i++)
	if (strcmp(category, mainCategories[i]) == 0)
	    return true;

    for(i = 0; additionalCategories[i] !=  NULL; i++)
	if (strcmp(category, additionalCategories[i]) == 0)
	    return true;

    for(i = 0; reservedCategories[i] !=  NULL; i++)
	if (strcmp(category, reservedCategories[i]) == 0) {
	    std::cout << "Warning: reserved category \""
		      << reservedCategories[i] << "\"\n";
	    return true;
	}
    for(i = 0; deprecatedCategories[i] != NULL; i++)
	if (strcmp(category, deprecatedCategories[i]) == 0) {
	    std::cout << "Warning: deprecated category \""
		      << deprecatedCategories[i] << "\"\n";
	    return true;
	}
    if (category[0] == 'X' && category[1] == '-')
	return true;

    std::cerr << "Invalid category: " << category << "\n";
    return false;
}

bool XDGMenu::validEnvironments(const char *environmentsString)
{
    char *s, *tokPtr, *tokStr;

    if (!XDGUtils::validSemicolonList(environmentsString))
	return false;

    s = strdup(environmentsString);

    ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
	if (!validEnvironment(tokStr)) {
	    free(s);
	    return false;
	}
    }

    free(s);
    return true;
}

bool XDGMenu::validEnvironment(const char *environment)
{
    int i;
    for(i = 0; environments[i] != NULL; i++)
	if (strcmp(environment, environments[i]) == 0)
	    return true;

    return false;
}
