/*
 *  The Mana Client
 *  Copyright (C) 2004-2009  The Mana World Development Team
 *  Copyright (C) 2009-2010  The Mana Developers
 *
 *  This file is part of The Mana Client.
 *
 *  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
 *  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, see <http://www.gnu.org/licenses/>.
 */

#include "gui/popupmenu.h"

#include "being.h"
#include "beingmanager.h"
#include "flooritem.h"
#include "graphics.h"
#include "item.h"
#include "localplayer.h"
#include "log.h"
#include "npc.h"
#include "party.h"
#include "playerrelations.h"

#include "gui/buy.h"
#include "gui/chat.h"
#include "gui/inventorywindow.h"
#include "gui/itemamount.h"
#include "gui/sell.h"
#include "gui/socialwindow.h"
#include "gui/storagewindow.h"

#include "gui/widgets/browserbox.h"
#include "gui/widgets/chattab.h"
#include "gui/widgets/whispertab.h"

#include "net/adminhandler.h"
#include "net/inventoryhandler.h"
#include "net/net.h"
#include "net/npchandler.h"
#include "net/partyhandler.h"
#include "net/tradehandler.h"

//include "net/ea/gui/partytab.h"

#include "resources/itemdb.h"
#include "resources/iteminfo.h"

#include "utils/gettext.h"
#include "utils/stringutils.h"

#include <cassert>

std::string tradePartnerName("");

PopupMenu::PopupMenu():
    Popup("PopupMenu"),
    mBeingId(0),
    mFloorItem(NULL),
    mItem(NULL),
    mTab(NULL)
{
    mBrowserBox = new BrowserBox;
    mBrowserBox->setPosition(4, 4);
    mBrowserBox->setHighlightMode(BrowserBox::BACKGROUND);
    mBrowserBox->setOpaque(false);
    mBrowserBox->setLinkHandler(this);
    add(mBrowserBox);
}

void PopupMenu::showPopup(int x, int y, Being *being)
{
    if (!being)
        return;

    mBeingId = being->getId();
    mBrowserBox->clearRows();

    const std::string &name = being->getName();

    switch (being->getType())
    {
        case Being::PLAYER:
            {
                mBrowserBox->addRow(strprintf(_("@@whisper|Whisper %s@@"), name.c_str()));

                // Players can be traded with.
                mBrowserBox->addRow(strprintf("@@trade|%s@@",
                                              strprintf(_("Trade with %s..."),
                                                        name.c_str()).c_str()));
                // TRANSLATORS: Attacking a player.
                mBrowserBox->addRow(strprintf("@@attack|%s@@",
                                              strprintf(_("Attack %s"),
                                                        name.c_str()).c_str()));
                // TRANSLATORS: Whispering a player.
                mBrowserBox->addRow(strprintf("@@whisper|%s@@",
                                              strprintf(_("Whisper %s"),
                                                        name.c_str()).c_str()));

                mBrowserBox->addRow("##3---");

                mBrowserBox->addRow(strprintf(_("@@heal|Heal %s@@"),
                                              name.c_str()));
                mBrowserBox->addRow("##3---");

                switch (player_relations.getRelation(name))
                {
                    case PlayerRelation::NEUTRAL:
                        mBrowserBox->addRow(strprintf("@@friend|%s@@",
                                                      strprintf(_("Befriend %s"),
                                                                name.c_str()).c_str()));

                    case PlayerRelation::FRIEND:
                        mBrowserBox->addRow(strprintf("@@disregard|%s@@",
                                                      strprintf(_("Disregard %s"),
                                                                name.c_str()).c_str()));
                        mBrowserBox->addRow(strprintf("@@ignore|%s@@",
                                                      strprintf(_("Ignore %s"),
                                                                name.c_str()).c_str()));
                        break;

                    case PlayerRelation::DISREGARDED:
                        mBrowserBox->addRow(strprintf("@@unignore|%s@@",
                                                      strprintf(_("Unignore %s"),
                                                                name.c_str()).c_str()));
                        mBrowserBox->addRow(strprintf("@@ignore|%s@@",
                                                      strprintf(_("Completely ignore %s"),
                                                                name.c_str()).c_str()));
                        break;

                    case PlayerRelation::IGNORED:
                        mBrowserBox->addRow(strprintf("@@unignore|%s@@",
                                                      strprintf(_("Unignore %s"),
                                                                name.c_str()).c_str()));
                        break;

                    default:
                        break;
                }

                mBrowserBox->addRow(strprintf("@@follow|%s@@",
                                              strprintf(_("Follow %s"),
                                                        name.c_str()).c_str()));
                mBrowserBox->addRow(strprintf("@@imitation|%s@@",
                                              strprintf(_("Imitation %s"),
                                                        name.c_str()).c_str()));
//                mBrowserBox->addRow(strprintf("@@guild|%s@@",
//                                strprintf(_("Invite %s to join your guild"),
//                                                    name.c_str()).c_str()));

                if (player_node->isInParty())
                {
                    mBrowserBox->addRow(strprintf("@@party|%s@@",
                                                  strprintf(_("Invite %s to join your party"),
                                                            name.c_str()).c_str()));
                    mBrowserBox->addRow(strprintf("@@kick party|%s@@",
                                                  _("Kick from party")));
                }

                if (player_node->isGM())
                {
                    mBrowserBox->addRow("##3---");
                    mBrowserBox->addRow(strprintf("@@admin-kick|%s@@",
                                                  _("Kick player")));
                }
                mBrowserBox->addRow("@@nuke|Nuke " + name + "@@");
            }
            break;

        case Being::NPC:
            // NPCs can be talked to (single option, candidate for removal
            // unless more options would be added)
            mBrowserBox->addRow(strprintf("@@talk|%s@@",
                                              strprintf(_("Talk to %s"),
                                                    name.c_str()).c_str()));

            mBrowserBox->addRow(strprintf("@@buy|%s@@", _("Buy")));
            mBrowserBox->addRow(strprintf("@@sell|%s@@", _("Sell")));
            break;

        case Being::MONSTER:
            {
                // Monsters can be attacked
                mBrowserBox->addRow(strprintf("@@attack|%s@@",
                                              strprintf(_("Attack %s"),
                                                    name.c_str()).c_str()));

                if (player_node->isGM())
                    mBrowserBox->addRow(strprintf("@@admin-kick|%s@@",
                                                  _("Kick monster")));
            }
            break;

        default:
            /* Other beings aren't interesting... */
            return;
    }
    mBrowserBox->addRow(strprintf("@@name|%s@@", _("Add name to chat")));

    //mBrowserBox->addRow(strprintf("@@look|%s@@", _("Look To")));
    mBrowserBox->addRow("##3---");
    mBrowserBox->addRow(strprintf("@@cancel|%s@@", _("Cancel")));

    showPopup(x, y);
}

