import java.util.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

public class Philosopher extends JLabel {
    private DiningPhilosophers parent;
    private int position;
    private double angle;
    public PhilWorker philWorker = null;

    // http://javazquez.com/juan/2008/07/11/rotating-java-images/
    static private ImageIcon rotateImageIcon(ImageIcon picture, double angle) {
        int w = picture.getImage().getWidth(null);
        int h = picture.getImage().getHeight(null);
        BufferedImage bi = new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
        Graphics2D bg = bi.createGraphics();
        bg.setColor(Color.white); 
        bg.fillRect(0,0,w,h);
        bg.rotate(angle, w/2, h/2);
        bg.drawImage(picture.getImage(), 0, 0, w, h, 0, 0, w, h, null);
        bg.dispose(); //cleans up resources
        return new ImageIcon(bi);
    }
    
    private int left() {
        return position == 0? parent.NUMPHILS-1: position-1;
    }

    private int right() {
        return position;
    }

    private boolean even() {
        return position % 2 == 0;
    }

    private boolean odd() {
        return position % 2 != 0;
    }
    
    private int first() {
        return parent.DEADLOCK || even()? right(): left();
    }

    private int second() {
        return parent.DEADLOCK || even()? left(): right();
    }

   private Chopstick firstStick() {
        return parent.chopsticks[first()];
    }
        
    private Chopstick secondStick() {
        return parent.chopsticks[second()];
    }
        
    private Dukes firstStickDuke() {
        return parent.DEADLOCK || even()? Dukes.RIGHTSTICKDUKE: Dukes.LEFTSTICKDUKE;
    }
    
    private Dukes secondStickDuke() {
        return parent.DEADLOCK || even()? Dukes.LEFTSTICKDUKE: Dukes.RIGHTSTICKDUKE;
    }
    
    public Philosopher(DiningPhilosophers parent, int position, double angle) {
        super(parent.names[position], 
              rotateImageIcon(parent.imgs.get(Dukes.NOSTICKSDUKE), angle), 
              JLabel.CENTER);

        this.parent = parent;
        this.position = position;
        this.angle = angle;

        setVerticalTextPosition(JLabel.BOTTOM);
        setHorizontalTextPosition(JLabel.CENTER);
    }
    
    public void start() {
         philWorker = new PhilWorker();
         philWorker.execute();
    }

    public void interrupt() {
         philWorker.cancel(true);
         philWorker = null;
    }

    private int period(int low, int high) {
         return (int) (low + (high-low) * Math.random()) * parent.grabDelay;
    }
    
    class PhilWorker extends SwingWorker <Object, Update> { //<Object,Update>
        @Override
        public Object doInBackground() {
             try {
                while (! isCancelled()) {
                    // THINK
                    System.err.format("Philospher %d thinks%n", position, first());
                    try {
                        updatePhilosopher(Dukes.NOSTICKSDUKE, "Think...", Color.black);
                    } catch (Exception e) {
                        break;
                    }
                    Thread.sleep(2*period(1, 2));
                    
                    // HUNGER
                    System.err.format("Philospher %d hungry%n", position, first());
                    try {
                        updatePhilosopher(Dukes.NOSTICKSDUKE, "Hungry", Color.RED);
                    } catch (Exception e) {
                        break;
                    }
                    Thread.sleep(period(1, 2));
                    
                    // HUNGER AND FIRST STICK
                    firstStick().grab();
                    System.err.format("Philospher %d grabs Stick %d%n", position, first());
                    try {
                        updatePhilosopher(firstStickDuke(), "Hungry", Color.RED);
                    } catch (Exception e) {
                        break;
                    }
                    Thread.sleep(period(1, 2));
                    
                    // BOTH STICKS AND EAT
                    secondStick().grab();
                    System.err.format("Philospher %d grabs Stick %d and eats%n", position, second());
                    try {
                        updatePhilosopher(Dukes.BOTHSTICKSDUKE, "Yum-Yum", Color.green);
                    } catch (Exception e) {
                        break;
                    }                               
                    Thread.sleep(3*period(1, 2)); 
                    
                    // RELEASE SECOND STICK
                    secondStick().release();
                    System.err.format("Philospher %d releases Stick %d%n", position, second());
                    try {
                        updatePhilosopher(firstStickDuke(), "Good!", Color.blue);
                    } catch (Exception e) {
                        break;
                    }
                    Thread.sleep(period(1, 2)); 
                    
                    // RELEASE FIRST STICK
                    firstStick().release();
                    System.err.format("Philospher %d releases Stick %d%n", position, first());
                }
            
            } catch (InterruptedException e) {
                System.err.format("Philospher %d was interrupted%n", position);
            } catch (Exception e) {
                System.err.format("Philospher %d raised an exception %s%n", position, e.getMessage());
            }

            firstStick().releaseIfMine();
            secondStick().releaseIfMine();
                        
            try {
                updatePhilosopher(Dukes.NOSTICKSDUKE, parent.names[position], Color.black);
            } catch (Exception e) {
                System.err.format("Couldn't update icon for %d%n", position);
            }
            
            return null;
        }
        
        void updatePhilosopher(Dukes icon, String text, Color color) throws Exception {
            publish(new Update(icon, text, color));
        }
        
        @Override
        public void process(java.util.List<Update> updates) {
            for (Update up: updates) {
              setIcon(rotateImageIcon(parent.imgs.get(up.icon), angle));
              setText(String.format("%d: %s", position, up.text));
              //setBackground(up.color);
              setForeground(up.color);
            }
        }
    }      
    
    class Update {
        public Dukes icon; 
        public String text;
        public Color color;
        public Update(Dukes icon, String text, Color color) {
            this.icon = icon;
            this.text = text;
            this.color = color;
        }
    }
    
}
