/*$$
 * packages uchicago.src.*
 * Copyright (c) 1999, Trustees of the University of Chicago
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with 
 * or without modification, are permitted provided that the following 
 * conditions are met:
 *
 *	 Redistributions of source code must retain the above copyright notice,
 *	 this list of conditions and the following disclaimer.
 *
 *	 Redistributions in binary form must reproduce the above copyright notice,
 *	 this list of conditions and the following disclaimer in the documentation
 *	 and/or other materials provided with the distribution.
 *
 *	 Neither the name of the University of Chicago nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Nick Collier
 * nick@src.uchicago.edu
 *
 * packages cern.jet.random.*
 * Copyright (c) 1999 CERN - European Laboratory for Particle
 * Physics. Permission to use, copy, modify, distribute and sell this
 * software and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice appear in
 * supporting documentation. CERN makes no representations about the 
 * suitability of this software for any purpose. It is provided "as is" 
 * without expressed or implied warranty. 
 *
 * Wolfgang Hoschek
 * wolfgang.hoschek@cern.ch
 *$$*/

package WealthModel;

import WealthModel.*;
import cern.jet.random.Uniform;
import cern.jet.random.Normal;
import uchicago.src.sim.gui.*;

import java.lang.Math;


// If an agent, or any other object is to be displayed in a display it
// needs to implement at least the drawable interface which has one method:
// draw(SimGraphics g)
public class WealthAgent implements Drawable {

  int x, y;                  // my coordinates
  double wealth;                // amount of wealth I own
  double smart = 0.0;      // how much smarter am I?
  int age;                 // how old I am
  int maxAge;              // how old can I get?

  private WealthSpace space;
  private WealthModel model;

  public WealthAgent(WealthSpace ss, WealthModel model) {
    space = ss;
    this.model = model;
  }

  public void setXY(int x, int y) {
    this.x = x;
    this.y = y;
  }

  public int getX() {
    return x;
  }

  public int getY() {
    return y;
  }

  // Just as the model needs get and set accessor methods in order for
  // its initial parameters to be displayed and thus subject to modification,
  // an agent (or any other object) can be probed through similar get and set
  // methods.
  //
  // Probing consists of clicking on an object in the display, causing that
  // object's current state to be displayed. What is displayed depends on
  // the various get and set methods implemented by the object. For example,
  // if an object has a setMetabolism and a getMetabolism method, a Metabolism
  // field will be displayed providing the current value of the metabolism
  // variable and allowing the user to change the value by entering a new value
  // and pressing enter. As of this release only number, String, and boolean
  // fields can be displayed. Of course a user can use the get and set methods
  // to turn a Vector, for example, into a String or whatever is appropriate.

  public void setWealth(double wealth) {
    this.wealth = wealth;
  }

  public double getWealth() {
    return wealth;
  }
  
  public void setAge(int age) {
    this.age = age;
  }

  public int getAge() {
    return age;
  }
  
  public void setSmart(double smartness) {
	  this.smart = smartness;
  }
  
  public double getSmart() {
	  return smart;
  }
  
  public void setMaxAge(int maxAge) {
    this.maxAge = maxAge;
  }

  public int getMaxAge() {
    return maxAge;
  }

