package gUI;

/**
 * Chapter 7: PirateNinjaBoxerPanel.java Creates the panel to be placed inside
 * the BallApp window. Used (with modifications) in all programs later in this
 * book. Version 3 of 3
 */
//Modified by Alexander James Ehwalt Ryan since November 5, 2008, to emulate Pong.

//Modified again by Alexander James Ehwalt Ryan to bring about a RTS style game in which one is to avoid getting seen by the enemy while trying to get to the end of the level, 
//indicated by a cyan square. The user is blocked by many things, mostly enemies and objects.

//Modified again by Alexander James Ehwalt Ryan to provide a grid instead that later on units can move along,
//in a TBS style akin to Fire Emblem - but with the capacity to act along more than just the x/y axis.

import java.applet.AudioClip;
import java.awt.*;
import coords.CharaPath;
import coords.Coords;
import java.awt.event.*;

import scenery.Scenery;
import unit.*;
import bounds.Bounds;
import coords.DCoords;
import java.util.Observable;
import java.util.Observer;
import sounda.Sound;

public class PirateNinjaBoxerPanel extends javax.swing.JPanel implements Mover, KeyListener, MouseListener, MouseMotionListener, Observer {

    /**
     *
     */
    private static final long serialVersionUID = 3596988778581506192L;
    //Just a workaround so we can get around an oversight in old design, we're going to reference a static copy.
    //TODO: Eventually refactor the code so that this isn't neeeded
    public static PirateNinjaBoxerPanel aPa = null;
    private final int INIT_X = 100; // attributes
    private final int INIT_Y = 100;
////	private final int DIAMETER = 20;
    private final int INTERVAL = 100;
    public int cameraScroll = 10;
    public int farRight, farDown;
    public Camera viewer;
//	public Enemy number1;
    public Unit currentUseUnit; // components
    public Unit[] heroes;
    public Enemy[] numbers;
//	public Scenery tscene;
    public Scenery[] barriers;
    public MoveTimer _timer;
    //private Image bg;
    public Font text = new Font(null, Font.BOLD, 18);
    private boolean gameover = false;
    private boolean complete = false;
    public FortressSneaker _frame;
    public AudioClip aC;
    public int level;
    public Bounds bounda;
    public CombatModel obsA;
    public Coords axesShow = new Coords(0, 0, 0, 1);

    //Old constructor - until confirming never needed, keep just for restoring sake.    
    public PirateNinjaBoxerPanel(FortressSneaker framea, int levela) {
        super();
        _frame = framea;
        _frame.setTitle("Fortress Sneaker Level " + levela);
        this.setDoubleBuffered(true);
        _timer = new MoveTimer(INTERVAL, this);
        this.setSize(400, 400);
        level = levela;
        this.farRight = 600 * level;
        this.farDown = 600 * level;
        this.bounda = new Bounds(new Coords(this.farRight - 100, this.farDown - 100), 100, 100);
        heroes = new Unit[1];
        numbers = new Enemy[(int) Math.pow(2, level)];
        barriers = new Scenery[(int) Math.pow(2, level)];
        viewer = new Camera(new Coords(0, 0), this);
        Coords something = new DCoords(10, 20, 0, 1, new DCoords(INIT_X, INIT_Y, 0, 1, new Coords(0, 0, 0, 1)));
        //currentUseUnit = new Pirate(new Coords(INIT_X, INIT_Y*4), this);
        currentUseUnit = new Unit(something, this, "a", "b");
        heroes[0] = currentUseUnit;

//        for (int a = 0; a < barriers.length; a++) {
//            double squareOrEllipse = Math.random();
//            double randomX = Math.random() * this.farRight - 100;
//            while (randomX <= INIT_X) {
//                randomX = Math.random() * this.farRight - 100;
//            }
//            double randomY = Math.random() * this.farDown - 100;
//            if (squareOrEllipse <= .5) {
//                barriers[a] = new Scenery("Square", new Coords(randomX, randomY), this);
//            } else if (squareOrEllipse > .5) {
//                barriers[a] = new Scenery("Ellipse", new Coords(randomX, randomY), this);
//            }
//
//        }
//        for (int a = 0; a < numbers.length; a++) {
//            double randomX = Math.random() * this.farRight - 100;
//            double randomY = Math.random() * this.farDown - 100;
//            double randomX2 = Math.random() * this.farRight - 100;
//            double randomY2 = Math.random() * this.farDown - 100;
//            CharaPath toss = new CharaPath(new Coords(randomX, randomY), new Coords(randomX2, randomY2), currentUseUnit);
//            toss.checkPath(currentUseUnit, barriers);
//            while (toss.getPath().length > 50) {
//                randomX2 = Math.random() * this.farRight - 100;
//                randomY2 = Math.random() * this.farDown - 100;
//                toss = new CharaPath(new Coords(randomX, randomY), new Coords(randomX2, randomY2), currentUseUnit);
//                toss.checkPath(currentUseUnit, barriers);
//            }
//            numbers[a] = new Gunner(new Coords(randomX, randomY), new Coords(randomX2, randomY2), this);
//        }
        this.setBackground(java.awt.Color.DARK_GRAY);
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        aC = (AudioClip) (Sound.getSound("pkmz.wav"));
        //aC.loop();
        //_timer.start();
        this.repaint();

    }

