import game.S;
import javax.microedition.lcdui.Image;
/**
 *
 * @author user
 */
public class Ball {
    
    private boolean jump, fall, rb_fall, stop;
    private boolean[] vGBS;
    private int md, jd, jmd, x, y, bmx, bjx, bjy, bjmx, bfy, tw, th;
    private float j_speed, m_speed, rb, mspeed, jspeed, rb_speed, mt, jt, jmt, ft, jcs, jsn;
    private int[][][] sides, thorns_sides, fans_sides, gbs_sides;
    private long dtime;
    private Image[] d;
    private S ball;
    
    private Fan[] fans;
    private GlassBlock[] gbs;
    
    /*
     * Конструктор класса, принимает спрайт шарика, скорость прыжка, скорость движение, падение скорости после отскока (множитель), края блоков, ширину и высоту спрайта
     */
    public Ball(S ball, float j_speed, float m_speed, float rb, int[][][] sides, int tw, int th) {
        this.ball = ball;
        this.j_speed = j_speed;
        this.m_speed = m_speed;
        this.rb = rb;
        this.sides = sides;
        this.tw = tw;
        this.th = th;
    }
    
    /*
     * Запуск уничтожения шарика (укол об колючки или т.п.), принимает массив картинок (фреймы взрыва)
     */
    public void destroy(Image[] d) {
        this.d = d;
        dtime = System.currentTimeMillis();
    }
    
    /*
     * Цикличное изменение анимации взрыва шарика
     */
    public boolean destroying() {
        long time = System.currentTimeMillis()-dtime;
        if(time < 400) {
            int bW = ball.getWidth(), bH = ball.getHeight();
            Image img = d[(int)(time/50)];
            ball.setImage(img, img.getWidth(), img.getHeight());
            ball.setPosition(ball.getX()+bW/2-ball.getWidth()/2, ball.getY()+bH/2-ball.getHeight()/2);
        } else {
            ball.setPosition(-ball.getWidth(), -ball.getHeight());
        }
        if(time < 800) {
            return true;
        }
        return false;
    }
    
    /*
     * Установка сторон колючек
     */
    public void setThorns(int[][][] sides) {
        thorns_sides = sides;
    }
    
    /*
     * Установка вентиляторов и их сторон
     */
    public void setFans(Fan[] fans, int[][][] sides) {
        this.fans = fans;
        fans_sides = sides;
    }
    
    /*
     * Установка стеклянных блоков и их сторон
     */
    public void setGlassBlocks(GlassBlock[] gbs, int[][][] sides) {
        this.gbs = gbs;
        vGBS = new boolean[gbs.length];
        for(int i=0; i<vGBS.length; i++) vGBS[i] = true;
        gbs_sides = sides;
    }
    
    /*
     * Проверка на столкновение шарика со стеклянными блоками, метод прнимает координаты шарика
     */
    private boolean checkGlassBlocks(int x, int y) {
        for(int i=0; i<gbs.length; i++) {
            S gb = gbs[i].getGlassBlock();
            if(x == gb.getX() && y == gb.getY()) {
                gbs[i].crash();
                return true;
            }
            gb = null;
        }
        return false;
    }
    
    /*
     * Привести шарик в движение, d=1 - влево, d=2 - вправо
     */
    public void startMove(int d) {
        if(md == 0 && !jump) {
            mt = (jmd != d ? 0 : jmt);
            bmx = (jmd != d ? 0 : bjmx);
            mspeed = (jmd != d ? m_speed : 0);
            md = (jmd != d ? d : jmd);
            if(jmd != 0) jmd = 0;
        }
        if(jmd == 0 && jump) {
            jmt = 0;
            bjmx = 0;
            jmd = d;
        }
    }
    
    /*
     * Цикличное обновление (проверка) движения шарика, метод  принмает значение 1/fps
     */
    public void updateMove(float t) {
        if(md != 0 && !jump) {
            if(!stop) {
                x = (int)((mspeed*mt+9.8f*sqr(mt)/2)*20);
                mt+=t;
                if(x != bmx) {
                    bmx = cLM(x, bmx, tw/2);
                    ball.move(md == 1 ? -(x-bmx) : x-bmx, 0);
                }
                bmx = x;
            } else {
                x = (int)((mspeed*mt-9.8f*sqr(mt)/2)*20);
                if(x >= bmx) {
                    mt+=t;
                    if(x != bmx) {
                        bmx = cLM(x, bmx, tw/2);
                        ball.move(md == 1 ? -(x-bmx) : x-bmx, 0);
                    }
                    bmx = x;
                } else {
                    stop = false;
                    md = 0;
                }
            }
            if(checkCollide(3)) {
                stopMove(1);
                mspeed*=rb;
                md = 2;
            }
            if(checkCollide(4)) {
                stopMove(2);
                mspeed*=rb;
                md = 1;
            }
        }
        if(jmd != 0 && jump) {
            x = (int)((9.8f*sqr(jmt)/2)*20);
            jmt+=t;
            if(x != bjmx) {
                bjmx = cLM(x, bjmx, tw/2);
                ball.move(jmd == 1 ? -(x-bjmx) : x-bjmx, 0);
            }
            bjmx = x;
        }
    }
    
