// utilities
function getLength(x0, y0, x1, y1) {
    // returns the length of a line segment
    const x = x1 - x0;
    const y = y1 - y0;
    return Math.sqrt(x * x + y * y);
}

function getDegAngle(x0, y0, x1, y1) {
    const y = y1 - y0;
    const x = x1 - x0;
    return Math.atan2(y, x) * (180 / Math.PI);
}

// some constants
const DECAY = 4;        // confetti decay in seconds
const SPREAD = 30*3.141592/180;      // degrees to spread from the angle of the cannon
const GRAVITY = 120;

class ConfettiCannon {
    constructor() {
        // setup a canvas
        this.canvas = document.getElementById('canvas');
        this.dpr = window.devicePixelRatio || 1;
        this.ctx = this.canvas.getContext('2d');
        this.ctx.scale(this.dpr, this.dpr);

        // add confetti here
        this.confettiSpriteIds = [];
        this.confettiSprites = {};
        
        // vector line representing the firing angle
        this.drawVector = false;
        this.vector = [{
            x: window.innerWidth * 0.9, 
            y: window.innerHeight * 0.8,
        }, {
            x: window.innerWidth, 
            y: window.innerHeight,
        }];
        
        this.pointer = {};
        
        // bind methods
        this.render = this.render.bind(this);
        this.handleMousedown = this.handleMousedown.bind(this);
        this.handleMouseup = this.handleMouseup.bind(this);
        this.handleMousemove = this.handleMousemove.bind(this);
        this.handleTouchstart = this.handleTouchstart.bind(this);
        this.handleTouchmove = this.handleTouchmove.bind(this);
        this.setCanvasSize = this.setCanvasSize.bind(this);
        
        this.setupListeners();
        this.setCanvasSize();
        
        // fire off for a demo
        //this.timer = setTimeout(this.handleMouseup, 1000);
    }
    
    setupListeners() {
        // Use TweenLite tick event for the render loop
        TweenLite.ticker.addEventListener('tick', this.render);
        
        // bind events
        window.addEventListener('mousedown', this.handleMousedown);
        window.addEventListener('mouseup', this.handleMouseup);
        window.addEventListener('mousemove', this.handleMousemove);
        window.addEventListener('touchstart', this.handleTouchstart);
        window.addEventListener('touchend', this.handleMouseup);
        window.addEventListener('touchmove', this.handleTouchmove);
        window.addEventListener('resize', this.setCanvasSize);
    }

    setCanvasSize() {
        this.canvas.width = window.innerWidth * this.dpr;
        this.canvas.height = window.innerHeight * this.dpr;
        this.canvas.style.width = window.innerWidth + 'px';
        this.canvas.style.height = window.innerHeight + 'px';
    }
    
    handleMousedown(event) {
        clearTimeout(this.timer);
        const x = event.clientX * this.dpr;
        const y = event.clientY * this.dpr;
        
        this.vector[0] = {
            x,
            y,
        };
        this.drawVector = true;
    }
    
    handleTouchstart(event) {
        clearTimeout(this.timer);
        event.preventDefault();
        const x = event.touches[0].clientX * this.dpr;
        const y = event.touches[0].clientY * this.dpr;
        this.vector[0] = {
            x,
            y,
        };
        
        this.drawVector = true;
    }
    
    handleMouseup(event) {
        this.drawVector = false;
        
        const x0 = this.vector[0].x;
        const y0 = this.vector[0].y;
        const x1 = this.vector[1].x;
        const y1 = this.vector[1].y;
        
        const length = getLength(x0, y0, x1, y1);
        const angle = (getDegAngle(x0, y0, x1, y1) - 180)*-3.141592/180;

        const particles = length / 3 + 5;
        const velocity = length * 50;
        this.addConfettiParticles(particles, angle, velocity, x0, y0);
    }
    
    handleMousemove(event) {
        const x = event.clientX * this.dpr;
        const y = event.clientY * this.dpr;   
        this.vector[1] = {
            x,
            y,
        };
        this.pointer = this.vector[1];
    }
    
    handleTouchmove(event) {
        event.preventDefault();
        const x = event.changedTouches[0].clientX * this.dpr;
        const y = event.changedTouches[0].clientY * this.dpr;   
        this.vector[1] = {
            x,
            y,
        };
        this.pointer = this.vector[1];
    }
    
    drawVectorLine() {
        this.ctx.strokeStyle = 'pink';
        this.ctx.lineWidth = 2 * this.dpr;
        
        const x0 = this.vector[0].x;
        const y0 = this.vector[0].y;
        const x1 = this.vector[1].x;
        const y1 = this.vector[1].y;
        
        this.ctx.beginPath();
        this.ctx.moveTo(x0, y0);
        this.ctx.lineTo(x1, y1);
        this.ctx.stroke();
    }