    public PirateNinjaBoxerPanel(FortressSneaker framea, int levela, CombatModel obs) {
        super();
        _frame = framea;
        //_frame.setTitle("Fortress Sneaker Level " + levela);
        this.setDoubleBuffered(true);
        _timer = new MoveTimer(INTERVAL, this);
        obsA = obs;
        obs.addObserver(this);

        this.setSize(400, 400);
        level = obsA.level;
        this.farRight = obsA.farRight;
        this.farDown = obsA.farDown;
        this.bounda = new Bounds(new Coords(this.farRight - 100, this.farDown - 100), 100, 100);

//        numbers = new Enemy[(int) Math.pow(2, level)];
//        barriers = new Scenery[(int) Math.pow(2, level)];
        viewer = new Camera(new Coords(0, 0), this);

//        Coords something = new DCoords(10, 20, 0, 1, new DCoords(INIT_X, INIT_Y, 0, 1, new Coords(0, 0, 0, 1)));
        //currentUseUnit = new Pirate(new Coords(INIT_X, INIT_Y*4), this);

        currentUseUnit = obsA.currentUseUnit;
        heroes = obsA.heroes;
        barriers = obsA.barriers;
        numbers = obsA.numbers;
//        for (int a = 0; a < barriers.length; a++) {
//            double squareOrEllipse = Math.random();
//            double randomX = Math.random() * this.farRight - 100;
//            while (randomX <= INIT_X) {
//                randomX = Math.random() * this.farRight - 100;
//            }
//            double randomY = Math.random() * this.farDown - 100;
//            if (squareOrEllipse <= .5) {
//                barriers[a] = new Scenery("Square", new Coords(randomX, randomY), this);
//            } else if (squareOrEllipse > .5) {
//                barriers[a] = new Scenery("Ellipse", new Coords(randomX, randomY), this);
//            }
//
//        }
//        for (int a = 0; a < numbers.length; a++) {
//            double randomX = Math.random() * this.farRight - 100;
//            double randomY = Math.random() * this.farDown - 100;
//            double randomX2 = Math.random() * this.farRight - 100;
//            double randomY2 = Math.random() * this.farDown - 100;
//            CharaPath toss = new CharaPath(new Coords(randomX, randomY), new Coords(randomX2, randomY2), currentUseUnit);
//            toss.checkPath(currentUseUnit, barriers, viewer);
//            while (toss.getPath().length > 50) {
//                randomX2 = Math.random() * this.farRight - 100;
//                randomY2 = Math.random() * this.farDown - 100;
//                toss = new CharaPath(new Coords(randomX, randomY), new Coords(randomX2, randomY2), currentUseUnit);
//                toss.checkPath(currentUseUnit, barriers, viewer);
//            }
//            numbers[a] = new Gunner(new Coords(randomX, randomY), new Coords(randomX2, randomY2), this);
//        }
        this.setBackground(java.awt.Color.DARK_GRAY);
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        aC = (AudioClip) (Sound.getSound("pkmz.wav"));
        //aC.loop();
        //_timer.start();
        this.repaint();

    }

