import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import java.awt.Image;



public class Soldier extends MobileSimObject {
    // OVERVIEW: A Soldier is a battle simulator object that performace a walk,
    //    moving one square in a direction toward closest enemy and attacks
    //  enemies when can. Soldier dies when life is less then 0

    private boolean inTurn; // Currently in the middle of a turn (used for coloring)
    public boolean fight;
    public Color color;
    private int health;
    private int speed;
    private int damage;
    private int exp;
    private int attacknum;
    
    
    
    public Soldier () {
	inTurn = false;
        fight=false;
        health=50;//default 
        speed=300;//default
        damage=5;//default
        exp=1;//default
        attacknum=1;//default
               
    }
    
    public Soldier (int row, int col) {
	inTurn = false;
        fight=false;
        health=50;//default 
        speed=300;//default
        damage=5;//default
        exp=1;//default
        attacknum=1;//default
        mrow=row;
        mcol=col;
               
    }
    
    public String getType()
    {   //Effects: returns string of type of soldier
     return "Soldier";   
    }
    
    public void setTeam(Color c1)
    {    //Effect:  A team color is given to a soldier

     color=c1;   
    }
    
     public void setHealth(int c1)
    {//Effects: An health status is assigned to a soldier.

     
     health=c1;   
    }
    
     public void setSpeed(int num)
    {//Effects:  Sets a speed to the soldier.

     
     speed=num;   
    }
    
    public void setDamage(int num)
    { //Effects: Sets the amount of damage a soldier has.

     
     damage=num;   
    }
    
    public void setExp(int num)
    {
     //Effects:  Sets the amount of attacking experience.

     exp=num;   
    }
    
    public void setAttacknum(int num)
    {  //Effects:  Sets the number of attacks made.

     speed=num;   
    }
    
    public void setFight(boolean bol)
    {    //Effects:  A soldier fights an opposing soldier.

     
     fight=bol;   
    }
    
    
    public Color getTeam()/*@non_null@*/
    {    //Effects:  Returns the color for a team.

     
     return color;   
    }//@nowarn NonNullResult // ESC/Java doesn't know Color constants are not null


    public Color getColor()
    {    //Effects:  Returns the color of soldier or white based on in motion

	if (inTurn) {
	    return Color.white;
	} else {
	    return color;
	}
    } //@nowarn NonNullResult // ESC/Java doesn't know Color constants are not null
    
     public int getSpeed()
    {   //Effects:  Returns the number of speed.
     return speed;   
    }
    
     public int getHealth()
    {   //Effects:  Returns soldier's Health Status.
     return health;
    }
    
    public int getDamage()
    {   //Effects:  Returns soldier's weapon damage.
     return damage;   
    }
    
    public int getExp()
    {    //Effects:  Returns level of experience. 
     return exp;   
    }
    
    public int getAttacknum()
    {   //Effects:  Returns number of maxium number attacks.
     return attacknum;   
    }
    
    public boolean getFight()
    {   //Effects:  if soldier is fighting return true else false.
          return fight;   
    }
   
    
    public boolean sameTeam(Soldier ss1)
    {    //Effects:  Returns true if soldier is on the same team, otherwise it returns false.
        
        if(ss1==null)
            return false;
     
       if(getTeam().getRed()!= ss1.getTeam().getRed())
            return false;
       
       if(getTeam().getBlue()!= ss1.getTeam().getBlue())
            return false;
          
       if(getTeam().getGreen()!= ss1.getTeam().getGreen())
            return false;
      
      return true;
     
    }//@nowarn NonNullResult // ESC/Java doesn't know Color constants are not null
    
    public void drawBody(Graphics g,int x, int y,int w, int h, double rat)
     //@requires g!=null;
    {   //Requires: x,y,w,h are positive g!=null
        //Effects:  Displays body for a soldier.  
        
        int x2=x;int y2=y;
         x2=x+w/3;
        y2=y+h/3;
        
        g.setColor(getTeam());
        g.fillRect(x2,y2,w/3,h/2);
        g.setColor(getColor());
        g.drawRect(x2,y2,w/3,h/2);
       
        drawHead(g,x2,y2,w,h,rat);
    }
    
    
    
    public void drawHead( Graphics g,int x, int y,int w, int h, double rat)
    //@requires g!=null;
    {   //Requires: x,y,w,h are positive g!=null
        //Effects:  Displays head for a soldier. 
        
         int x2=x;int y2=y;
      
        g.setColor(Color.pink);
        y2-=h/3;
        
        g.fillOval(x2,y2,w/3,h/3);
        g.setColor(Color.orange);
        g.drawOval(x2,y2,w/3,h/3);
        g.setColor(getTeam());
         g.setColor(getTeam());
        g.fillArc(x2-=(int)(2*rat),y2,w/3+(int)(4*rat),h/3+(int)(4*rat),10,180);
  
    }
    
    synchronized public int Hurt(int dam,int ex)
    
    {   //Requires: dam>=0, 
        //Modifies: this's health.
        //Effects:  Lesses Soldiers health by damage and experience
        //and returns amount of total damage.
        
      
     int power=2;
     if(ex>0)
     power=dam-getRandom().nextInt (dam/ex+(1))+exp;
        
     setHealth(getHealth()-power);   
     
      if(getHealth()<0)
      {     Died();
            getGrid().killObjectAt(mrow,mcol);
      }
     
     return power;
        
    }
    
    
    public Vector toAttack()
    {   //Effects: returns vector of all neighboring enemies one away
        
        Vector els = getVecNeighbors();
        Vector att = new Vector();
        if(els!=null)
        {
         for(int i=0;i<els.size();i++)
         {
         Soldier ss1 = (Soldier)els.get(i);  
           
         if(!sameTeam(ss1))
         {
          att.add(ss1);   
          
         }   
         }
            
        }
        return els;
    }
       