  // step() is called by the scheduler once per tick.
  public void step() {
  
  if (WealthModel.Sync) {
    if (wealth < WealthModel.SyncMax)
        wealth = wealth + 1;
    else if (!WealthModel.SyncRegion) {
        for(int i = 0; i < WealthModel.agentList.size(); i++) {
            WealthAgent agent = (WealthAgent)WealthModel.agentList.get(i);
            if (agent.getWealth() > WealthModel.SyncLatency)
                // agent.setWealth(agent.getWealth() + 0.03); // 0.05
				agent.setWealth(agent.getWealth() + WealthModel.SyncIncrement);
        }
        wealth = 0.001;
    }
    else {
        for (int dX = -1*WealthModel.RegionSize; dX <= WealthModel.RegionSize; dX++)
            for (int dY = -1*WealthModel.RegionSize; dY <= WealthModel.RegionSize; dY++)
                if ((dX != 0) || (dY != 0)) {
                    int newX = (x + dX) % space.getXSize();
                    int newY = (y + dY) % space.getYSize();
                    WealthAgent agent = WealthModel.getAgentAt(newX, newY);
                    if (agent != null)
                        if (agent.getWealth() > WealthModel.SyncLatency)
                            if (WealthModel.Moving)
                                // agent.setWealth(agent.getWealth() + 0.925); // 1.25
								agent.setWealth(agent.getWealth() + WealthModel.SyncIncrement);
                            else
                                // agent.setWealth(agent.getWealth() + 0.925); // 1.45
								agent.setWealth(agent.getWealth() + WealthModel.SyncIncrement);
                }
        wealth = 0.001;
    }
    if (WealthModel.Moving) {
        int dX = Uniform.staticNextIntFromTo(-1, 1);
        int dY = Uniform.staticNextIntFromTo(-1, 1);
        int newX = (x + dX + space.getXSize()) % space.getXSize();
        int newY = (y + dY + space.getYSize()) % space.getYSize();
        WealthAgent agent = WealthModel.getAgentAt(newX, newY);
        if (agent == null)
            WealthModel.moveAgent(this, newX, newY);
    }

  }
      
  else {
  
  if (WealthModel.ROI || WealthModel.Both) {
       /* double r = Normal.staticNextDouble(WealthModel.ROIMean, WealthModel.ROIStdDev);
       wealth = wealth * (1 + r);
	  /**/
	  
	  double r = Normal.staticNextDouble(WealthModel.ROIMean - (WealthModel.ROIStdDev * WealthModel.ROIStdDev) / 2.0,
										 WealthModel.ROIStdDev);
	  
	   wealth = wealth * Math.exp(r + smart);
       /**/
  }
  
  if (WealthModel.Moving && (WealthModel.Exchange || WealthModel.Both)) {
    int dX = Uniform.staticNextIntFromTo(-1, 1);
    int dY = Uniform.staticNextIntFromTo(-1, 1);
    int newX = (x + dX + space.getXSize()) % space.getXSize();
    int newY = (y + dY + space.getYSize()) % space.getYSize();
    WealthAgent agent = WealthModel.getAgentAt(newX, newY);
    if (agent != null) {
      if (wealth >= 1.0) {
        agent.setWealth(agent.getWealth() + 1.0);
        wealth = wealth - 1.0;
      }
    }
    else
      WealthModel.moveAgent(this, newX, newY);
  
  }
  if (WealthModel.RandomWalk) {
      if (wealth < 1)
        wealth = 1;
      else {
        int i = Uniform.staticNextIntFromTo(0, 1);
        if (i == 0) {
          wealth = wealth - 1.0;
        }
        else
          wealth = wealth + 1.0;
      }
    }
 if ((WealthModel.Exchange || WealthModel.Both) && (wealth > 1.0) && !WealthModel.Moving) {
       int i = Uniform.staticNextIntFromTo(0, WealthModel.agentList.size() - 1);
       WealthAgent agent = (WealthAgent)WealthModel.agentList.get(i);
       agent.setWealth(agent.getWealth() + 1.0);
       wealth = wealth - 1.0;
  }
  if (WealthModel.DeathTax) {
    age = age + 1;
    if (age >= maxAge) {
      age = 0;
      double currentWealth = wealth;
      while (wealth > (((100 - WealthModel.TaxPCT) * currentWealth) / 100)) {
        int i = Uniform.staticNextIntFromTo(0, WealthModel.agentList.size() - 1);
        WealthAgent agent = (WealthAgent)WealthModel.agentList.get(i);
        agent.setWealth(agent.getWealth() + 1);
        wealth = wealth - 1;
      }
    }
  }
  if ((WealthModel.Both || WealthModel.Exchange)
        && (wealth < WealthModel.MinWealth))
	  wealth = WealthModel.MinWealth;
  }
 }

  // drawable implementation
  // draw() is called whenever the display is updated, assuming the agent
  // is part of what is being displayed.
  public void draw(SimGraphics g) {
    if (wealth > 250)
      g.drawFastRoundRect(java.awt.Color.magenta);
    else if (wealth > 200)
      g.drawFastRoundRect(java.awt.Color.pink);
    else if (wealth > 150)
      g.drawFastRoundRect(java.awt.Color.red);
    else if (wealth > 125)
      g.drawFastRoundRect(java.awt.Color.orange);
    else if (wealth > 100)
      g.drawFastRoundRect(java.awt.Color.yellow);
    else if (wealth > 75)
      g.drawFastRoundRect(java.awt.Color.green);
    else if (wealth > 50)
      g.drawFastRoundRect(java.awt.Color.cyan);
    else if (wealth > 40)
      g.drawFastRoundRect(java.awt.Color.blue);
    else if (wealth > 30)
      g.drawFastRoundRect(java.awt.Color.lightGray);
    else if (wealth > 20)
      g.drawFastRoundRect(java.awt.Color.gray);
    else if (wealth > 10)
      g.drawFastRoundRect(java.awt.Color.darkGray);
    else
      g.drawFastRoundRect(java.awt.Color.black);

  }
}