    /**
     * Taking it a step further - let's deal with x/y and y/z separation: We're
     * using integers instead of booleans to indicate each axis for when we want
     * to do reverse images for each side - this way we can easily generate code
     * that allows for reversing the images.
     *
     * For example: - -1 on x axis when referring to the point (farRight, 50) -
     * farRight would give (0, 50). Whereas the point (0, 50) would become
     * (-farRight, 50) - we can the *-1 and get farRight there.
     *
     * Numbers themselves refer to the axes - 1 is x in Java, 2 is y in Java.
     * The current plan is to only really swap the x with the z axis.
     *
     */
    public PirateNinjaBoxerPanel(FortressSneaker framea, int levela, int xAxis, int yAxis, int zAxis, CombatModel obs) {
        super();
        _frame = framea;
        //_frame.setTitle("Fortress Sneaker Level " + levela);
        this.setDoubleBuffered(true);
        _timer = new MoveTimer(INTERVAL, this);
        obsA = obs;
        obs.addObserver(this);

        axesShow = new Coords(xAxis, yAxis, zAxis, 1);

        this.setSize(400, 400);
        level = obsA.level;
        this.farRight = obsA.farRight;
        this.farDown = obsA.farDown;
        this.bounda = new Bounds(new Coords(this.farRight - 100, this.farDown - 100), 100, 100);

//        numbers = new Enemy[(int) Math.pow(2, level)];
//        barriers = new Scenery[(int) Math.pow(2, level)];
        viewer = new Camera(new Coords(0, 0), this);
//        Coords something = new DCoords(10, 20, 0, 1, new DCoords(INIT_X, INIT_Y, 0, 1, new Coords(0, 0, 0, 1)));
        //currentUseUnit = new Pirate(new Coords(INIT_X, INIT_Y*4), this);

        currentUseUnit = obsA.currentUseUnit;
        heroes = obsA.heroes;
        barriers = obsA.barriers;
        numbers = obsA.numbers;
//        for (int a = 0; a < barriers.length; a++) {
//            double squareOrEllipse = Math.random();
//            double randomX = Math.random() * this.farRight - 100;
//            while (randomX <= INIT_X) {
//                randomX = Math.random() * this.farRight - 100;
//            }
//            double randomY = Math.random() * this.farDown - 100;
//            if (squareOrEllipse <= .5) {
//                barriers[a] = new Scenery("Square", new Coords(randomX, randomY), this);
//            } else if (squareOrEllipse > .5) {
//                barriers[a] = new Scenery("Ellipse", new Coords(randomX, randomY), this);
//            }
//
//        }
//        for (int a = 0; a < numbers.length; a++) {
//            double randomX = Math.random() * this.farRight - 100;
//            double randomY = Math.random() * this.farDown - 100;
//            double randomX2 = Math.random() * this.farRight - 100;
//            double randomY2 = Math.random() * this.farDown - 100;
//            CharaPath toss = new CharaPath(new Coords(randomX, randomY), new Coords(randomX2, randomY2), currentUseUnit);
//            toss.checkPath(currentUseUnit, barriers, viewer);
//            while (toss.getPath().length > 50) {
//                randomX2 = Math.random() * this.farRight - 100;
//                randomY2 = Math.random() * this.farDown - 100;
//                toss = new CharaPath(new Coords(randomX, randomY), new Coords(randomX2, randomY2), currentUseUnit);
//                toss.checkPath(currentUseUnit, barriers, viewer);
//            }
//            numbers[a] = new Gunner(new Coords(randomX, randomY), new Coords(randomX2, randomY2), this);
//        }
        this.setBackground(java.awt.Color.DARK_GRAY);
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        aC = (AudioClip) (Sound.getSound("pkmz.wav"));
        //aC.loop();
        //_timer.start();
        this.repaint();

    }

    public void move() {
        try {
            if (gameover || complete) {
                _timer.stop();
                return;
            }

            for (int a = 0; a < heroes.length; a++) {
                heroes[a].move();
                if (bounda.rect.contains(heroes[a].placement.getX(), heroes[a].placement.getY())) {
                    complete = true;
                    this.aC.stop();
                }
            }

            for (int b = 0; b < numbers.length; b++) {
                numbers[b].move();
                boolean alert = numbers[b].getView(this);
                if (alert) {
                    gameover = true;
                    this.aC.stop();
                }
            }
            this.repaint();
        } catch (Exception e) {
        }


    }

