/*
 * psquest.cpp
 *
//  * Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) 
 *
 *
 * 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 (version 2 of the License)
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include <psconfig.h>

#include <iutil/document.h>
#include <csutil/xmltiny.h>

#include "util/log.h"
#include "util/strutil.h"
#include "util/consoleout.h"

#include "../globals.h"
#include "../psserver.h"
#include "../cachemanager.h"

#include "psquest.h"
#include "psquestprereqops.h"


psQuest::psQuest()
{
    id = 0;
    parent_quest = NULL;
    image = NULL;
    step_id = 0;
    player_lockout_time = 0;
    quest_lockout_time = 0;
    quest_last_activated = 0;
    prerequisite = NULL;
}

psQuest::~psQuest()
{
    if (prerequisite)
        delete prerequisite;
    
    prerequisite = NULL;
}

void psQuest::Init(int new_id, const char *new_name)
{
    id   = new_id;
    name = new_name;
}

bool psQuest::Load(iResultRow& row)
{
    id   = row.GetInt("id");
    name = row["name"];
    task = row["task"];
    flags = row.GetInt("flags");
    step_id = row.GetInt("minor_step_number");
    category = row["category"];
    prerequisiteStr = row["prerequisite"];

    parent_quest = CacheManager::GetSingleton().GetQuestByID( row.GetUInt32("master_quest_id") );

    image = CacheManager::GetSingleton().FindCommonString( row.GetUInt32("cstr_id_icon") );

    // the value is expressed in seconds
    player_lockout_time = row.GetInt("player_lockout_time")*1000; // Convert from sec to ticks 
    quest_lockout_time  = row.GetInt("quest_lockout_time")*1000;  // Convert from sec to ticks 

    return true;
}

bool LoadPrerequisiteXML(iDocumentNode * topNode, psQuestPrereqOp*&prerequisite)
{
    // Recursively decode the xml document and generate prerequisite operators.

    if ( strcmp( topNode->GetValue(), "pre" ) == 0 )
    {
        // This is the top node. Only one node is legal.

        csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

        while ( iter->HasNext() )
        {
            csRef<iDocumentNode> node = iter->Next();
            
            if ( node->GetType() != CS_NODE_ELEMENT )
                continue;

            LoadPrerequisiteXML(node,prerequisite);

            break;
        }
    } 
    else if ( strcmp( topNode->GetValue(), "completed" ) == 0 )
    {
        if (topNode->GetAttributeValue("quest"))
        {
            psQuest * quest = CacheManager::GetSingleton().GetQuestByName( topNode->GetAttributeValue("quest") );
            if (quest)
            {
                prerequisite = new psQuestPrereqOpQuestCompleted(quest);
            }
            else
            {
                Error2("Can't find quest %s while loading prerequisite script",
                       topNode->GetAttributeValue("quest"));
            }
        }
        else if (topNode->GetAttributeValue("category"))
        {
            int min = -1,max = -1;
            csString category = topNode->GetAttributeValue("category");
            if (topNode->GetAttributeValue("min")) min = topNode->GetAttributeValueAsInt("min");
            if (topNode->GetAttributeValue("max")) max = topNode->GetAttributeValueAsInt("max");
            if (min == -1 && max == -1)
            {
                Error1("Both min and max is -1, seting min to 1");
                min = 1;
            }
            prerequisite = new psQuestPrereqOpQuestCompletedCategory(category,min,max);
        }
        else
        {
            Error1("No attrib of quest or category in the completed node.");
            return false;
        }
    }
    else if ( strcmp( topNode->GetValue(), "assigned" ) == 0 )
    {
        psQuest * quest = CacheManager::GetSingleton().GetQuestByName( topNode->GetAttributeValue("quest") );
        if (quest)
        {
            prerequisite = new psQuestPrereqOpQuestAssigned(quest);
        }
        else
        {
            Error2("Can't find quest %s while loading prerequisite script",
                   topNode->GetAttributeValue("quest"));
            return false;
        }
    } 
    else if ( strcmp( topNode->GetValue(), "and" ) == 0 )
    {
        psQuestPrereqOpList * list = new psQuestPrereqOpAnd();
        prerequisite = list;
        
        csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

        while ( iter->HasNext() )
        {
            csRef<iDocumentNode> node = iter->Next();
            
            if (node->GetType() != CS_NODE_ELEMENT)
                continue;

            psQuestPrereqOp * op = NULL;
            
            LoadPrerequisiteXML(node,op);
            if (op)
            {
                list->Push(op);
            }
            else
                return false;
        }
    }
    else if ( strcmp( topNode->GetValue(), "or" ) == 0 )
    {
        psQuestPrereqOpList * list = new psQuestPrereqOpOr();
        prerequisite = list;
        
        csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

        while ( iter->HasNext() )
        {
            csRef<iDocumentNode> node = iter->Next();
            
            if ( node->GetType() != CS_NODE_ELEMENT )
                continue;

            psQuestPrereqOp * op = NULL;
            
            LoadPrerequisiteXML(node,op);
            if (op)
            {
                list->Push(op);
            }
            else
                return false;
        }
    }
    else if ( strcmp( topNode->GetValue(), "require" ) == 0 )
    {
        int min = -1,max = -1;
        if (topNode->GetAttributeValue("min")) 
            min = topNode->GetAttributeValueAsInt("min");
        if (topNode->GetAttributeValue("max")) 
            max = topNode->GetAttributeValueAsInt("max");

        psQuestPrereqOpList * list = new psQuestPrereqOpRequire(min,max);
        prerequisite = list;
        
        csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

        while ( iter->HasNext() )
        {
            csRef<iDocumentNode> node = iter->Next();
            
            if ( node->GetType() != CS_NODE_ELEMENT )
            continue;

            psQuestPrereqOp * op = NULL;
            
            LoadPrerequisiteXML(node,op);
            if (op)
            {
                list->Push(op);
            }
        }
    }
    else if ( strcmp( topNode->GetValue(), "not" ) == 0 )
    {
        csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

        while ( iter->HasNext() )
        {
            csRef<iDocumentNode> node = iter->Next();
            
            if ( node->GetType() != CS_NODE_ELEMENT )
            continue;

            psQuestPrereqOp * op;
            LoadPrerequisiteXML(node,op);
            psQuestPrereqOpList* list = new psQuestPrereqOpNot();
            list->Push(op);
            prerequisite = list;
            break;
        }
    } 
    else
    {
        Error2("Node \"%s\" isn't supported in  prerequisite scripts",topNode->GetValue());
        return false;
    }
    return true;
}


bool LoadPrerequisiteXML(psQuestPrereqOp*&prerequisite, csString script)
{
    csRef<iDocumentSystem> xml = csPtr<iDocumentSystem>(new csTinyDocumentSystem);
    csRef<iDocument> doc = xml->CreateDocument();
    const char* error = doc->Parse( script );
    if ( error )
    {
        Error3("%s\n%s",error, script.GetDataSafe() );
        return false;
    }

    csRef<iDocumentNode> root    = doc->GetRoot();
    if(!root)
    {
        Error1("No XML root in prerequisite script");
        return false;
    }
    csRef<iDocumentNode> topNode = root->GetNode("pre");
    if (topNode)
        return LoadPrerequisiteXML(topNode,prerequisite);
    else
    {
        Error1("Could not find <pre> tag in prerequisite script!");
        return false;
    }    

    return true;
}



bool psQuest::PostLoad()
{
    // Parse the prerequisite string
    if (prerequisiteStr != "")
    {
        Debug2(LOG_QUESTS,0, "Loading prereq  : %s", prerequisiteStr.GetDataSafe());

        LoadPrerequisiteXML(prerequisite,prerequisiteStr);
        prerequisiteStr.Empty();
        if (prerequisite)
        {
            Debug2(LOG_QUESTS, 0, "Resulting prereq: %s\n", prerequisite->GetScript().GetDataSafe());
        }
        else
            return false;
    }

    return true;
}

bool psQuest::AddPrerequisite(csString prerequisitescript)
{
    psQuestPrereqOp * op;
    LoadPrerequisiteXML(op,prerequisitescript);
    if (op) AddPrerequisite(op);
    return true;
}


bool psQuest::AddPrerequisite(psQuestPrereqOp * op)
{
    // Make sure that the first op is an AND list if there are an
    // prerequisite from before.
    if (prerequisite)
    {
        // Check if first op is an and list.
        psQuestPrereqOpAnd * list = dynamic_cast<psQuestPrereqOpAnd*>(prerequisite);
        if (list == NULL)
        {
            // If not insert an and list.
            list = new psQuestPrereqOpAnd();
            list->Push(prerequisite);
            prerequisite = list;
        }

        list->Push(op);
    }
    else
    {
        // No prerequisite from before so just set this.
        prerequisite = op;
    }

    return true;
}

csString psQuest::GetPrerequisiteStr()
{
    if (prerequisite)
        return prerequisite->GetScript();

    return "";
}
