import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.BasicStroke;
import java.util.Random;

public class World
{
    private Sounds snds;
    private Random RG;

    private float xpos;
    private float ypos;
    private float yvel;
    private float xdir;
    private boolean jumping;

    private final int MAPLEN = 30;
    private final int NUMOBSTACLES = 25;
//    private final int NUMOBSTACLES = 1;
    private float[] dists;
    private float[] obsdists;
    private int[] obsids;
    private float[] distssum;
    private float[] angles;
    
    private final int NUMDECOR = 200;
    private float[] decorx;
    private float[] decory;
    private int[] decorids;

    private float mapxpos;
    private final float mapypos = 150;
    private float mapxdir;
    
    private int currentanim;
    private int flameanim;
    private double animtime;

    private BMPSprite[] sprites;
    private final int NUMSPRITES = 9    ;
    
    public World()
    {
        snds = new Sounds();
        
        animtime = 0;
        currentanim = 0;
        flameanim = 6;
        
        Random RG = new Random();

        xpos = 80;
        ypos = 0;
        yvel = 0;
        xdir = 0;

        mapxpos = 0;
        mapxdir = -1.89f;

        dists = new float[MAPLEN];
        obsdists = new float[NUMOBSTACLES];
        obsids = new int[NUMOBSTACLES];
        distssum = new float[MAPLEN];
        angles = new float[MAPLEN];
        for (int i = 0; i < MAPLEN; i++)
        {
            if (i % 2 == 1)
            {
                angles[i] = RG.nextFloat()*20+25;
                dists[i] = RG.nextFloat()*60+20;
            }
            else
            {
                angles[i] = 0;
                dists[i] = RG.nextFloat()*200+40;
            }
            if (i > 0)
            {
                distssum[i] = distssum[i-1] + dists[i];
            }
            else
            {
                distssum[i] = dists[i];
            }
            System.out.println(dists[i] + " " + distssum[i]);
        }
        
        sprites = new BMPSprite[NUMSPRITES];
        for (int i = 0; i < NUMSPRITES; i++)
        {
            sprites[i] = new BMPSprite(i);
        }
        
        for (int i = 0; i < NUMOBSTACLES; i++)
        {
            obsdists[i] = RG.nextFloat()*distssum[MAPLEN-1]*0.8f+distssum[MAPLEN-1]*0.1f;
            obsids[i] = RG.nextFloat()*5 < 3.9 ? 1 : 4;
//obsdists[i] = 200;
        }
        
        decorx = new float[NUMDECOR];
        decory = new float[NUMDECOR];
        decorids = new int[NUMDECOR];
        for (int i = 0; i < NUMDECOR; i++)
        {
            decorids[i] = RG.nextFloat()*10 < 1 ? 2 : 3;
            decorx[i] = RG.nextFloat()*distssum[MAPLEN-1]*0.98f;
            if (decorids[i] == 2)// cloud
            {
                decory[i] = -RG.nextFloat()*mapypos/2 - mapypos/2;
            }
            else // grass
            {
                decorids[i] = RG.nextFloat()*2 < 1 ? 3 : 8;
                decory[i] = RG.nextFloat()*mapypos*2 + 20;
            }
        }
    }

    public void moveX(float xd)
    {
        xdir = xd + mapxdir;
        // System.out.println(xdir);
    }

    public void jump()
    {
        if (jumping == true) {
            return;
        }
        System.out.println("jumping...");
        snds.JumpSound();
        yvel += 3;
        jumping = true;
    }

    public void step(double dt)
    {
        animtime = animtime + dt;
        if (animtime > 200)
        {
            animtime -= 200;
            if (!jumping)
            {
                if (currentanim == 0)
                    currentanim = 5;
                else
                    currentanim = 0;
            }
            else
            {
                if (flameanim == 6)
                    flameanim = 7;
                else
                    flameanim = 6;
            }
        }
        
        // xpos += xdir;
        // ypos += ydir;
        if (mapxpos > 0)// && mapxpos <= (distssum[29] - 640))
        {
            mapxpos = -0.1f;
        }
        else if (640-mapxpos > distssum[MAPLEN-1])
        {
            mapxpos = 640 - distssum[MAPLEN-1] + 0.1f;
            mapxdir = 0;
        }
        mapxpos += mapxdir;
        
        if (xpos < 0)
        {
            xpos = 0;
            xdir = 0;
        }
        if (xpos > 630)
        {
            xpos = 630;
            xdir = 0;
            if (mapxdir == 0)
            {
                System.out.println("You win!");
                System.exit(2);
            }
        }
        xpos += xdir;

        yvel -= 0.1;
        ypos += yvel;
        // System.out.println(yvel);
        if (ypos < 0)
        {
            ypos = 0;
            yvel = 0;
            jumping = false;
        }
        
        checkForCollision();
    }
    
