//#define INDOOR_VIS

import java.io.*;
import java.util.*;
import java.util.zip.*;


public class WaypointSystem implements ISaveGame
{
    final static String _ZIP_FILE_NAME = "Waypoints.zip";
    
    static float _CORRECTION = 5f;
	static float _MAX_TEST_VECTOR_LEN = 150f;
    static float _MIN_TEST_VECTOR_LEN = 50f;
    static int _MIN_GEN_POINTS_COUNT = 50;

	static int _MAX_GEN_POINTS = 4000; // maksymalna liczba punktow (ograniczenia pamieciowe)
    
    java.util.ArrayList pts = new java.util.ArrayList(); // tablica wszystkich waypointow tego waypointsystem
    
    short m_CurrWaypoint = 0; // biezacy waypoint AI
	NewAI m_Owner = null; // wlasciciel tego waypoint systemu

	static int _MAX_MOVE_STEPS = 10; // maksymalna liczba krokow przy szukaniu drogi w budynkach
	static java.util.HashSet m_CalcSet = new java.util.HashSet(_MAX_MOVE_STEPS); // pomocniczy hashset do punktow
	Vector [] m_aPath = null;// sciezka ze znaleziona droga
    short [] m_aWayPointsPath = null; // waypointy nalezace do drogi

    static FindPathInfo m_FindPathInfo = new FindPathInfo();
    
    /** zmienne pomocnicze */
    static Vector vTemp[] = new Vector[2];
    static int iNdx = 0;

	WaypointSystem()
    {
        //SG
    }
    
	WaypointSystem(NewAI ai)
	{
		m_Owner = ai;
        m_aWayPointsPath = new short[_MAX_MOVE_STEPS];
        m_aPath = new Vector[_MAX_MOVE_STEPS + 1]; 
        for (int i = 0; i < m_aPath.length; i++)
            m_aPath[i] = new Vector();
    }

    public void GenerateWaypoints()
    {
        GenerateWaypoints(_MAX_TEST_VECTOR_LEN);
    }
	
	void GenerateWaypoints(float moveLen)
	{
		// tablica przetwarzanych punktow
        pts = null;
		pts = new java.util.ArrayList();
		Vector vS = new Vector();
		Vector vE = new Vector();
		Vector vectors[] = new Vector[8];
		vectors[0] = new Vector(0, 0, moveLen);
		vectors[1] = new Vector(moveLen, 0, 0);
		vectors[2] = new Vector(0, 0, -moveLen);
		vectors[3] = new Vector(-moveLen, 0, 0);
		vectors[4] = new Vector(moveLen, 0, moveLen);
		vectors[5] = new Vector(-moveLen, 0, moveLen);
		vectors[6] = new Vector(moveLen, 0, -moveLen);
		vectors[7] = new Vector(-moveLen, 0, -moveLen);
		short iCurrInd = 0;
        AIWayPoint currWP = null;

        m_Owner.SetCollAction(ECollFlags.OBJCOL_ACTION_STOP);
		
		// ten punkt wrzucamy jako pierwszy - na pewno mozna tam stac...
		AIWayPoint startPoint = new AIWayPoint(m_Owner.GetPosition());
		pts.add(startPoint);
		SetCurrWaypoint((short) 0);
		boolean bGenerate = true;
	
		while ( (iCurrInd < pts.size()) && (bGenerate) )
		{
            currWP = ((AIWayPoint) pts.get(iCurrInd)); 
			vS.Set(currWP.m_vPos);
			for (int i = 0; i < vectors.length; i++)
			{
				m_Owner.SetPositionWorld(vS);
				vE.Set(vS);

				Vector vMove = new Vector(vectors[i]);
				if ( m_Owner.MoveByEllipsoidPure(vMove) )
				{
					vE.Add(vMove);
					m_Owner.SetPositionWorld(vE);
					if (InBuilding(m_Owner) && 
                        !IsOnMovingPlatform(m_Owner))
					{
						if (!CorrectPosInBuilding(m_Owner, 0f))
							continue; // nie mozemy "zagruntowac", nie mozemy tam wejsc
						
						// sprawdzamy, czy mozemy przejsc w druga strone
						vMove.Set(vS);
						vMove.Sub(vE);
						if ( !m_Owner.MoveByEllipsoidPure(vMove) )
							continue;
						
						vE.Set(m_Owner.GetPosition());
						// sprawdzamy, czy nie byl juz wczesniej dodany do tablicy
						short iwp = InTable(vE);
						if (iwp == -1)
						{
							// nie byl dodany do tablicy, dodajemy teraz
							AIWayPoint wp = new AIWayPoint(vE);
                            iwp = (short) pts.size();
							pts.add(wp);
							if (pts.size() > _MAX_GEN_POINTS)
								bGenerate = false;
						}
						currWP.AddNeighbour(iwp); // dodajemy sasiada
                        ((AIWayPoint) pts.get(iwp)).AddNeighbour(iCurrInd);
					}
				}
			}
			iCurrInd++;
		}
		if (!bGenerate)
			GameObject.CrashLog("max num of waypoints reached...");
		m_Owner.SetPositionWorld(startPoint.m_vPos);
        
        if (pts.size() < _MIN_GEN_POINTS_COUNT && 
            moveLen > _MIN_TEST_VECTOR_LEN)
        {
            pts = null;
            GenerateWaypoints(_MIN_TEST_VECTOR_LEN);
            return;
        }
        
		SaveWaypoints(pts, m_Owner);

        m_Owner.SetCollAction(ECollFlags.OBJCOL_ACTION_SLIDE);
	}
	