    /*
     * Остановить движение шарика, d=1 - влево, d=2 - вправо
     */
    public void stopMove(int d) {
        if(d == md && !jump) {
            mspeed+=9.8f*mt;
            mt = 0;
            bmx = 0;
            stop = true;
        }
        if(d == jmd && jump) {
            jmd = 0;
        }
    }
    
    /*
     * Осуществить прыжок мяча
     */
    public void jump() {
        if(!jump && !fall && !rb_fall) {
            float angle = (float)Math.toRadians(md != 0 ? mt < 1 ? 90-(22.5f*mt) : 67.5f : 90);
            jcs = (float)Math.cos(angle);
            jsn = (float)Math.sin(angle);
            jspeed = j_speed+(md != 0 && !stop ? mt < 1 ? mt : 1 : 0);
            mt = 0;
            jt = 0;
            jd = md;
            if(md != 0) md = 0;
            jump = true;
        }
    }
    
    /*
     * Цикличное обновление (проверка) прыжка шарика, метод  принмает значение 1/fps
     */
    public void updateJump(float t) {
        if(jump && md == 0 && !fall && !rb_fall) {
            x = (int)((jspeed*jcs*jt)*100);
            y = (int)((jspeed*jsn*jt-9.8f*sqr(jt)/2)*100);
            jt+=t;
            if(x != bjx) {
                bjx = cLM(x, bjx, tw/2);
                ball.move(jd == 0 ? 0 : jd == 1 ? -(x-bjx) : x-bjx, 0);
            }
            if(y != bjy) {
                bjy = cLM(y, bjy, th/2);
                ball.move(0, -(y-bjy));
            }
            bjx = x;
            bjy = y;
            if(checkCollide(1)) {
                bjy = y;
                while(true) {
                    y = (int)((jspeed*jsn*jt-9.8f*sqr(jt)/2)*100);
                    jt+=t;
                    if(y < bjy) {
                        bjy = y;
                        break;
                    }
                }
                bjx = (int)((jspeed*jcs*jt)*100);
            }
            if(checkCollide(2)) {
                bjx = 0;
                bjy = 0;
                jspeed = (float)Math.sqrt(sqr(jspeed*jcs)+sqr(jspeed*jsn-9.8f*jt))*rb;
                jt = 0;
                if(sqr(jspeed)*sqr(jsn)/(2*9.8f)*100 < 1/rb) {
                    jump = false;
                }
            }
            if(checkCollide(3)) {
                if(jmd != 0) {
                    jmt = 0;
                    bjmx = 0;
                }
                jd = 2;
            }
            if(checkCollide(4)) {
                if(jmd != 0) {
                    jmt = 0;
                    bjmx = 0;
                }
                jd = 1;
            }
        }
    }
    
    /*
     * Цикличное обновление (проверка) падения шарика, метод  принмает значение 1/fps
     */
    public void updateFall(float t) {
        if(!jump) {
            if(!fall && !rb_fall) {
                boolean fc = false;
                for(int i=0; i<sides[1].length; i++) {
                    if(ball.getY() >= sides[1][i][0]-ball.getHeight() && ball.getY() < sides[1][i][0]+th/2-ball.getHeight()/2 && ball.getX() > sides[1][i][1]-ball.getWidth()+10 && ball.getX() < sides[1][i][2]-10) {
                        fc = true;
                    }
                }
                if(!fc) {
                    ft = 0;
                    bfy = 0;
                    fall = true;
                }
            }
            if(fall) {
                y = (int)((9.8*sqr(ft)/2)*100);
                ft+=t;
                if(y != bfy) {
                    bfy = cLM(y, bfy, th/2);
                    ball.move(0, y-bfy);
                }
                bfy = y;
                if(checkCollide(2)) {
                    bfy = 0;
                    rb_speed = 9.8f*ft*rb;
                    ft = 0;
                    fall = false;
                    rb_fall = true;
                }
            }
            if(rb_fall) {
                y = (int)((rb_speed*ft-9.8*sqr(ft)/2)*100);
                ft+=t;
                if(y != bfy) {
                    bfy = cLM(y, bfy, th/2);
                    ball.move(0, -(y-bfy));
                }
                bfy = y;
                if(checkCollide(1)) {
                    bfy = y;
                    while(true) {
                        y = (int)((rb_speed*ft-9.8f*sqr(ft)/2)*100);
                        ft+=t;
                        if(y < bfy) {
                            bfy = y;
                            break;
                        }
                    }
                    if(md != 0) bmx = (int)((mspeed*mt+(!stop ? 9.8f : -9.8f)*(mt*mt)/2)*20);
                }
                if(checkCollide(2)) {
                    bfy = 0;
                    rb_speed = Math.abs(rb_speed-9.8f*ft)*rb;
                    ft = 0;
                    if(sqr(rb_speed)/(2*9.8f)*100 < 1/rb) {
                        rb_fall = false;
                    }
                }
            }
            if(fall || rb_fall) {
                checkCollide(3);
                checkCollide(4);
            }
        }
    }
    