    public boolean Attack()
    {    
        //Effects: if attacks/hurts maxium number (based on this) of enemies 
        //in neighboring cell returns true, else false
    
    Vector att = toAttack();
    Soldier ss1;
    
    boolean tcc=false;
    
    if((att==null)||(att.size()<1))
        fight=false;
    
    if(att!=null)
        if(att.size()>0)
            for(int i=0;i<att.size();i++)
            {
                fight=true;
                ss1=(Soldier)att.get(i);
                
                if(ss1==null)
                    return false;
                
            if(!sameTeam(ss1)&&(ss1.getHealth()>=0))
            {
                tcc=true;
              int power = ss1.Hurt(getDamage(),getExp());
              System.out.println( toString() + "\t" + "Attacked " +"\t"+ ss1.toString() + "\t for damage:" + power  );
             
            }
                
             if(i >= getAttacknum())
                 break;
            }
    
            if(!fight)
                exp++;
    
                fight=tcc;
        return tcc;
        
    }
    
    public int Distance(Soldier ss1)
    {   //Requires: ssl can't be null and on grid
        //Effects: returns number of square moves from this to ss1  
        
        if(ss1==null)
            return 0;
        
     int x = getRow()-ss1.getRow();   
     int y = getColumn()-ss1.getColumn();   
     
     if(x<0)
         x*=(-1);
     
     if(y<0)
         y*=(-1);
     
        return(x+y);
    }
    
    public Soldier Locate()
    {   // if enemy soldiers returns closest enemy in arbitrary order
        // else no ememies means team on board has won game and exits program
       
     Vector all = getGrid().getObjects();
     Vector enemy =  new Vector();
     Soldier ss1 = new Soldier();
     
     if(all==null)
         return ss1;
     
     for(int i=0;i<all.size();i++)
     {
         ss1=(Soldier) all.get(i);
         if(!sameTeam(ss1))
             enemy.add(all.get(i));
     }
     
     if(enemy.size()<1)
     {    System.out.println(toString()+ " is Victorious");
        System.out.println("The Enemies are crushed");
        System.out.println("Gameover");
     
        delay(2000);
        System.exit(1);
     }
     
     Soldier closest =(Soldier) enemy.get(0);
     int low = Distance(closest);
     
     for(int i=0;i<enemy.size();i++)
     {
     ss1=(Soldier) enemy.get(i);
               
     if(Distance(ss1)<low)
     {
      closest = ss1;
      low=Distance(ss1);
     }
         
     }
      
     return closest;
  }
     
    public void drawMan(Graphics g,int x, int y,int w, int h)
    {   //Effects:  if g!=null Draws a whole soldier on graph

        if(g==null)
            return;
       
        double rat=w/50;
        //rat is a the ratio of square width to a orginal squarewidth 
        //used to form graphics: allows squarewidth to change without
        //having to change all additions/subtractions

        drawBody(g,x,y,w,h,rat);
                    
    }
 
    public void Died()
    {   //Modifies: this
        //Effect: outputs soldier death and pauses thread
        
     System.out.println(toString()+ " has died");
     pauseObject();
    }
    
    
    public String teamToString()
    {   //Effect: returns string of soldier team
        
    Color cl=getTeam();
    int cc=0;
    
    if(cl==Color.red)
    {   return "Red";}
    else if(cl==Color.white)
    {   return "White";}
    else if(cl==Color.blue)
    {   return "Blue";}
    else if(cl==Color.black)
    {   return "Black";}
    else if(cl==Color.magenta)
    {   return "Purple";}
    else if(cl==Color.orange)
    {   return "Orange";}
    else if(cl==Color.green)
    {   return "Green";}
         
    return " some Team";    
    }
    
    public String toString()
    {   //Effect: returns string of soldier team and type and health
        
    return ( teamToString() + "\t" + getType() + " H= " + getHealth());    
    }
    
    public void MoveTo() throws RuntimeException
    {   //Effects: if valid moves toward closest enemy first row, then columns
        //else sats in position
      
          synchronized(getGrid())
          {
        
        Soldier enemy = (Soldier) Locate();
        
        if((enemy == null))
            return;
        
        
        int r = enemy.getRow(); int c = enemy.getColumn();
        int dr = r - getRow(); int dc = c - getColumn();
        
        Direction dir = Direction.MoveToEnemy (dc,dr);
        
        if(dir==null)
            return;       
        
        int newrow = getRow()  + dir.northerlyDirection ();
        int newcol = getColumn ()  + dir.easterlyDirection ();
        
        
        
        if (getGrid ().validLocation (newrow, newcol))  {
            if (getGrid().isSquareEmpty (newrow, newcol))
            {
                // Here, we pretend there is a lot of compution to do by sleeping,
                // to make race conditions more likely.
               
                
                setLocation (newrow, newcol);
                //enemy.toAttack();
                
            }
        }
        
    }
  
    }

  
    public void executeTurn() throws RuntimeException
       // Note: requires isInitialized is inherited from SimObject
       // EFFECTS: if enemy one away attacks first, closest enemy with for damage  
       // if not attacking Picks a direction closer to enemy and tries to move that way. 
       //          If the move is successful, return true. If the move fails
       //          because the spot being attempted to move into is already
       //          occupied then return false.
       //          then delays for given speed

    {
        
        if(getHealth()<0)
        {    getGrid().killObjectAt(mrow,mcol);
        return;
        }
         inTurn = true;
      
        if(!  Attack())  
            MoveTo();
        
         delay (getSpeed());
        inTurn = false;
     }
}