    short FindNearestPoint(Vector vPos)
    {
        float minDist2 = Float.MAX_VALUE;
        short pointIdx = -1;
        short ptsNum = (short)pts.size();
        float dist2;
        for (short i = 0; i < ptsNum; i++)
        {
            AIWayPoint point = (AIWayPoint)pts.get(i);
            if (point != null && 
                (dist2 = MathUtils.dist2D2(vPos, point.m_vPos)) < minDist2)
            {
                minDist2 = dist2;
                pointIdx = i;
            }
        }
        return pointIdx;
    }
    
	private short InTable(Vector vPoint)
	{
		for (short j = 0; j < pts.size(); j++)
		{
			AIWayPoint pt = (AIWayPoint) pts.get(j);
			if ( (Math.abs(pt.m_vPos.fX - vPoint.fX) < AIWayPoint._IDENTITY_DIST_2) &&
				 (Math.abs(pt.m_vPos.fY - vPoint.fY) < _CORRECTION) &&
				 (Math.abs(pt.m_vPos.fZ - vPoint.fZ) < AIWayPoint._IDENTITY_DIST_2) )
				return j;
		}
		return -1;
	}
	
    private static Vector sv_vStart = new Vector();
    private static Vector sv_vEnd = new Vector();
    static boolean InBuilding(ChromeActor actor)
    {
        actor.GetPosition(sv_vStart);
        sv_vEnd.Set(sv_vStart);
        sv_vStart.fY += 25.f;
        sv_vEnd.fY -= 100.f;
        
        Collision stCollInfo = actor.GetModule().TraceObjectsExclude(sv_vStart, sv_vEnd, actor);
        
        // TODO: ten warunek powinien byc bardziej skomplikowany
        if ( (stCollInfo != null) && (stCollInfo.Obstacle != null) )
            return true;
        return false;
    }
    
    static boolean IsOnMovingPlatform(ChromeActor actor)
    {
        actor.GetPosition(sv_vStart);
        sv_vEnd.Set(sv_vStart);
        sv_vStart.fY += 25.f;
        sv_vEnd.fY -= 100.f;
        
        Collision stCollInfo = actor.GetModule().TraceObjectsExclude(sv_vStart, sv_vEnd, actor);
        
        return ((stCollInfo != null) && 
                (stCollInfo.Obstacle instanceof LiftPlatform));
    }
    
	static boolean CorrectPosInBuilding(ChromeActor actor, float fAdditionalUp)
	{
		Vector vMove = new Vector(0f, fAdditionalUp, 0f);
		vMove.Add(actor.GetPosition());
		actor.SetPositionWorld(vMove);
		vMove.Set(0f, -125f, 0f);
		if (actor.MoveByEllipsoidPure(vMove))
			return false; // nie natrafilismy na przeszkode
		else
		{
			float fLen = vMove.Len();
			if (fLen == 0)
				vMove.Set(0, _CORRECTION, 0);
			else
			{
				fLen -= _CORRECTION;
				vMove.Set(0, -fLen, 0);
			}
			vMove.Add(actor.GetPosition());
			actor.SetPositionWorld(vMove);
			return true;
		}
	}
	