    /*
     * Цикличное обновление (проверка) воздействия вентиляторов на шарик, метод  принмает значение 1/fps
     */
    public void updateFans(float t) {
        int bX = ball.getX()+ball.getWidth()/2, bY = ball.getY()+ball.getHeight()/2;
        for(int i=0; i<fans.length; i++) {
            S fan = fans[i].getFan();
            int fX = fan.getX()+fan.getWidth()/2, fY = fan.getY()+fan.getHeight()/2;
            switch(fans[i].getDirection()) {
                case 1:
                    if(bY >= fY-45 && bY <= fY-fan.getHeight()/2 && bX >= fX-20 && bX <= fX+20) {
                        int dist = (int)(32*fans[i].getTime()-9.8f*sqr(fans[i].getTime())/2);
                        if(dist >= fans[i].getBackDistance()) {
                            fans[i].setTime(fans[i].getTime()+t);
                            if(dist != fans[i].getBackDistance()) {
                                ball.move(0, -(dist-fans[i].getBackDistance()));
                            }
                            fans[i].setBackDistance(dist);
                        } else {
                            stop = false;
                            md = 0;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                        if(stop) {
                            md = 0;
                            stop = false;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                    } else {
                        fans[i].setTime(0);
                        fans[i].setBackDistance(0);
                    }
                    break;
                case 2:
                    if(bY >= fY+fan.getHeight()/2 && bY <= fY+65 && bX >= fX-20 && bX <= fX+20) {
                        int dist = (int)(96*fans[i].getTime()-9.8f*sqr(fans[i].getTime())/2);
                        if(dist >= fans[i].getBackDistance()) {
                            fans[i].setTime(fans[i].getTime()+t);
                            if(dist != fans[i].getBackDistance()) {
                                ball.move(0, dist-fans[i].getBackDistance());
                            }
                            fans[i].setBackDistance(dist);
                        } else {
                            stop = false;
                            md = 0;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                        if(stop) {
                            md = 0;
                            stop = false;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                    } else {
                        fans[i].setTime(0);
                        fans[i].setBackDistance(0);
                    }
                    break;
                case 3:
                    if(bX >= fX-60 && bX <= fX-fan.getWidth()/2 && bY >= fY-15 && bY <= fY+15) {
                        int dist = (int)(36*fans[i].getTime()-9.8f*sqr(fans[i].getTime())/2);
                        if(dist >= fans[i].getBackDistance()) {
                            fans[i].setTime(fans[i].getTime()+t);
                            if(dist != fans[i].getBackDistance()) {
                                ball.move(-(dist-fans[i].getBackDistance()), 0);
                            }
                            fans[i].setBackDistance(dist);
                        } else {
                            stop = false;
                            md = 0;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                        if(stop) {
                            md = 0;
                            stop = false;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                    } else {
                        fans[i].setTime(0);
                        fans[i].setBackDistance(0);
                    }
                    break;
                case 4:
                    if(bX >= fX+fan.getWidth()/2 && bX <= fX+60 && bY >= fY-15 && bY <= fY+15) {
                        int dist = (int)(36*fans[i].getTime()-9.8f*sqr(fans[i].getTime())/2);
                        if(dist >= fans[i].getBackDistance()) {
                            fans[i].setTime(fans[i].getTime()+t);
                            if(dist != fans[i].getBackDistance()) {
                                ball.move(dist-fans[i].getBackDistance(), 0);
                            }
                            fans[i].setBackDistance(dist);
                        } else {
                            stop = false;
                            md = 0;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                        if(stop) {
                            md = 0;
                            stop = false;
                            fans[i].setTime(0);
                            fans[i].setBackDistance(0);
                        }
                    } else {
                        fans[i].setTime(0);
                        fans[i].setBackDistance(0);
                    }
                    break;
            }
            fan = null;
        }
    }
    
    /*
     * Проверка столкновения шарика с поверхностью (сторонами), d=1 - потолок, d=2 - пол, d=3 - лево, d=4 - право
     */
    private boolean checkCollide(int d) {
        for(int j=0; j<sides[d-1].length; j++) {
            if(checkCollide(sides[d-1][j], d)) {
                ball.setPosition((d <= 2 ? ball.getX() : sides[d-1][j][0])-(d == 4 ? ball.getWidth() : 0), (d >= 3 ? ball.getY() : sides[d-1][j][0])-(d == 2 ? ball.getHeight() : 0));
                return true;
            }
        }
        for(int j=0; j<fans_sides[d-1].length; j++) {
            if(checkCollide(fans_sides[d-1][j], d)) {
                ball.setPosition((d <= 2 ? ball.getX() : fans_sides[d-1][j][0])-(d == 4 ? ball.getWidth() : 0), (d >= 3 ? ball.getY() : fans_sides[d-1][j][0])-(d == 2 ? ball.getHeight() : 0));
                return true;
            }
        }
        if(d != 2) {
            for(int j=0; j<gbs_sides[d-1].length; j++) {
                if(vGBS[j]) {
                    if(checkCollide(gbs_sides[d-1][j], d)) {
                        ball.setPosition((d <= 2 ? ball.getX() : gbs_sides[d-1][j][0])-(d == 4 ? ball.getWidth() : 0), (d >= 3 ? ball.getY() : gbs_sides[d-1][j][0])-(d == 2 ? ball.getHeight() : 0));
                        return true;
                    }
                }
            }
        } else {
            for(int j=0; j<gbs_sides[d-1].length; j++) {
                if(vGBS[j]) {
                    if(checkCollide(gbs_sides[d-1][j], d)) {
                        ball.setPosition((d <= 2 ? ball.getX() : gbs_sides[d-1][j][0])-(d == 4 ? ball.getWidth() : 0), (d >= 3 ? ball.getY() : gbs_sides[d-1][j][0])-(d == 2 ? ball.getHeight() : 0));
                        if(checkGlassBlocks(gbs_sides[d-1][j][1], gbs_sides[d-1][j][0])) vGBS[j] = false;
                        return true;
                    }
                }
            }
        }
        if(d == 3 || d == 4) {
            for(int j=0; j<thorns_sides[d-1].length; j++) {
                if(ball.getX() > (d == 3 ? thorns_sides[d-1][j][0]-tw/2-ball.getWidth()/2 : thorns_sides[d-1][j][0]-ball.getWidth()) && ball.getX() < (d == 3 ? thorns_sides[d-1][j][0] : thorns_sides[d-1][j][0]+tw/2-ball.getWidth()/2) && ball.getY() > thorns_sides[d-1][j][1]-ball.getHeight()+10 && ball.getY() < thorns_sides[d-1][j][2]-10) {
                    ball.setPosition(thorns_sides[d-1][j][0]-(d == 4 ? ball.getWidth() : 0), ball.getY());
                    return true;
                }
            }
        }
        return false;
    }
    
    /*
     * Внешний метод функции checkCollide(int), проверяет столконовение одной любой стороны
     */
    private boolean checkCollide(int[] side, int p) {
        if(p == 1 || p == 2) {
            if(ball.getY() > (p == 1 ? side[0]-th/2-ball.getHeight()/2 : side[0]-ball.getHeight()) && ball.getY() < (p == 1 ? side[0] : side[0]+th/2-ball.getHeight()/2) && ball.getX() > side[1]-ball.getWidth()+10 && ball.getX() < side[2]-10) {
                return true;
            } else {
                return false;
            }
        }
        if(p == 3 || p == 4) {
            if(ball.getX() > (p == 3 ? side[0]-tw/2-ball.getWidth()/2 : side[0]-ball.getWidth()) && ball.getX() < (p == 3 ? side[0] : side[0]+tw/2-ball.getWidth()/2) && ball.getY() > side[1]-ball.getHeight()+5 && ball.getY() < side[2]-5) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }
    
    /*
     * Функция возведения числа в квадрат
     */
    private float sqr(float n) {
        return n*n;
    }
    
    /*
     * Проверка разрывности траектории шарика, если она слишком большая, метод уменшьшит разрыв. Предназначение функции - исключение возможности проскакивания шарика через блоки
     */
    private int cLM(int p, int bp, int dlong) {
        if(Math.abs(p-bp) > dlong) {
            if(p > bp) return p-dlong;
            if(p < bp) return p+dlong;
        }
        return bp;
    }
    
}