/*
    This file is part of Sight code generator system for data-mining robots.
    Sight code generator system for data-mining robots 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
    (at your option) any later version.

    Foobar 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 Foobar; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA or
    try URL http://www.gnu.org/licenses/.

 * @institution Ulm university
 * @version 2.1.0
 * @copyright Ulm university, Frank Lehmann Horn,
 * Karin Jurkat-Rott and Audrius Meskauskas. There four
 * authors and copyright holders are independent and each of them has
 * right to release subsequent versions of this software
 * under any chosen license and without permission from others.
 * Released under General Public License (GPL).
 * @programmer Dr Audrius Meskauskas
 */

package Sight.Agents;

import Sight.io.*;
import Sight.io.GUI.*;
import Sight.Agents.util.*;

/** Used to run some agent request in a separate thread. */
public class Request extends Thread
 {
   /** Result, is zero at the beginning, then no null, is a result.
    *  To allow different results, this has an Object type. */
   public Object result = null;

   /** Request.
    *  To allow different results, this has an Object type. */
   public Object q = null;

   /** key for this request to get result from the cache later.
    *  Can be null, then the cache is not used.
    */
   public String key = null;

   /** True if search was not successful. */
   public boolean err = false;

   /** True if task is finished. */
   public boolean end = false;

   public Agent agent;

   private Request()
    {
      //Recycler.Recycled.register(this); // Register with memory leak detector.
    };

   public Request(Agent _agent, Object _q, String _key)
    { this();
      agent = _agent;
      q = _q;
      result = null;
      err = false;
      end = false;
      key = _key;
      result = null;
    };

   public String getId()
    { return key;
    };

   public void run()
    {
      String kn = key;
      String nn = agent.getAgentName();
      try {
      if (kn == null) kn = "";
      if (kn.length()>30) kn = kn.substring(0,30);
      agent.p("Query "+
      agent.getClass().getName()+" started in separate thread.");
      Pind.setAgent(agent);
      Pind.Net(nn,Pind.PROCESSING, kn);
      try {
      result = agent.go(q, key);
      //System.out.println(nn+" Result "+result);
          } catch (Exception exc)
             { result = null;
               err = true;
               end = true;
               agent.p("Query "+
                 agent.getClass().getName()+" finished with errors. ");
               Output.e(exc);
               Pind.Net(nn,Pind.BUG, kn);
               return;
             };
      agent.p("Query "+
      agent.getClass().getName()+" ready.");
      Pind.Net(nn,Pind.I, kn);
      end = true;
      }
        catch (java.lang.ThreadDeath de)
         {
            agent.p("The task was killed by user.");
            end = true;
            err = true;
            result = null;
            //System.out.println("Stop-Kill task message catchd");
            Pind.Net(nn, Pind.STOP, "Stopped "+kn);
         }
        catch (Sight.Agents.Signals.KillTaskMessage kim)
         {
            agent.p("The task was killed by user.");
            end = true;
            err = true;
            result = null;
            //System.out.println("Kill task message catchd");
            Pind.Net(nn, Pind.STOP, "Killed "+kn);
          }
        catch (Throwable e)
         { end = true;
           err = true;
           result = null;
           e.printStackTrace();
         }
    };


   /** How long it is worthwile to wait for the results from this thread.
    *  Default value is set 2/3 from Main.ProcessMethodLimit on the constructor call.
    */
   public long AgentTimeOut = 42* 60*1000;

   /** Return the result of this search. If the task is not finished, b
    *  locks the execution for at most AgentTimeOut time. Then it returns null,
    *  but request thread remains alive and can write value to the cache when
    *  it finally finishes (if finishes).
    */
   public Object getResult()
    {
      return getResult(AgentTimeOut);
    };

   /** Return result, but dont block the execution for longer than waitAtMost_msec.
    *  (returns null if there is no result available after that time).
    */
   public Object getResult(long waitAtMost_msec)
    {  if (err) return null;

       if (result!=null && !err)
        return result;

      String wId = agent.getClass().getName();
      if (result == null)
         agent.p("Query "+ wId +" still not finished, waiting.");

      Pind.Net("still not finished ",Pind.INFO_WAITING,
       agent.getClass().getName());

      /*
      if (Pind.Frame!=null && !Pind.ConsoleClosed)
       { Pind.Frame.setIconImage(Pind.hhh[Pind.INFO_WAITING].getImage());
         Pind.Frame.setTitle(wId);
       };
      */

      int SleepTime = 10;
      long st, ct;
      st = System.currentTimeMillis();

      // get the thread info label:
      ThreadInfoLabel info = Pind.getThreadLabel(this);
      //if (info==null) System.out.println(this.getName()+" no label");
        // this is only !null if GUI is active in general.
      while (!end)
       {
          if (info!=null)
           {
           if (info.abandon)
            {
              err = true;
              info.abandon = false;
              info.setText2("Abandoned.");
              info.setIcon(Pind.ABANDON);
              Sight.io.Output.a("Thread abandoned by user command.");
              return null;
            }
           };
          if (err) return null;
          try
          {
            Sight.util.Sleeper.Sleep(SleepTime);
            SleepTime = SleepTime*2;
            if (SleepTime>1000) SleepTime = 1000;

            ct = System.currentTimeMillis();

            if ((ct-st)>waitAtMost_msec)
             { Output.e("Request for "+agent.getAgentName()+" lasted for so long. Null returned.");
               info.setText2("Abandoned, too long");
               info.setIcon(Pind.ABANDON);
               end = true;
               return null;
             };
          }
          catch (InterruptedException ex)
          {
          }
       };
       return result;
    };

    /** Creates and starts a new request, based on the q query. */
    public static Request submit(Agent _agent, Object _q, String key)
     {
       Request bt = new Request(_agent, _q, key);
       bt.start();
       return bt;
     };


 };