	void GetCloserPoint(Vector vPos)
	{
		short nearest = m_CurrWaypoint;
        AIWayPoint curr = (AIWayPoint) pts.get(m_CurrWaypoint);
		
		Vector vDist = new Vector(curr.m_vPos);
		vDist.Sub(vPos);
		vDist.fY = 0;
		float fMinDist = vDist.Len2();
		
		for (int i = 0; i < curr.m_Neighbours.length; i++)
		{
			vDist.Set( ((AIWayPoint) pts.get(curr.GetNeighbour(i))).m_vPos );
			vDist.Sub(vPos);
			vDist.fY = 0;
			float fDist = vDist.Len2();
			
			if (fDist < fMinDist)
			{
				nearest = curr.GetNeighbour(i);
				fMinDist = fDist;
			}
		}
		SetCurrWaypoint(nearest);
	}

    // to test
    Vector GetCurrWPPos()
    {
        return ((AIWayPoint) pts.get(m_CurrWaypoint)).m_vPos;
    }
    
	void SaveWaypoints(java.util.ArrayList pts, ChromeActor actor)
	{
		String sDirectory = actor.GetModule().GetModulePath();
		
		java.io.File directory = new java.io.File(sDirectory);
		if ( (!directory.exists()) || (!directory.isDirectory()) )
			directory.mkdir();
		
		String fileName = sDirectory + actor.GetName() + "_" + actor.GetUniqueID() + ".wp";
		
		GameObject.CrashLog("Saving waypoints:" + fileName + "...");
		
		try
		{
			DataOutputStream output = new DataOutputStream(new FileOutputStream(fileName));
			
			// zapisujemy pozycje aktora do pozniejszego sprawdzenia, czy jego pozycja sie nie
			// zmienila, i czy przypadkiem nie nalezy przegenerowac waypointow
			actor.GetPosition().WriteToDisk(output);
			output.writeInt(pts.size());
			for (int i = 0; i < pts.size(); i++)
			{
				AIWayPoint pt = (AIWayPoint) pts.get(i);
				pt.Save(output);
			}
			
			for (int i = 0; i < pts.size(); i++)
			{
				AIWayPoint pt = (AIWayPoint) pts.get(i);
				pt.SaveReferences(output);
			}
			
			output.close();
			
			GameObject.CrashLog("Done.\n");
		}
		catch (Exception e)
		{
			GameObject.CrashLog("error in waypoints writing!!!\n");
			e.printStackTrace();
			return;
		}
	}
	