void PopupMenu::showPopup(int x, int y, FloorItem *floorItem)
{
    if (!floorItem)
        return;

    mFloorItem = floorItem;
    mItem = floorItem->getItem();
    mBrowserBox->clearRows();

    // Floor item can be picked up (single option, candidate for removal)
    std::string name = ItemDB::get(mFloorItem->getItemId()).getName();
    mBrowserBox->addRow(strprintf("@@pickup|%s@@", strprintf(_("Pick up %s"),
                                                    name.c_str()).c_str()));
    mBrowserBox->addRow(strprintf("@@chat|%s@@", _("Add to chat")));

    //mBrowserBox->addRow(strprintf("@@look|%s@@", _("Look To")));
    mBrowserBox->addRow("##3---");
    mBrowserBox->addRow(strprintf("@@cancel|%s@@", _("Cancel")));

    showPopup(x, y);
}

void PopupMenu::showChatPopup(int x, int y, ChatTab *tab)
{
    if (!tab)
        return;

    mTab = tab;

    mBrowserBox->clearRows();

    if (tab->getType() == ChatTab::TAB_WHISPER)
    {
        mBrowserBox->addRow(strprintf("@@chat close|%s %s@@", _("Close"), tab->getCaption().c_str()));
    }
    if (tab->getType() == ChatTab::TAB_PARTY)
    {
        mBrowserBox->addRow(strprintf("@@leave party|%s@@", _("Leave")));
    }
    mBrowserBox->addRow(strprintf("@@chat clear|%s@@", _("Clear")));
    mBrowserBox->addRow("##3---");

    if (tab->getType() == ChatTab::TAB_WHISPER)
    {
        WhisperTab *wTab = static_cast<WhisperTab*>(tab);
        std::string name = wTab->getNick();

        Being* being  = beingManager->findBeingByName(name, Being::PLAYER);
        if (being)
        {
            mBeingId = being->getId();

            mBrowserBox->addRow(strprintf("@@trade|%s@@",
                                          strprintf(_("Trade with %s..."),
                                                    name.c_str()).c_str()));
            mBrowserBox->addRow(strprintf("@@attack|%s@@",
                                          strprintf(_("Attack %s"),
                                                    name.c_str()).c_str()));

            mBrowserBox->addRow("##3---");

            mBrowserBox->addRow(strprintf(_("@@heal|Heal %s@@"), name.c_str()));
            mBrowserBox->addRow("##3---");

            switch (player_relations.getRelation(name))
            {
                case PlayerRelation::NEUTRAL:
                    mBrowserBox->addRow(strprintf("@@friend|%s@@",
                                        strprintf(_("Befriend %s"),
                                                  name.c_str()).c_str()));

                case PlayerRelation::FRIEND:
                    mBrowserBox->addRow(strprintf("@@disregard|%s@@",
                                        strprintf(_("Disregard %s"),
                                                  name.c_str()).c_str()));
                    mBrowserBox->addRow(strprintf("@@ignore|%s@@",
                                        strprintf(_("Ignore %s"),
                                                  name.c_str()).c_str()));
                    break;

                case PlayerRelation::DISREGARDED:
                    mBrowserBox->addRow(strprintf("@@unignore|%s@@",
                                        strprintf(_("Unignore %s"),
                                                  name.c_str()).c_str()));
                    mBrowserBox->addRow(strprintf("@@ignore|%s@@",
                                        strprintf(_("Completely ignore %s"),
                                                  name.c_str()).c_str()));
                    break;

                case PlayerRelation::IGNORED:
                    mBrowserBox->addRow(strprintf("@@unignore|%s@@",
                                        strprintf(_("Unignore %s"),
                                                  name.c_str()).c_str()));
                    break;

                default:
                    break;
            }

            mBrowserBox->addRow(strprintf("@@follow|%s@@",
                                          strprintf(_("Follow %s"),
                                                    name.c_str()).c_str()));
            mBrowserBox->addRow(strprintf("@@imitation|%s@@",
                                          strprintf(_("Imitation %s"),
                                                    name.c_str()).c_str()));

            mBrowserBox->addRow("##3---");

            if (player_node->isInParty())
            {
                if (player_node->getParty())
                {
                    if (!player_node->getParty()->isMember(wTab->getNick()))
                        mBrowserBox->addRow(strprintf("@@party|%s@@", _("Invite to party")));
                    else
                        mBrowserBox->addRow(strprintf("@@kick party|%s@@", _("Kick from party")));
                    mBrowserBox->addRow("##3---");
                }
            }
        }
    }
    mBrowserBox->addRow(strprintf("@@cancel|%s@@", _("Cancel")));

    showPopup(x, y);
}