    public void paintComponent(java.awt.Graphics aBrush) {
        super.paintComponent(aBrush);
        java.awt.Graphics2D betterBrush = (java.awt.Graphics2D) aBrush;
        betterBrush.setFont(text);
        //Drawing the endgoal location for PirateNinjaBoxerPanel - we don't need it
//        betterBrush.setColor(java.awt.Color.CYAN);
//        betterBrush.fillRect((int) (bounda.rect.x - viewer.getX()), (int) (bounda.rect.y - viewer.getY()), bounda.rect.width, bounda.rect.height);

        //Let's draw the backing grid here
        betterBrush.setColor(java.awt.Color.orange);
        for (int i = 0; i <= (int) (this.farDown / 40); i++) {
            
            if (i == ((this.farDown / 40) / 2)) {
                betterBrush.setColor(java.awt.Color.RED);
            } else {
                betterBrush.setColor(java.awt.Color.orange);
            }
            //Horizontal line
            betterBrush.drawLine(0, i * 40, this.farRight, i * 40);
            //Vertical lines
            betterBrush.drawLine(i * 40, 0, i * 40, this.farDown);
        }
        //Trying hex grid
        //Then objects themselves

        betterBrush.setColor(java.awt.Color.YELLOW);
        for (int a = 0; a < barriers.length; a++) {
            barriers[a].paintComponent(betterBrush, this, viewer);
        }
        betterBrush.setColor(java.awt.Color.BLUE);
        for (int a = 0; a < heroes.length; a++) {
            heroes[a].paintComponent(betterBrush, this, heroes[a].imagear, viewer);
        }
        betterBrush.setColor(java.awt.Color.MAGENTA);
        for (int a = 0; a < numbers.length; a++) {
            numbers[a].paintComponent(betterBrush, this, numbers[a].imagear, viewer);
        }
        if (gameover) {
            this.updateUI();
            this.repaint();
            betterBrush.setColor(java.awt.Color.orange);
            betterBrush.drawString("Game Over.", this.getWidth() / 2 - this.getWidth() / text.getSize(), this.getHeight() / 2);
            betterBrush.drawString("Press 'Enter' to replay.", this.getWidth() / 2 - this.getWidth() / text.getSize(), this.getHeight() / 2 + text.getSize());
        }
        if (complete) {
            this.updateUI();
            this.repaint();
            betterBrush.setColor(java.awt.Color.orange);
            betterBrush.drawString("Complete.", this.getWidth() / 2 - this.getWidth() / text.getSize(), this.getHeight() / 2);
            betterBrush.drawString("Press 'Enter' to continue.", this.getWidth() / 2 - this.getWidth() / text.getSize(), this.getHeight() / 2 + text.getSize());
        }
    }

    public double getLeftBound() {
        return (double) (this.getX());
    }

    public double getRightBound() {
        return (double) (this.getX() + this.farRight);
    }

    public double getUpBound() {
        return (double) (this.getY());
    }

    public double getLowBound() {
        return (double) (this.getY() + this.farDown);
    }