    /** Zaczytanie waypointow dla podanego AI.
     *  System probuje zaczytac nowsza z dwoch wersji (z zipa albo pliku wp). 
     *  Jesli nowszy jest plik w zipie a ma nieprawidlowa pozycje - nastepuje proba zaczytania pliku na dysku. Jesli sie 
     *  to nie powiedzie (albo pozycja jest nieprawidlowa) - nastepuje odczyt jest nieudany i nalezy przegenerowac waypointy.
     */
    static WaypointSystem LoadWaypoints(NewAI ai)
    {
		String sDirectory = ai.GetModule().GetModulePath();
		
		File directory = new File(sDirectory);
		if ( (!directory.exists()) || (!directory.isDirectory()) )
		{
			GameObject.CrashLog("WaypointSystem.LoadWaypoints(): Directory " + sDirectory + "doesn't exist!!!\n");
			return null;
		}
		
		String fileName = ai.GetName() + "_" + ai.GetUniqueID() + ".wp";
        String filePath = sDirectory + fileName;
        String zipPath = sDirectory + _ZIP_FILE_NAME;
        
        DataInputStream dataInput;
        WaypointSystem retWS = null;
        File wpFile = new File(filePath);

        try
        {
            ZipFile zipFile = new ZipFile(zipPath);
            Enumeration entries = zipFile.entries();
            ZipEntry entry, foundEntry = null;
            while (entries.hasMoreElements())
            {
                entry = (ZipEntry)entries.nextElement();
                if (entry.getName().equalsIgnoreCase(fileName))
                {
                    foundEntry = entry;
                    break;
                }
            }

            if (foundEntry != null) // znalazl plik w zipie
            {
                if (wpFile.exists() && foundEntry.getTime() < wpFile.lastModified())
                {
                    GameObject.CrashLog("Waypoints in zip file older than on disk\n");
                    foundEntry = null; // nowszy plik na dysku, pomin ten z zipa
                }
            }
            
            if (foundEntry != null)
            {
                //GameObject.CrashLog("Trying to load waypoints from zip file " + fileName + "...");
                dataInput = new DataInputStream(zipFile.getInputStream(foundEntry));
                retWS = LoadWaypointsFromStream(ai, dataInput, fileName);
                /*
                if (retWS == null)
                    GameObject.CrashLog("\nCan't read valid waypoints from zip!\n");
                else
                    GameObject.CrashLog("Done\n");
                */
            }
        }
        catch (IOException e)
        { 
            GameObject.CrashLog("ERROR reading waypoints (" + fileName + ") from " + _ZIP_FILE_NAME + " file!\n");
        }				
        
        try {
            if (retWS == null)
            {
                if (wpFile.exists())
                {
                    //GameObject.CrashLog("Trying to load waypoints from disk file " + fileName + "...");
                    dataInput = new DataInputStream(new FileInputStream(filePath));
                    retWS = LoadWaypointsFromStream(ai, dataInput, fileName);
                    /*if (retWS == null)                                       
                        GameObject.CrashLog("\nCan't read valid waypoints from disk!\n");
                    else
                        GameObject.CrashLog("Done\n");
                    */
                }
                else
                    GameObject.CrashLog("Can't find file " + filePath + " on disk!\n");
            }
        }
        catch (IOException e)
        { 
            GameObject.CrashLog("ERROR reading waypoints from file: " + filePath + "!\n");
        }				

        return retWS;
    }
    
	static WaypointSystem LoadWaypointsFromStream(NewAI ai, DataInputStream inputStream, String friendlyName)
	{
		WaypointSystem ws = new WaypointSystem(ai);
		
		try
		{
			Vector vActorPos = new Vector();
			vActorPos.ReadFromDisk(inputStream);
			
			if ( !AIWayPoint.Equals(ai.GetPosition(), vActorPos) )
			{
                //GameObject.CrashLog("Pozycja aktora zmienila sie, przegenerowuje punkty !!!\n");
				return null;
			}

			int iPoints = inputStream.readInt();
            ws.pts = new java.util.ArrayList();
			
			for (int i = 0; i < iPoints; i++)
			{
				AIWayPoint pt = new AIWayPoint();
				pt.Load(inputStream);
				ws.pts.add(pt);
			}
			
			for (int i = 0; i < iPoints; i++)
			{
				AIWayPoint pt = (AIWayPoint) ws.pts.get(i);
				pt.LoadReferences(inputStream);
			}
			
			boolean bFound = false;
			for (int i = 0; i < iPoints; i++)
			{
				AIWayPoint pt = (AIWayPoint) ws.pts.get(i);
				if (pt.m_Neighbours == null || pt.m_Neighbours.length == 0)
					bFound = true;
			}
			if (bFound)
            {
                if (iPoints > 1)
                    GameObject.CrashLog("WARNING!!! Waypoint without exit in file " + friendlyName + ". Delete the file!!!\n");
                else
                if (iPoints == 1)
                    GameObject.CrashLog("WARNING!!! File " + friendlyName + " contains only one waypoint. AI won't be able to move!!!\n");
                
            }

            ws.SetCurrWaypoint((short) 0);
		}
		catch (Exception e)
		{
			GameObject.CrashLog("error in waypoints reading!!!\n");
			return null;
		}
		
		return ws;
	}
	
    static java.util.ArrayList visibleWaypointsSystems = null;
    
