public class AIPatroler extends AIMover
{
	/** sciezka tego AI */
	AIPatrolerPath aPath = null;
	
    /**indeks tablicy punktow patrolowych */
	int nCurrentNode = 0;

    /** zapamietana wartosc nCurrentNode (do CancelAction) */
    int nOldCurrentNode = 0;
    
    /** przyrost do patrolowania z powrotem */
    int nNextSkip = 1;

    /** ustawione dla nPathType==ONCE gdy AI dojdzie do konca sciezki */
    boolean bEndOfPathReached = false;
    
    //////////////////
    final static int _ACTION_GO_NEXT_PPOINT     = 50;
    final static int _ACTION_CHANGE_DIR_TO_PPOINT = 51;
    final static int _ACTION_PPOINT_WAIT_FOR = 52;

    /** chunki obslugiwane przez AIPatrolera 
     *  (MUSZA BYC ZGODNE Z TYMI Z C++)
     */
    final static int CHK_JAVA_AI_PATROL_POINTS     = 10010;
    final static int CHK_JAVA_AI_PATROL_POINTS_EXT = 10011;
    final static int CHK_JAVA_AI_PATROL_POINTS_EXT2= 10012;
       

    int METHOD_WaitInPatrolPointDone        = 0;
    int METHOD_ChangeDirToPatrolPointDone   = 0;
    
    public void OnCreate()
    {
        super.OnCreate();
        
        METHOD_WaitInPatrolPointDone = RegisterCallMethod("WaitInPatrolPointDone");
        METHOD_ChangeDirToPatrolPointDone = RegisterCallMethod("ChangeDirToPatrolPointDone");
    }
    
    void Initialize()
    {
        // ustawiamy pierwszy punkt patrolowy (przed pierwszym)
        nCurrentNode = -1;
        nOldCurrentNode = -1;
        nNextSkip = 1;
        bEndOfPathReached = false;
        super.Initialize();
    }
    
    /** Poczatkowe uruchomienie dzialania AI
     */
    public void Run()
    {
        // wlaczamy patrolowanie
        SetState("Patrol");
    }
    
    public void OnLoadChunk(int nChkID, int nDataSize, MapInputStream cMapStream)
    {
        if (nChkID == CHK_JAVA_AI_PATROL_POINTS)
        {
			// wczytywanie punktow
            try
			{
                int nPoints = cMapStream.readInt();
                
				aPath = new AIPatrolerPath();
                for (int i = 0; i < nPoints; i++)
                {
					AIPatrolerPathPoint point = new AIPatrolerPathPoint(cMapStream);
					aPath.aPoints.addElement(point);
                }
            }
            catch(java.io.IOException e)
            {
                LogD("File error while reading AI map data (CHK_JAVA_AI_PATROL_POINTS)\n");
            }
        }
        else
		if (nChkID == CHK_JAVA_AI_PATROL_POINTS_EXT)
        {
			// wczytywanie dodatkowych informacji o punktach
            try
			{
				int nPoints = cMapStream.readInt();
                
                if (aPath != null)
                {
                    if (nPoints != aPath.GetPointsNum())
					{
                        LogD("Incorrect number of points in chunk: CHK_JAVA_AI_PATROL_POINTS_EXT!!!\n");
						return;
					}

                    for (int i = 0; i < nPoints; i++)
                    {
						AIPatrolerPathPoint point = (AIPatrolerPathPoint) aPath.GetPoint(i);
						point.fRadius = cMapStream.readFloat();
                        point.fStopTimeInterval = cMapStream.readFloat();
                        point.iSpeed = (byte) (2 + cMapStream.readInt()); //2+ bo przenumerowano SPEED_... w MotionState
                        String sFnName = cMapStream.readString();
                        if (sFnName.compareTo("") == 0)
                            point.sTextField = null;
                        else
                        {
                            point.sTextField = sFnName;
                            // Sprawdz, czy na pewno taka metoda istnieje
                            try {
                                getClass().getMethod(sFnName, null);
                            }
                            catch (java.lang.Exception e)
                            {
                                LogD("ERROR: public method \"" + sFnName + "\" doesn't exist!\n");
                            }
                        }
                        //Log("PatrolNode[" + i + "] = {r:" + aPatrolPath[i].fRadius + 
                            //", t:" + aPatrolPath[i].fStopTimeInterval + 
                            //", s:" + MotionState.aSpeedNames[aPatrolPath[i].iSpeed] +
                            //", fn:" + aPatrolPath[i].sFunctionName + 
                            //"\n");
                    }
                }
				else
					CrashLog("Sciezka patrolowa nie istnieje\n");
				
				//TODO: dorobic obsluge innych flag, jak beda...
				aPath.ConvertToFlags(cMapStream.readInt());
            }
            catch(java.io.IOException e)
            {
                LogD("File error while reading AI map data (CHK_JAVA_AI_PATROL_POINTS_EXT)\n");
            }
        }
        else if (nChkID == CHK_JAVA_AI_PATROL_POINTS_EXT2)
        {
			// wczytanie flag punktow
            try
			{
                int nPoints = cMapStream.readInt();
                
                if (aPath != null)
                {
                    if (nPoints != aPath.GetPointsNum())
					{
                        LogD("Incorrect number of points in chunk: CHK_JAVA_AI_PATROL_POINTS_EXT2!!!\n");
						return;
					}

                    for (int i = 0; i < nPoints; i++)
                    {
						AIPatrolerPathPoint point = (AIPatrolerPathPoint) aPath.GetPoint(i);
                        point.nFlags = cMapStream.readInt();
                        //Log("PatrolNode[" + i + "].nFlags = " + aPatrolPath[i].nFlags + "\n");
                    }
                }
            }
            catch (java.io.IOException e)
            {
                LogD("File error while reading AI map data (CHK_JAVA_AI_PATROL_POINTS_EXT2)\n");
            }
        }
    }
    