    addConfettiParticles(amount, angleP, velocityP, x, y) {
        let i = 0;
        while (i < amount) {
            // sprite
            const r = _.random(4, 6) * this.dpr;
            const d = _.random(15, 25) * this.dpr;
            
            const cr = _.random(30, 255);
            const cg = _.random(30, 230);
            const cb = _.random(30, 230);
            const color = `rgb(${cr}, ${cg}, ${cb})`;
            
            const tilt = _.random(10, -10);
            const tiltAngleIncremental = _.random(0.07, 0.05);
            const tiltAngle = 0;

            const id = _.uniqueId();

            const friction = _.random(0.10, 0.25);



            const minAngle = angleP - SPREAD / 2;
            const maxAngle = angleP + SPREAD / 2;
            const minVelocity = velocityP / 2;
            const maxVelocity = velocityP;
            // Physics Props
            const velocity = _.random(minVelocity, maxVelocity);
            const angle = _.random(minAngle, maxAngle);



            const sprite = {
                [id]: {
                    angle,
                    velocity,
                    x,
                    y,
                    r,
                    d,
                    color,
                    tilt,
                    tiltAngleIncremental,
                    tiltAngle,
                    friction
                },
            };

            Object.assign(this.confettiSprites, sprite);
            this.confettiSpriteIds.push(id);
            //this.tweenConfettiParticle(id);
            i++;
        }
    }

    tweenConfettiParticle(id) {
        const minAngle = this.confettiSprites[id].angle - SPREAD / 2;
        const maxAngle = this.confettiSprites[id].angle + SPREAD / 2;
        
        const minVelocity = this.confettiSprites[id].velocity / 4;
        const maxVelocity = this.confettiSprites[id].velocity;

        // Physics Props
        const velocity = _.random(minVelocity, maxVelocity);
        const angle = _.random(minAngle, maxAngle);
        const gravity = GRAVITY;
        const friction = _.random(0.01, 0.025);
        const d = 0;

        /*TweenLite.to(this.confettiSprites[id], DECAY, {
            physics2D: {
                velocity,
                angle,
                gravity,
                friction,
            },
            d,
            ease: Power4.easeIn,
            onComplete: () => {
                // remove confetti sprite and id
                _.pull(this.confettiSpriteIds, id);
                delete this.confettiSprites[id];
            },
        });*/
    }

    updateConfettiParticle(id) {

        const sprite = this.confettiSprites[id];
        
        const tiltAngle = 0.0005 * sprite.d;

        //sprite.angle -= 0.01;
        var vx =  Math.cos(sprite.angle)*sprite.velocity;
        var vy = -Math.sin(sprite.angle)*sprite.velocity;
        vx = Math.sign(vx)*Math.max(0, Math.abs(vx) - sprite.friction);
        vy = vy + GRAVITY - sprite.friction;
        sprite.angle = Math.atan2(-vy, vx);

        sprite.tiltAngle += tiltAngle;
        sprite.tiltAngle += sprite.tiltAngleIncremental;
        sprite.tilt = (Math.sin(sprite.tiltAngle - (sprite.r / 2))) * sprite.r * 2;
        sprite.y += vy*0.002+Math.sin(sprite.r / 2);//+Math.sin(sprite.angle + sprite.r / 2);
        sprite.x += vx*0.002;//Math.cos(sprite.angle) * 2;
    }

    drawConfetti() {
        var deletable = [];
        this.confettiSpriteIds.map(id => {
            const sprite = this.confettiSprites[id];
            
            this.ctx.beginPath();
            this.ctx.lineWidth = sprite.d / 2;
            this.ctx.strokeStyle = sprite.color;
            this.ctx.moveTo(sprite.x + sprite.tilt + sprite.r, sprite.y);
            this.ctx.lineTo(sprite.x + sprite.tilt, sprite.y + sprite.tilt + sprite.r);
            this.ctx.stroke();

            this.updateConfettiParticle(id);
        });
    }
    
    drawPointer() {
        const centerX = this.pointer.x;
        const centerY = this.pointer.y;
        const radius = 15 * this.dpr;

        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
        this.ctx.fillStyle = 'transparent';
        this.ctx.fill();
        this.ctx.lineWidth = 2 * this.dpr;
        this.ctx.strokeStyle = '#ffffff';
        this.ctx.stroke();
    }
    
    drawPower() {
        const x0 = this.vector[0].x;
        const y0 = this.vector[0].y;
        const x1 = this.vector[1].x;
        const y1 = this.vector[1].y;
        
        const length = getLength(x0, y0, x1, y1);
        const centerX = x0;
        const centerY = y0;
        const radius = 1 * this.dpr * length / 20;

        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
        this.ctx.fillStyle = 'transparent';
        this.ctx.fill();
        this.ctx.lineWidth = 2 * this.dpr;
        this.ctx.strokeStyle = '#333333';
        this.ctx.stroke();
    }

    render() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        // only draw the vector when the drawVector flag is on
        this.drawVector && this.drawVectorLine();
        this.drawVector && this.drawPower();
        
        this.drawPointer();
        this.drawConfetti();
    }

    fire(x1, y1, x2, y2) {
        this.vector = [{
            x: x1, 
            y: y1,
        }, {
            x: x2, 
            y: y2,
        }];
        this.handleMouseup();
    }
}
var isMobile = false; //initiate as false
// device detection
if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) 
    || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) isMobile = true;

if (!isMobile) {
    const confetti = new ConfettiCannon();
    setTimeout(() => {
        confetti.fire(window.innerWidth * 0.9, window.innerHeight * 0.8, window.innerWidth, window.innerHeight);
    }, 1000);
    setTimeout(() => {
        confetti.fire(window.innerWidth * 0.1, window.innerHeight * 0.8, 0, window.innerHeight);
    }, 1050);
    setTimeout(() => {
        confetti.fire(window.innerWidth * 0.9, window.innerHeight * 0.8, window.innerWidth, window.innerHeight);
    }, 2000);
    setTimeout(() => {
        confetti.fire(window.innerWidth * 0.1, window.innerHeight * 0.8, 0, window.innerHeight);
    }, 2050);
}