	static void VisualizeWaypointSystem(ChromeActor actor)
	{
        if (!(actor instanceof NewAI))
            return;
        NewAI ai = (NewAI) actor;
        if (ai.m_WpSystem == null)
            return;
        
        if (visibleWaypointsSystems == null)
            visibleWaypointsSystems = new java.util.ArrayList();
        
        VisibleWaypointsHolder holder = new VisibleWaypointsHolder(ai);
        
    	for (int i = 0; i < ai.m_WpSystem.pts.size(); i++)
    	{
    		MeshObject cMesh = (MeshObject) actor.CreateObject("MeshObject");
    		cMesh.LoadMesh("Data/Interface/zarowka.3da");
            cMesh.ActivateOnModule();
    		cMesh.ClearFlag(EFlags._COLLISION);
    		cMesh.SetLighting(false);
        	cMesh.EnableRendering(true);
    		cMesh.SetFlag(EFlags._PORTAL_INDEPENDENT);
    		cMesh.SetPositionWorld( ((AIWayPoint) ai.m_WpSystem.pts.get(i)).m_vPos );
            cMesh.SetName("Zarowka" + i);
            
            holder.AddMesh(cMesh);
        }
        
        visibleWaypointsSystems.add(holder);
        
//#if INDOOR_VIS
        aRoute = new MeshObject[_MAX_MOVE_STEPS + 1];
        for (int i = 0; i < aRoute.length; i++)
    	{
    		MeshObject cMesh = (MeshObject) actor.CreateObject("MeshObject");
    		cMesh.LoadMesh("data/interface/choragiewka.3da");
            cMesh.ActivateOnModule();
    		cMesh.ClearFlag(EFlags._COLLISION);
    		cMesh.SetFlag(EFlags._PORTAL_INDEPENDENT);
    		cMesh.SetLighting(false);
    		cMesh.EnableRendering(false);
            cMesh.SetScale(0.3f, 0.3f, 0.3f);
            aRoute[i] = cMesh;
        }
//#endif
	}
    
	static void DevisualizeWaypointSystem(ChromeActor actor)
    {
        if (!(actor instanceof NewAI))
            return;
        
        if (visibleWaypointsSystems == null)
            return;
        
        NewAI ai = (NewAI) actor;
        VisibleWaypointsHolder holder = null;
        MeshObject mesh = null;
        
        for (int i = 0; i < visibleWaypointsSystems.size(); i++)
        {
            holder = (VisibleWaypointsHolder) visibleWaypointsSystems.get(i);
            
            if (holder.GetAI() == ai)
            {
                for (int j = 0; j < holder.GetMeshesNum(); j++)
                {
                    mesh = holder.GetMesh(j);
                    mesh.delete();
                    mesh = null;
                }
                
                holder.delete();
                holder = null;
                
                visibleWaypointsSystems.remove(i);
                
                return;
            }
        }
    }
    

//#if INDOOR_VIS
	static MeshObject aRoute[] = null;
//#endif
    