void PopupMenu::handleLink(const std::string &link)
{
    logger->log("popup link: " + link);

    Being *being = beingManager->findBeing(mBeingId);

    // Talk To action
    if (link == "talk" &&
        being && being->getType() == Being::NPC)
    {
        dynamic_cast<NPC*>(being)->talk();
    }
    // Trade action
    else if (link == "trade" &&
             being &&
             being->getType() == Being::PLAYER)
    {
        Net::getTradeHandler()->request(being);
        tradePartnerName = being->getName();
    }
    else if (link == "buy" &&
             being && mBeingId != 0 &&
             being->getType() == Being::NPC)
    {
        Net::getNpcHandler()->buy(mBeingId);
    }
    else if (link == "sell" &&
             being && mBeingId != 0 &&
             being->getType() == Being::NPC)
    {
        Net::getNpcHandler()->sell(mBeingId);
    }
    // Attack action
    else if (link == "attack" && being)
    {
        player_node->attack(being, true);
    }
    else if (link == "heal" && being && being->getType() != Being::MONSTER)
    {
        beingManager->heal(player_node, being);
    }
    else if (link == "unignore" &&
             being &&
             being->getType() == Being::PLAYER)
    {
        player_relations.setRelation(being->getName(), PlayerRelation::NEUTRAL);
    }
    else if (link == "ignore" &&
             being &&
             being->getType() == Being::PLAYER)
    {
        player_relations.setRelation(being->getName(), PlayerRelation::IGNORED);
    }
    else if (link == "disregard" &&
             being &&
             being->getType() == Being::PLAYER)
    {
        player_relations.setRelation(being->getName(), PlayerRelation::DISREGARDED);
    }
    else if (link == "friend" &&
             being &&
             being->getType() == Being::PLAYER)
    {
        player_relations.setRelation(being->getName(), PlayerRelation::FRIEND);
    }
    // Guild action
    else if (link == "guild" &&
             being &&
             being->getType() == Being::PLAYER)
    {
        player_node->inviteToGuild(being);
    }
    else if (link == "nuke" && being)
    {
        beingManager->addBlock(being->getId());
        beingManager->destroyBeing(being);
    }
    // Follow Player action
    else if (link == "follow" && being)
    {
        player_node->setFollow(being->getName());
    }
    else if (link == "imitation" && being)
    {
        player_node->setImitate(being->getName());
    }
    // Pick Up Floor Item action
    else if ((link == "pickup") && mFloorItem)
    {
        player_node->pickUp(mFloorItem);
    }
    // Look To action
    else if (link == "look")
    {
    }
    else if (link == "use" && mItem)
    {
        if (mItem->isEquipment())
        {
            if (mItem->isEquipped())
                Net::getInventoryHandler()->unequipItem(mItem);
            else
                Net::getInventoryHandler()->equipItem(mItem);
        }
        else
        {
            Net::getInventoryHandler()->useItem(mItem);
        }
    }
    else if (link == "chat" && mItem)
    {
        chatWindow->addItemText(mItem->getInfo().getName());
    }
    else if (link == "whisper" && being)
    {
        if (config.getValue("whispertab", false))
            chatWindow->localChatInput("/q " + being->getName());
        else
            chatWindow->setInputText("/w \"" + being->getName() + "\" ");
    }
    else if (link == "split" && mItem)
    {
        ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit,
                             inventoryWindow, mItem);
    }
    else if (link == "drop" && mItem)
    {
        ItemAmountWindow::showWindow(ItemAmountWindow::ItemDrop,
                             inventoryWindow, mItem);
    }
    else if (link == "store" && mItem)
    {
        ItemAmountWindow::showWindow(ItemAmountWindow::StoreAdd,
                             inventoryWindow, mItem);
    }
    else if (link == "retrieve" && mItem)
    {
        ItemAmountWindow::showWindow(ItemAmountWindow::StoreRemove, mWindow,
                                     mItem);
    }
    else if (link == "party" && being && being->getType() == Being::PLAYER)
    {
        Net::getPartyHandler()->invite(dynamic_cast<Player*>(being));
    }
    else if (link == "kick party" && being && being->getType() == Being::PLAYER)
    {
        Net::getPartyHandler()->kick(dynamic_cast<Player*>(being));
    }
    else if (link == "name" && being)
    {
        const std::string &name = being->getName();
        chatWindow->addInputText(name);
    }
    else if (link == "admin-kick" &&
             being &&
             (being->getType() == Being::PLAYER ||
              being->getType() == Being::MONSTER))
    {
        Net::getAdminHandler()->kick(being->getId());
    }
    else if (link == "chat close" && mTab)
    {
        mTab->handleCommand("close", "");
    }
    else if (link == "leave party" && mTab)
    {
        Net::getPartyHandler()->leave();
    }
    else if (link == "chat clear" && mTab)
    {
        chatWindow->clearTab();
    }
    // Unknown actions
    else if (link != "cancel")
    {
        logger->log("PopupMenu: Warning, unknown action '%s'", link.c_str());
    }

    setVisible(false);

    mBeingId = 0;
    mFloorItem = NULL;
    mItem = NULL;
}