    /** Podjecie akcji
     */
    void TakeAction(int nAction)
    {
        //LogD("(" + nAction + "), aPatrolPath = " + aPatrolPath + "\n");
        bActionInProgress = true;
        switch(nAction)
        {
        case _ACTION_GO_NEXT_PPOINT:
            {
                nOldCurrentNode = nCurrentNode;
                // gdy jest sciezka patrolowa to idziemy
                if ( (aPath != null) && (aPath.GetPointsNum() > 0) )
                    GoToNextPatrolPoint();
            }
            break;
            
        case _ACTION_CHANGE_DIR_TO_PPOINT:
            {
                if(aPath != null)
                    ChangeDirToPatrolPoint();
            }
            break;
            
        case _ACTION_PPOINT_WAIT_FOR:
            {
                if (aPath != null)
                    WaitInPatrolPoint();
            }
            break;
            
        default:
            super.TakeAction(nAction);
        }
    }

    /** Anuluje akcje
     */
    void CancelAction(int nAction)
    {
        if (bActionInProgress)
        {
            switch(nAction)
            {
            case _ACTION_GO_NEXT_PPOINT:
                {
                    if (nOldCurrentNode >= 0)
                        nCurrentNode = nOldCurrentNode;
                    else
                        nCurrentNode = 0;
                    StopGoTo();
                }
                break;
                
            case _ACTION_CHANGE_DIR_TO_PPOINT:
                {
                    ClearDestinationDir();  
                }
                break;
                
            case _ACTION_PPOINT_WAIT_FOR:
                {
                    DisableCallMethod( METHOD_WaitInPatrolPointDone );
                }
                break;
                
            default:
                super.CancelAction(nAction);
            }
            bActionInProgress = false;
        }
    }
    
    /** Wlacza zmiane kierunku na nastepny punkt patrolowy
     */
    void ChangeDirToPatrolPoint()
    {
        //LogD("\n");
        // pobieramy nastepny punkt patrolowy
        int nNextNode = nCurrentNode+1;
        if (nNextNode > aPath.GetPointsNum() - 1)
			nNextNode = 0;
        
        // ustawiamy kierunek chodzenia
        Vector vDir = new Vector(aPath.GetPoint(nNextNode).vPosition);
        vDir.Sub( GetPosition() );

        METHOD_DestDirReached = METHOD_ChangeDirToPatrolPointDone;
        SetFacingDestDir(vDir);
        
        // wlaczamy obracanie ciala do podanego kata
        //LogD("End\n");
    }
    
    /** Zakonczenie zmiany kierunku do punktu patrolowego */
    void ChangeDirToPatrolPointDone()
    {
        TaskFinishedQuick();
    }
    
    /** Uruchamia przejscie do nastepnego punktu patrolowego
     */
    void GoToNextPatrolPoint()
    {
        //CrashLog("DBG: [AIPatroler.GoToNextPatrolPoint] node = " + nCurrentNode + "\n");
        int nGotoFlags = _GOTO_DEFAULT & (~(_GOTO_USE_PATHFINDER | _GOTO_UPDATE_MOTION));
        boolean bUsePathFinder;
        if (nCurrentNode >= 0)
            bUsePathFinder = (aPath.GetPoint(nCurrentNode).nFlags & AIPatrolerPathPoint._PNF_NOPATHFINDER) == 0;
        else 
            bUsePathFinder = (aPath.GetPoint(0).nFlags & AIPatrolerPathPoint._PNF_NOPATHFINDER) == 0;

        if (bUsePathFinder)
            nGotoFlags |= _GOTO_USE_PATHFINDER;
        
        // przelaczamy nastepny punkt patrolowy
		if (aPath != null)
		{
			if (aPath.IsPathOneWay())
            {
                nCurrentNode++;    
                if(nCurrentNode >= aPath.GetPointsNum() - 1)
                {
                    bEndOfPathReached = true;
                    nCurrentNode = aPath.GetPointsNum() - 1;
                    nGotoFlags |= _GOTO_UPDATE_MOTION;
                }
            }
			else
			if (aPath.IsPathLooped())
            {
                nCurrentNode++;    
                if(nCurrentNode > aPath.GetPointsNum() - 1)
					nCurrentNode = 0;
            }
			else
			if (aPath.IsPathTwoWays())
            {
                nCurrentNode+=nNextSkip;
                
                if(nCurrentNode < 0)
                {
                    nCurrentNode = 0;
                    nNextSkip = 1;
                }
                
                if(nCurrentNode > aPath.GetPointsNum() - 1)
                {
                    nCurrentNode = aPath.GetPointsNum() - 1;
                    nNextSkip = -1;
                }
            }
		}
        
        AIPatrolerPathPoint cCurrentNode = (AIPatrolerPathPoint) aPath.GetPoint(nCurrentNode);
        
        fOnDestinationTolerance = cCurrentNode.fRadius;
        GoTo(cCurrentNode.vPosition, cMotion.iSpeed, "PatrolPointReached", 
             nGotoFlags);
/*        
        // ustwiamy punkt docelowy
        SetDestinationPos(cCurrentNode.vPosition);
	    SetDestinationRadius(cCurrentNode.fRadius);
        
        // wlaczamy chodzenie
        cMotion.iMoveZ = 1;
        UpdateMotion();
        
        // ustawiamy predkosc chodzenia
        SetLocalSpeed(new Vector(0,0, aMotionVelocity[cMotion.iSpeed]));
        */
    }