	// szukanie drogi od biezacego punktu (m_CurrWaypoint) do vEnd. Zwraca liczbe punktow sciezki
	FindPathInfo FindPath(Vector vStart, Vector vEnd)
	{
		int i, iSteps = 0;
		AIWayPoint currWP = (AIWayPoint) pts.get(m_CurrWaypoint);
		AIWayPoint currNeighbour = null;
		short chosenNeighbour = -1;
		float fMinDist;

		m_aPath[0].Set(currWP.m_vPos);
        m_aWayPointsPath[0] = m_CurrWaypoint;
        m_CalcSet.clear();
		m_CalcSet.add(currWP);
		iSteps++;

//#if INDOOR_VIS        
        if (aRoute != null)
            for (i = 0; i < aRoute.length; i++)
                aRoute[i].EnableRendering(false);
//#endif
        
		while (iSteps < m_aWayPointsPath.length)
		{
			fMinDist = MathUtils.dist2D2(currWP.m_vPos, vEnd);
			chosenNeighbour = -1;

            if (currWP.m_Neighbours == null)
            {
                m_FindPathInfo.iWPSteps = iSteps;
                m_FindPathInfo.iRealSteps = iSteps;
                return m_FindPathInfo;
            }
            
			for (i = 0; i < currWP.m_Neighbours.length; i++)
			{
				currNeighbour = (AIWayPoint) m_Owner.m_WpSystem.pts.get(currWP.m_Neighbours[i]);
				float fDist = MathUtils.dist2D2(currNeighbour.m_vPos, vEnd);
				if ( (fDist < fMinDist) && (!m_CalcSet.contains(currNeighbour)) )
				{
					fMinDist = fDist;
					chosenNeighbour = currWP.m_Neighbours[i];
				}
			}
			
			if (chosenNeighbour != -1)
			{
				currWP = (AIWayPoint) pts.get(chosenNeighbour);
				m_CalcSet.add(currWP);
				m_aPath[iSteps].Set(currWP.m_vPos);
                m_aWayPointsPath[iSteps] = chosenNeighbour;
//#if INDOOR_VIS                
                if (aRoute != null)
                {
                    aRoute[iSteps].EnableRendering(true);
                    aRoute[iSteps].SetPositionWorld(currWP.m_vPos);
                }
//#endif                
                iSteps++;
			}
			else
                break;
		}
        
        m_FindPathInfo.iWPSteps = iSteps;

        // nie wybralismy juz zadnego WP, wiec jestesmy w WP najblizszym do celu
        if (CanMakeExactMove(currWP, vEnd))
        {
            m_aPath[iSteps].Set(vEnd);
//#if INDOOR_VIS                
            if (aRoute != null)
            {
                aRoute[iSteps].EnableRendering(true);
                Vector vPos = new Vector(vEnd); // pobieramy wysokosc z poprzedniego wp
                vPos.fY = m_aPath[iSteps-1].fY;
                aRoute[iSteps].SetPositionWorld(vPos);
            }
//#endif                
            iSteps++;
        }
        
        m_FindPathInfo.iRealSteps = iSteps;
        return m_FindPathInfo;
	}
    
    boolean CanMakeExactMove(AIWayPoint wp, Vector vEnd)
    {
        if (MathUtils.dist2D2(wp.m_vPos, vEnd) < 1f)
            return false;
        
        Vector vDir = new Vector(vEnd);
        vDir.Sub(wp.m_vPos);
        vDir.fY = 0;
        vDir.Normalize();
        
        Vector vNeighbourDir = new Vector();
        
        iNdx = 0;
        for (int i = 0; i < wp.m_Neighbours.length; i++)
        {
            vNeighbourDir.Set( ((AIWayPoint) pts.get(wp.m_Neighbours[i])).m_vPos );
            vNeighbourDir.Sub(wp.m_vPos);
            vNeighbourDir.fY = 0;
            vNeighbourDir.Normalize();
            
            if (vDir.Dot(vNeighbourDir) > 0.7072f)
            {
                if (iNdx == 2)
                {
                    GameObject.CrashLog("\n\n\nBLAD W CANMAKEEXACTMOVE!!!\n");
                    GameObject.CrashLog("wp.m_vPos:" + wp.m_vPos + "\n");
                    GameObject.CrashLog("vEnd:" + vEnd + "\n");
                    for (int k = 0; k < wp.m_Neighbours.length; k++)
                        GameObject.CrashLog("NEIGHBOUR[" + k + "]:" + (((AIWayPoint) pts.get(wp.m_Neighbours[k])).m_vPos) + "\n");
                    GameObject.CrashLog("\n\n\n");
                    return false;
                }
                
                vTemp[iNdx] = ((AIWayPoint) pts.get(wp.m_Neighbours[i])).m_vPos;
                iNdx++;
            }
        }
        
        if (iNdx < 2)
            return false;
        
        // liczymy, czy punkty leza po tej samej stronie prostej wyznaczonej przez znalezionych sasiadow
        vDir.Set(vTemp[0]);
        vDir.Sub(vTemp[1]);
        vDir.fY = 0;
        vDir.Normalize();
        
        vNeighbourDir.Set(vEnd);
        vNeighbourDir.Sub(vTemp[1]);
        vNeighbourDir.fY = 0;
        vNeighbourDir.Normalize();
        
        float fCross = vDir.fZ * vNeighbourDir.fX - vNeighbourDir.fZ * vDir.fX;
        
        vNeighbourDir.Set(wp.m_vPos);
        vNeighbourDir.Sub(vTemp[1]);
        vNeighbourDir.fY = 0;
        vNeighbourDir.Normalize();
        
        if ((vDir.fZ * vNeighbourDir.fX - vNeighbourDir.fZ * vDir.fX) * fCross >= 0)
            return true;
        return false;
    }
    