    public void keyPressed(KeyEvent e) {
        try {
            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                if (complete) {
                    this.readyforGarbageCollection();
                    _frame = new FortressSneaker("Fortress Sneaker Level", level + 1, (int) this.axesShow.getX(), (int) this.axesShow.getY(), (int) this.axesShow.getZ(), this.obsA);

                }
                if (gameover) {
                    this.readyforGarbageCollection();
                    _frame = new FortressSneaker("Fortress Sneaker Level", level, (int) this.axesShow.getX(), (int) this.axesShow.getY(), (int) this.axesShow.getZ(), this.obsA);
                } else if (_timer.isRunning()) {
                    _timer.stop();
                    return;
                } else {
                    _timer.start();
                    return;
                }
            } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                this.readyforGarbageCollection();
                _frame = new FortressSneaker("Fortress Sneaker Level", level, (int) this.axesShow.getX(), (int) this.axesShow.getY(), (int) this.axesShow.getZ(), this.obsA);
            }
        } catch (NullPointerException e3) {
        }
    }

    public void mousePressed(MouseEvent e) {
        try {
            currentUseUnit.developPath((double) e.getX() + viewer.getX(), (double) e.getY() + viewer.getY(),
                    currentUseUnit.placement.getX(), currentUseUnit.placement.getY(), barriers, viewer);
            this.repaint();
        } catch (Exception argh) {
        }
    }

    public void mouseMoved(MouseEvent e) {
        /*
         * if (e.getX() < 10) { viewer.moveLeft(); } else if (e.getX() > 390) {
         * viewer.moveRight(); } if (e.getY() < 10) { viewer.moveUp(); } else if
         * (e.getY() > 390) { viewer.moveDown();
         *
         * } else { return; }
         */
    }

    public void readyforGarbageCollection()//Readies the frame for garbage collection, so as not to...freeze the computer. As soon, at least.
    {//                                     Got the idea for this code from http://mindprod.com/jgloss/garbagecollection.html . Code is also 
        //									continued in FortressSneaker with the line System.gc(); to insinuate to the the garbage collector 
        //									that now is a good time to garbage collect.
        for (int a = 0; a < barriers.length; a++) {
            barriers[a].placement = null;
            barriers[a].bounda = null;
            //barriers[a].pP = null;
            barriers[a].iD = null;
            barriers[a].iDP = null;
            barriers[a].iDR = null;
        }
        for (int b = 0; b < heroes.length; b++) {
            heroes[b].placement = null;
            heroes[b].bounda = null;
            //heroes[b].pP = null;
            heroes[b].iD = null;
            heroes[b].iDP = null;
            heroes[b].iDR = null;
            heroes[b].imagear = null;
            heroes[b].lastplacement = null;
            heroes[b].path = null;
        }
        for (int c = 0; c < numbers.length; c++) {
            numbers[c].placement = null;
            numbers[c].bounda = null;
            //numbers[c].pP = null;
            numbers[c].iD = null;
            numbers[c].iDP = null;
            numbers[c].iDR = null;
            numbers[c].imagear = null;
            numbers[c].lastplacement = null;
            numbers[c].path = null;
        }
        this._timer = null;
        this.barriers = null;
        this.heroes = null;
        this.bounda = null;
        this.currentUseUnit = null;
        this.numbers = null;
        this.viewer = null;
        this.text = null;
        this.removeMouseListener(this);
        this.removeMouseMotionListener(this);
        this.aC = null;
        this.removeAll();
        _frame.setVisible(false);
        _frame.removeKeyListener(_frame.bP);
        _frame.bP = null;
//        _frame.app = null;//This is an error that, for garbage collection purposes, is ignored.
        _frame.removeAll();
        _frame.dispose();
        _frame = null;
        System.gc();
        return;
    }

//Keeping these below to keep the adapaters in place, so that I don't need more listeners.
    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
        int movementButton = e.getButton();
//        if (movementButton == 1) {
        System.out.println("Will rotate:");
        int tempX = e.getX();
        int tempY = e.getY();
        
        //TODO: Later, we'll need to make this more general, but
        //For now, we basically translate the z value into the x
        //value only if z is used
        if(this.axesShow.getZ() != 0){
            tempY *= axesShow.getY();
            tempX *= axesShow.getZ();
            tempX -= this.currentUseUnit.placement.flattenGlobal().getZ();
            tempY -= this.currentUseUnit.placement.flattenGlobal().getY();
            this.obsA.moveLocallyPart(tempX, tempY, 1);
            //1 for aZ = "aX refers to Z axis
            //0 = Still X axis
        }else{
            tempY *= axesShow.getY();
            tempX *= axesShow.getX();
            tempX -= this.currentUseUnit.placement.flattenGlobal().getX();
            tempY -= this.currentUseUnit.placement.flattenGlobal().getY();
            this.obsA.moveLocallyPart(tempX, tempY, 0);
        }
        //Rotation sort of works now; //TODO: Fix rounding errors
        //Specifically, when snapping to the grid instead of just 
        //floating the rotation, we find that we shrink the "circle"
        //We rotate around - creating a sort of spiral.
        //We could do it another way, where we increase the spiral,
        //But that's not ideal either.
//        if(this.axesShow.getZ() != 0){
//            this.obsA.rotateLocallyPart(50, "x");
//        }else{
//            this.obsA.rotateLocallyPart(50, "z");
//        }
        
        //Now testing movement via direct selection
        System.out.println("Should be moved:");
        //this.update(obsA, this);
//        } else {
//            this.obsA.rollback();
//            //currentUseUnit.placement.rotateLocallyPart(315, "z");
//        }
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("Has been picked up: " + new Integer(this.level).toString());
        currentUseUnit = obsA.currentUseUnit;
        heroes = obsA.heroes;
        barriers = obsA.barriers;
        numbers = obsA.numbers;
        System.out.println("Updated");
        this.repaint();
    }
}