void PopupMenu::showPopup(Window *parent, int x, int y, Item *item,
                          bool isInventory)
{
    if (!item)
        return;

    mItem = item;
    mWindow = parent;
    mBrowserBox->clearRows();

    if (isInventory)
    {
        if (item->isEquipment())
        {
            if (item->isEquipped())
                mBrowserBox->addRow(strprintf("@@use|%s@@", _("Unequip")));
            else
                mBrowserBox->addRow(strprintf("@@use|%s@@", _("Equip")));
        }
        else
            mBrowserBox->addRow(strprintf("@@use|%s@@", _("Use")));

        if (item->getQuantity() > 1)
            mBrowserBox->addRow(strprintf("@@drop|%s@@", _("Drop...")));
        else
            mBrowserBox->addRow(strprintf("@@drop|%s@@", _("Drop")));

        if (Net::getInventoryHandler()->canSplit(item))
            mBrowserBox->addRow(strprintf("@@split|%s@@", _("Split")));

        if (StorageWindow::isActive())
            mBrowserBox->addRow(strprintf("@@store|%s@@", _("Store")));
    }
    // Assume in storage for now
    // TODO: make this whole system more flexible, if needed
    else
    {
        mBrowserBox->addRow(strprintf("@@retrieve|%s@@", _("Retrieve")));
    }
    mBrowserBox->addRow(strprintf("@@chat|%s@@", _("Add to chat")));
    mBrowserBox->addRow("##3---");
    mBrowserBox->addRow(strprintf("@@cancel|%s@@", _("Cancel")));

    showPopup(x, y);
}

void PopupMenu::showPopup(int x, int y)
{
    setContentSize(mBrowserBox->getWidth() + 8, mBrowserBox->getHeight() + 8);
    if (graphics->getWidth() < (x + getWidth() + 5))
        x = graphics->getWidth() - getWidth();
    if (graphics->getHeight() < (y + getHeight() + 5))
        y = graphics->getHeight() - getHeight();
    setPosition(x, y);
    setVisible(true);
    requestMoveToTop();
}