	Vector [] GetPathTab()
	{
		return m_aPath;
	}
    
    void SetCurrWaypoint(short wp)
    {
        m_CurrWaypoint = wp;
    }

    //*********************************************************
    //					SaveGame
    //*********************************************************
	
    public void SGLoadChunk(FileChunk cParentFC)
    {	
		FileChunk.Log("[WaypointSystem.SGLoadChunk]\n");
		
		FileChunk cFC = GameObject.LoadChunk(cParentFC);   
		
        if (cFC.GetID() == ESGChunksChrome._CHK_WAYPOINT_SYSTEM)
        {
            m_CurrWaypoint = (short) cFC.LoadInt();
            m_Owner = (NewAI) cFC.LoadObjectRef();
            
            int iTemp = cFC.LoadInt();
            if (iTemp < 0)
                m_aPath = null;
            else
            {
                m_aPath = new Vector[iTemp];
                for (int i = 0; i < iTemp; i++)
                    m_aPath[i] = cFC.LoadVector();
            }
            
            iTemp = cFC.LoadInt();
            if (iTemp < 0)
                m_aWayPointsPath = null;
            else
            {
                m_aWayPointsPath = new short[iTemp];
                for (int i = 0; i < iTemp; i++)
                    m_aWayPointsPath[i] = (short) cFC.LoadInt();
            }
            
            m_FindPathInfo.iRealSteps = cFC.LoadInt();
            m_FindPathInfo.iWPSteps = cFC.LoadInt();
        }
		
		cFC.delete();
	}
    
    public void SGSaveChunk(FileChunk cFCParent)
    {
		FileChunk.Log("[WaypointSystem.SGSaveChunk]\n");
		
        FileChunk cFC = GameObject.NewChunk(ESGChunksChrome._CHK_WAYPOINT_SYSTEM, cFCParent);
		
        cFC.SaveInt(m_CurrWaypoint);
        cFC.SaveObjectRef(m_Owner);
        
        if (m_aPath == null)
            cFC.SaveInt(-1);
        else
        {
            cFC.SaveInt(m_aPath.length);
            for (int i = 0; i < m_aPath.length; i++)
                cFC.SaveVector(m_aPath[i]);
        }

        
        if (m_aWayPointsPath == null)
            cFC.SaveInt(-1);
        else
        {
            cFC.SaveInt(m_aWayPointsPath.length);
            for (int i = 0; i < m_aWayPointsPath.length; i++)
                cFC.SaveInt((int) m_aWayPointsPath[i]);
        }
            
        cFC.SaveInt(m_FindPathInfo.iRealSteps);
        cFC.SaveInt(m_FindPathInfo.iWPSteps);

        cFC.delete(); 
	}				
	
	public void SGUpdate()
	{
		FileChunk.Log("[WaypointSystem.SGUpdate]\n");
	}
}

class FindPathInfo
{
    int iRealSteps = 0;
    int iWPSteps = 0;
    
    int GetRealSteps() { return iRealSteps; }
    int GetWPSteps() { return iWPSteps; }
}

class VisibleWaypointsHolder
{
    NewAI m_AI = null;
    java.util.ArrayList m_WaypointsMeshes = null;
        
    VisibleWaypointsHolder(NewAI ai)
    {
        m_WaypointsMeshes = new java.util.ArrayList();
        m_AI = ai;
    }
        
    void AddMesh(MeshObject obj)
    {
        if (obj != null)
            m_WaypointsMeshes.add(obj);
    }
    
    NewAI GetAI()
    {
        return m_AI;
    }
    
    int GetMeshesNum()
    {
        return m_WaypointsMeshes.size();
    }
    
    MeshObject GetMesh(int iNum)
    {
        return (MeshObject) m_WaypointsMeshes.get(iNum);
    }
    
    void delete()
    {
        m_WaypointsMeshes.clear();
        m_WaypointsMeshes = null;
        m_AI = null;
    }
}
