/*
* location.cpp - author: Keith Fulton <keith@paqrat.com>
*
* Copyright (C) 2004 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/objreg.h>
#include <iutil/object.h>
#include <csutil/csobject.h>
#include <iutil/vfs.h>
#include <iutil/document.h>
#include <csutil/xmltiny.h>
#include <iengine/engine.h>

#include "location.h"
#include "util/consoleout.h"
#include "util/log.h"
#include "util/psstring.h"


/*------------------------------------------------------------------*/

iSector* Location::GetSector(iEngine * engine)
{
    if (sector) return sector;
    
    sector = engine->FindSector(sectorName.GetDataSafe());
    return sector;
}



/*------------------------------------------------------------------*/


bool LocationType::Load(iDocumentNode *node)
{
    name = node->GetAttributeValue("name");
    if ( !name.Length() )
    {
        CPrintf(CON_ERROR, "Location Types must all have name attributes.\n");
        return false;
    }

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

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

        // This is a widget so read it's factory to create it.
        if ( strcmp( node->GetValue(), "loc" ) == 0 )
        {
            Location *newloc = new Location;
            newloc->pos.x      = node->GetAttributeValueAsFloat("x");
            newloc->pos.y      = node->GetAttributeValueAsFloat("y");
            newloc->pos.z      = node->GetAttributeValueAsFloat("z");
            newloc->rot_angle  = node->GetAttributeValueAsFloat("angle");
            newloc->radius     = node->GetAttributeValueAsFloat("radius");
            newloc->sectorName = node->GetAttributeValue("sector");
            locs.Push(newloc);
        }
    }
    return true;
}

bool LocationType::CheckWithinBounds(csVector3& p)
{
    if (!region)
        return false;

    // Thanks to http://astronomy.swin.edu.au/~pbourke/geometry/insidepoly/
    // for this example code.
    int counter = 0;
    size_t i,N=locs.Length();
    float xinters;
    csVector3 p1,p2;

    p1 = locs[0]->pos;
    for (i=1; i<=N; i++)
    {
        p2 = locs[i % N]->pos;
        if (p.z > MIN(p1.z,p2.z))
        {
            if (p.z <= MAX(p1.z,p2.z))
            {
                if (p.x <= MAX(p1.x,p2.x))
                {
                    if (p1.z != p2.z)
                    {
                        xinters = (p.z-p1.z)*(p2.x-p1.x)/(p2.z-p1.z)+p1.x;
                        if (p1.x == p2.x || p.x <= xinters)
                            counter++;
                    }
                }
            }
        }
        p1 = p2;
    }

    return (counter % 2 != 0);
}

Waypoint::Waypoint()
{
    distance = 0.0;
    pi = NULL;   
}


bool Waypoint::Load(iDocumentNode *node, iEngine * engine)
{
    id = 0;
    name = node->GetAttributeValue("name");
    loc.pos.x = node->GetAttributeValueAsFloat("x");
    loc.pos.y = node->GetAttributeValueAsFloat("y");
    loc.pos.z = node->GetAttributeValueAsFloat("z");
    loc.sectorName = node->GetAttributeValue("sector");
    loc.sector = engine->FindSector(loc.sectorName);
    loc.radius = node->GetAttributeValueAsFloat("radius");
    loc.rot_angle = 0.0;
    allow_return = node->GetAttributeValueAsBool("allow_return",false); // by default don't allow return to previous point

    return (name.Length()>0);
}


bool Waypoint::Load(iResultRow& row, iEngine *engine)
{
    id             = row.GetInt("id");
    name           = row["name"];
    loc.pos.x      = row.GetFloat("x");
    loc.pos.y      = row.GetFloat("y");
    loc.pos.z      = row.GetFloat("z");
    loc.sectorName = row["sector"];
    loc.sector     = engine->FindSector(loc.sectorName);
    loc.radius     = row.GetFloat("radius");
    loc.rot_angle  = 0.0;

    psString flagstr(row["flags"]);
    if (flagstr.FindSubString("ALLOW_RETURN",0,true)!=-1)
    {
        allow_return = true;
    }
    else
    {
        allow_return = false;
    }
    return true;
}



bool Waypoint::CheckWithin(iEngine * engine, const csVector3& pos, const iSector* sector)
{
    if (sector == loc.GetSector(engine))
    {
        float range = (loc.pos - pos).Norm();
        if (range < loc.radius)
            return true;
    }

    return false;
}