    private void checkForCollision()
    {
        int ow;// = (sprites[1].getWidth())/2 - 2;
        int oh;// = sprites[1].getHeight() - 2;
        int pp = (int)xpos;
        int op;
        for (int i = 0; i < NUMOBSTACLES; i++)
        {
            ow = (sprites[obsids[i]].getWidth())/2 - 2;
            oh = sprites[obsids[i]].getHeight() - 2;
        
            op = (int)(obsdists[i] + mapxpos);
            if ((pp > (op - ow)) && (pp < (op + ow)))
            {
                if (ypos < oh)
                {
                    System.out.println("You died!");
                    System.exit(0);
                }
            }
        }
    }

    public void render(Graphics2D g2d)
    {
        drawMap(g2d);
        drawDecor(g2d);
        drawObstacles(g2d);
        drawPerson(g2d);
    }
    
    private void drawDecor(Graphics2D g2d)
    {
        float mxpos;
        float mypos;
        for (int i = 0; i < NUMDECOR; i++)
        {
            mxpos = decorx[i] + mapxpos;
            mypos = decory[i] + mapypos + yoffset(mapxpos) - yoffset(mapxpos - mxpos);
            sprites[decorids[i]].draw(g2d, (int)mxpos, (int)mypos);
        }
    }
    
    private void drawObstacles(Graphics2D g2d)
    {        
        float mxpos;
        float mypos;
        for (int i = 0; i < NUMOBSTACLES; i++)
        {
            mxpos = obsdists[i] + mapxpos;
            mypos = mapypos + yoffset(mapxpos) - yoffset(mapxpos - mxpos);
            sprites[obsids[i]].draw(g2d, (int)mxpos, (int)mypos);
        }
    }

    private void drawPerson(Graphics2D g2d)
    {
//        g2d.setColor(Color.BLUE);
//        g2d.setStroke(new BasicStroke(4));
        float mypos = mapypos + yoffset(mapxpos) - yoffset(mapxpos - xpos) - ypos;
        // g2d.drawLine((int)xpos, (int)mypos, (int)xpos, (int)mypos-20);
        sprites[currentanim].draw(g2d, (int)xpos, (int)mypos);
        if (jumping)
        {
            sprites[flameanim].draw(g2d, (int)xpos, (int)mypos + sprites[flameanim].getHeight());
        }
    }

    private void drawMap(Graphics2D g2d)
    {
        g2d.setColor(new Color(135,206,250));
        g2d.fillRect(0,0,640,480);

        float mxpos = mapxpos;
        float mypos = mapypos;
        float newxpos, newypos;

        int xpoints[] = new int[MAPLEN+3];
        int ypoints[] = new int[MAPLEN+3];
        xpoints[0] = (int)mxpos;
        ypoints[0] = (int)mypos;

        mypos += yoffset(mapxpos);
        int i;
        for (i = 0; i < MAPLEN; i++)
        {
            newxpos = mxpos + dists[i];
            newypos = mypos + dists[i]*(float)Math.tan((double)Math.toRadians(angles[i]));
            //            g2d.drawLine((int)(mxpos), (int)(mypos), (int)(newxpos), (int)(newypos));
            mxpos = newxpos;
            mypos = newypos;
            xpoints[i+1] = (int)newxpos;
            ypoints[i+1] = (int)newypos;
        }
        i++;
        xpoints[i] = xpoints[i-1];
        ypoints[i] = ypoints[i-1] + 500;
        i++;
        xpoints[i] = xpoints[0];
        ypoints[i] = ypoints[i-1];

        g2d.setColor(Color.GREEN);
        g2d.setStroke(new BasicStroke(2));
        g2d.fillPolygon(xpoints, ypoints, xpoints.length);

        g2d.setColor(Color.BLACK);
        g2d.setStroke(new BasicStroke(10));
        g2d.drawPolygon(xpoints, ypoints, xpoints.length);
    }

    private float yoffset(float x)
    {
        float mypos = 0;

        // need to adjust mapypos to current height in the map...
        int currentline = 0;
        while (currentline < MAPLEN && Math.abs(x) >= distssum[currentline])
        {
            currentline++;
        }
        // System.out.println(currentline);
        for (int i = 0; i <= currentline; i++)
        {
            mypos = mypos - dists[i]*(float)Math.tan((double)Math.toRadians(angles[i]));
        }
        mypos = mypos + (distssum[currentline]+x)*(float)Math.tan((double)Math.toRadians(angles[currentline]));

        return mypos;
    }
}