    /** Funkcja wolana, gdy AI dojdzie do aktualnego punktu patrolowego
     */
    void PatrolPointReached()
    {
        TaskFinishedQuick();
    	String sFnName = aPath.GetPoint(nCurrentNode).sTextField;
        if (sFnName != null)
			CallMethodOnceIn(sFnName, 0);
	}
    
    
    /** Odczekuje odpowiednia ilosc czasu w punkcie patrolowym
     */
    void WaitInPatrolPoint()
    {
        //CrashLog("DBG: time=" + aPatrolPath[nCurrentNode].fStopTimeInterval + "\n");

        UpdateMotion();
        StopGoTo();

        CallMethodIn(METHOD_WaitInPatrolPointDone, ((AIPatrolerPathPoint) aPath.GetPoint(nCurrentNode)).fStopTimeInterval);
    }
    
    void WaitInPatrolPointDone()
    {
//        LogD("\n");

        // ruszamy ponownie
//        cMotion.iMoveZ = 1;
//        UpdateMotion();

        TaskFinished();
    }

    
    public class Patrol extends AIBase.AIBaseState
    {
//        public void OnEnter()
//        {
//            super.OnEnter();
            //cMotion.iSpeed = MotionState.SPEED_WALK;
			
/*			CrashLog("aPath.nFlags:" + aPath.nFlags + "\n");
			CrashLog("aPath.nPathID:" + aPath.nPathID + "\n");
			CrashLog("aPath.sStringID:" + aPath.sStringID + "\n");
			CrashLog("aPath.GetPointsNum():" + aPath.GetPointsNum() + "\n");
			for (int i = 0; i < aPath.GetPointsNum(); i++)
				CrashLog("Point:" + i + ":" + aPath.GetPoint(i).vPosition + "\n");
*/			
//        }
        
        /** Przy wyjsciu z patrolowania, trzeba wylaczyc je
         */
        public void OnLeave()
        {
            super.OnLeave();
            //LogD("\n");
            // kasujemy wszystkie zadania dla AI dotychczasowe
            StopGoTo();
            ClearTasks();
            UpdateMotion();

//            ClearDestinationPos();
            ClearDestinationDir();
        }
        
        /** Podjecie decyzji co robic
         */
        public void OnMakeDecision()
        {
            // sprawdzamy czy AI doszlo do konca sciezki
            if(!bEndOfPathReached)
            {
            
                AIPatrolerPathPoint cCurrentNode = (AIPatrolerPathPoint) aPath.GetPoint(nCurrentNode);
            
                if (cCurrentNode.fStopTimeInterval != 0)
                    NewTask(_ACTION_PPOINT_WAIT_FOR);
                //NewTask(_ACTION_CHANGE_DIR_TO_PPOINT);

                if (cCurrentNode.iSpeed != cMotion.iSpeed)
                {
                    // zmieniamy sposob poruszania sie
                    cMotion.iSpeed = cCurrentNode.iSpeed;
                    NewTask(_ACTION_CHANGE_MOTION);
                }

                NewTask(_ACTION_GO_NEXT_PPOINT);
            }
            else
                NewTask(_ACTION_THINK);
        }
        
        /** Podjecie decyzji co robic przy wejsciu do stanu
         */
        public void OnMakeDecisionEntering()
        {
            //Log("[AIPatroler.OnMakeDecisionEntering]\n");
            //NewTask(_ACTION_CHANGE_DIR_TO_PPOINT);
            if (nCurrentNode >= 0)
                OnMakeDecision(); // kontynuacja patrolowania
            else
            {
                cMotion.iSpeed = ((AIPatrolerPathPoint)aPath.GetPoint(0)).iSpeed;
                NewTask(_ACTION_GO_NEXT_PPOINT);
            }
        }

        
        /** Koniec zmiany typu chodzenia
         */
        void OnChangeMotionType()
        {
            //LogD("###\n");
            TaskFinishedQuick();
        }
    }
    
}
