class Reel
{
    constructor (config)
    {
        this.parentScene = config.scene;
        this.rm = config.rm;
        
        this.reel = config.reel;//array, [0..n] integers

        this.ngreel = config.ngreel;
        this.fgreel = config.fgreel;

        this.reel = [...this.ngreel];

        this.reelId = config.reelId;

        this.position = config.reelPosition;//position at which reel must stop [0..n-1]

        this.containerX = config.containerX;
        this.containerY = config.containerY;

        this.reelRectWidth = REEL_RECT_WIDTH;//width of reel rect in pixels
        this.reelRectHeight = REEL_RECT_HEIGHT;//height of reel rect in pixels

        this.container = this.parentScene.add.container(this.containerX, this.containerY);
        this.container.setSize(REEL_RECT_WIDTH, REEL_RECT_HEIGHT);

        this.reeltween = new ReelTween({reel: this,
                                    scene: this.parentScene,
                                    duration: config.duration,
                                    backDuration: config.backDuration,
                                    backEase: config.backEase,
                                    spinTime: config.spinTime
                                });


        this.spinTime = 0;
        this.extraSpinTime = 0;

        this.symbolField = [];
        this.symbolFieldAS = [];

        this.winLines = [];
        this.winLinesAS = [];

        this.winLine = 0;

        this.haveWild = false;
        this.haveScatter = false;

        this.forceStopped = false;

        this.direction = REEL_DOWN;

        this.animationsComplete = 0;

        this.sprites = [];
        
        this.winGroup = [];

        this.list = [];

        this.landingSprite = this.parentScene.add.sprite(0, 0, "scatterAnim").setVisible(false);
        this.landingSprite.on("animationcomplete", this.landingAnimationComplete, this);

        this.initReelDown();
    }

    setSpinDirection (direction)
    {
        let list;

        this.rm.setState(RM_STATE_READY);

        this.stopAnimations();//stop container from reordering sprite list

        if (this.list.length > 0)
        {
            list = this.list;
        }
        else
        {
            list = this.container.getAll();
        }

        let newlist = [];

        if (this.direction == REEL_DOWN)
        {
            newlist.push(list[1]);
            newlist.push(list[2]);
            newlist.push(list[3]);
            newlist.push(list[0]);
        }
        else
        {
            newlist.push(list[3]);
            newlist.push(list[0]);
            newlist.push(list[1]);
            newlist.push(list[2]);
        }

        this.direction = direction;

        if (this.direction == REEL_UP && !this.reeltween.getDirection())
        {
            this.container.removeAll();

            newlist[3].y += 4 * REEL_RECT_HEIGHT;
        
            this.reeltween.setDirection(REEL_UP);
        }
        else if (this.direction == REEL_DOWN && this.reeltween.getDirection())
        {
            this.container.removeAll();

            newlist[0].y += 4 * -REEL_RECT_HEIGHT;

            this.reeltween.setDirection(REEL_DOWN);
        }

        this.container.add(newlist);

        this.list.length = 0;//reset old container list
    }

    initReelDown ()
    {
        for (let i = 0; i < 3; ++i)
        {
            if (++this.position >= this.reel.length)
            {
                this.position = 0;
            }
            
            this.symbolField.push(this.reel[this.position]);
        }

        let spr1 = new SpriteEx(this.parentScene, this, 0, -REEL_RECT_HEIGHT * 2 - 19, 474, 466, this.getSymbolKey());//invisible
        let spr2 = new SpriteEx(this.parentScene, this, 0, -REEL_RECT_HEIGHT - 19, 474, 466, SYMBOLS[this.symbolField[2]]);//top line
        let spr3 = new SpriteEx(this.parentScene, this, 0, -19, 474, 466, SYMBOLS[this.symbolField[1]]);//middle line
        let spr4 = new SpriteEx(this.parentScene, this, 0, REEL_RECT_HEIGHT - 19, 474, 466, SYMBOLS[this.symbolField[0]]);//bottom line

        spr1.setVisible(false);

        this.container.add([ spr1, spr2, spr3, spr4 ]);

        this.createContainerClipRect(0, 0);
        
        this.mask = this.clippingRect.createGeometryMask();

        this.container.setMask(this.mask);

        this.direction = REEL_DOWN;
    
        this.reeltween.setDirection(this.direction);

        this.updateMask();
    }

    initReelUp()
    {
        for (let i = 0; i < 3; ++i)
        {
            if (++this.position >= this.reel.length)
            {
                this.position = 0;
            }
            
            this.symbolField.push(this.reel[this.position]);
        }

        let spr1 = new SpriteEx(this.parentScene, this, 0, -REEL_RECT_HEIGHT - 19, 474, 466, this.getSymbolKey());//top
        let spr2 = new SpriteEx(this.parentScene, this, 0, - 19, 474, 466, this.getSymbolKey());//middle line
        let spr3 = new SpriteEx(this.parentScene, this, 0, REEL_RECT_HEIGHT - 19, 474, 466, this.getSymbolKey());//bottom line
        let spr4 = new SpriteEx(this.parentScene, this, 0, REEL_RECT_HEIGHT * 2 - 19, 474, 466, this.getSymbolKey());//invisible

        spr4.setVisible(false);

        this.container.add([spr1, spr2, spr3, spr4]);

        this.createContainerClipRect(0, 0);

        this.mask = this.clippingRect.createGeometryMask();

        this.container.setMask(this.mask);

        this.direction = REEL_UP;
    
        this.reeltween.setDirection(this.direction);

        this.updateMask();
    }

    createContainerClipRect ()
    {
        if (this.reelId == 0)
        {
            this.clippingRect = this.parentScene.add.rectangle(960 - 2 * REEL_RECT_WIDTH + 132, 590, 480, 600, 0x9966ff).setVisible(false).setAlpha(0.5).setDepth(100);
        }
        else if (this.reelId == 1)
        {
            this.clippingRect = this.parentScene.add.rectangle(960 - REEL_RECT_WIDTH - 8, 590, 344, 600, 0x9966ff).setVisible(false).setAlpha(0.5).setDepth(100);
        }
        else if (this.reelId == 2)
        {
            this.clippingRect = this.parentScene.add.rectangle(960, 590, 344, 600, 0x9966ff).setVisible(false).setAlpha(0.5).setDepth(100);
        }
        else if (this.reelId == 3)
        {
            this.clippingRect = this.parentScene.add.rectangle(960 + REEL_RECT_WIDTH + 8, 590, 344, 600, 0x9966ff).setVisible(false).setAlpha(0.5).setDepth(100);
        }
        else if(this.reelId == 4)
        {
            this.clippingRect = this.parentScene.add.rectangle(960 + REEL_RECT_WIDTH * 2 - 120, 590, 460, 600, 0x9966ff).setVisible(false).setAlpha(0.5).setDepth(100);
        }

        this.shapeX = this.clippingRect.x;
        this.shapeY = this.clippingRect.y;
    }

    resizeMask (y1, y2)
    {
        if (this.reelId == 0)
        {
            this.clippingRect.setSize(480, 600 + y1 + y2);
        }
        else if (this.reelId == 1 || this.reelId == 2 || this.reelId == 3)
        {
            this.clippingRect.setSize(344, 600 + y1 + y2);
        }
        else
        {
            this.clippingRect.setSize(460, 600 + y1 + y2);
        }
    }

    updateSwapMask ()
    {
        if (this.rm.state == RM_STATE_BEFORE_SWAP || RM_STATE_AFTER_SWAP)//update only if rm is in swap state
        {
            if (this.parentScene.isLandscapeMode())
            {
                this.clippingRect.setPosition(960, 590);
            }
            else
            {
                this.clippingRect.setPosition(540, 926);
            }
            
            this.clippingRect.setSize(1054, 700);
        }
    }
    
    restoreMaskAfterSwap ()
    {
        let dx = (this.parentScene.isLandscapeMode())?0:-420;
        let dy = (this.parentScene.isLandscapeMode())?0:336;

        this.clippingRect.setPosition(this.shapeX + dx, this.shapeY + dy);

        this.updateMask();
    }

    updateMask ()
    {
        let y1 = 70;
        let y2 = 70;

        if (this.direction == REEL_DOWN)
        {
            if (this.container.list[0].visible && (this.rm.state != RM_STATE_BEFORE_SWAP && this.rm.state != RM_STATE_AFTER_SWAP))
            {
                this.container.list[0].setVisible(false);
            }
        }
        else
        {
            if (this.container.list[3].visible && (this.rm.state != RM_STATE_BEFORE_SWAP && this.rm.state != RM_STATE_AFTER_SWAP))
            {
                this.container.list[3].setVisible(false);
            }
        }
        
        this.resizeMask(y1, y2);
    }

    getSymbolKey ()
    {
        if (++this.position >= this.reel.length)
        {
            this.position = 0;
        }

        return SYMBOLS[this.reel[this.position]];
    }

    update (time, delta) 
    {
        for (let i = 0; i < this.sprites.length; ++i)
        {
            this.sprites[i].update(time, delta);
        }
    }

    startSpin ()
    {
        if (this.sprites[3] != undefined)
        {
            this.sprites[3].setVisible(true);
        }
        else
        {
            if (this.direction == REEL_DOWN)
            {
                this.container.list[0].setVisible(true);
            }
            else
            {
                this.container.list[3].setVisible(true);
            }
        }

        if (this.list.length > 0)
        {
            this.container.list = this.list;

            this.list = [];
        }

        this.reeltween.renderReel();
    }

    normalSpin()
    {
        this.reeltween.normalSpin(this.spinTime, this.extraSpinTime);
    }

    fastSpin()
    {
        this.reeltween.fastSpin();
    }

    specialSpin (spinTime)
    {
        this.reeltween.specialSpin(spinTime);
    }

    beginSpin ()
    {
        this.destroyAnimations(true);

        this.resizeMask(0, 0);

        if (this.parentScene.isPortraitMode())
        {
            this.clippingRect.setPosition(this.shapeX - 420, this.shapeY + 336);
        }

        this.haveWild = false;
        this.haveScatter = false;

        this.forceStopped = false;

        this.animationsComplete = 0;

        this.rm.container.bringToTop(this.container);

        this.startSpin();
    }

    endSpin()
    {
        this.symbolField = this.symbolField.reverse();//restore symbol field original order

        this.resetContainer();

        this.list = [...this.container.list];

        if (this.reeltween.getDirection() == REEL_DOWN)
        {
            this.sprites[0] = this.container.list[1];
            this.sprites[1] = this.container.list[2];
            this.sprites[2] = this.container.list[3];
            this.sprites[3] = this.container.list[0];
        }
        else
        {
            this.sprites[0] = this.container.list[0];
            this.sprites[1] = this.container.list[1];
            this.sprites[2] = this.container.list[2];
            this.sprites[3] = this.container.list[3];
        }

        if (this.sprites[3].visible)
        {
            this.sprites[3].setVisible(false);
        }

        this.rm.reelStopped(this.reelId);
    }

    createAnimation (v, key, hasLanding = false)
    {
        switch (key)
        {
            case "ten":
            case "jack":
            case "queen":
            case "king":
            case "ace":
            case "leg":
            case "knee":
            case "elbow":
            case "hand":
            case "warrior":
            case "wild":
            case "scatter":
            {
                if (this.sprites[v].sprite.anims.currentAnim == null)
                {
                    this.sprites[v].setTextureKey(key, hasLanding);
                    
                    let anim = this.parentScene.anims.create({
                        key: this.reelId + "_PlayAnim_" + v,
                        frames: this.parentScene.anims.generateFrameNumbers(key),
                        frameRate: 5,
                        repeat: 0
                    });

                    this.sprites[v].sprite.anims.currentAnim = anim;
                    this.sprites[v].sprite.anims.setCurrentFrame(anim.frames[0]);
                };break;
            }
        }
    }

    createLineAnimations ()
    {
        for (let i = 0; i < this.winLines.length; ++i)
        {
            if (this.reelId < this.winLines[i].len)
            {
                let v = this.winLines[i]["line"][this.reelId] + 1;
                let key = SYMBOLS[this.symbolField[v]];

                this.createAnimation(v, key);
            }
        }
    }

    createLineAnimationsAS ()
    {
        for (let i = 0; i < this.winLinesAS.length; ++i)
        {
            if (this.reelId < this.winLinesAS[i].len)
            {
                let v = this.winLinesAS[i]["line"][this.reelId] + 1;
                let key = SYMBOLS[this.symbolFieldAS[v]];

                this.createAnimation(v, key);
            }
        }
    }

    createScatterAnimation (scattersField, scatterCount)
    {
        for (let i = 0; i < scattersField.length; ++i)
        {
            if (scattersField[i] == 1)
            {
                this.createAnimation(i, "scatter", true);
                return;
            }
        }
    }

    stopAnimations ()
    {
        this.sprites.forEach(sprite => {sprite.stopAnimation();});
    }

    destroyAnimations (clearLanding = false)
    {
        this.sprites.forEach(sprite => {sprite.destroyAnimation(clearLanding);});
    }

    stopLandingAnimations ()
    {
        this.sprites.forEach(sprite => {sprite.stopLandingAnimation();});
    }

    setWinLine (winLine)
    {
        this.winLine = winLine;
    }

    playLineAnimation ()
    {
        if (!this.rm.visible)
        {
            return;
        }

        let len = this.winLines[this.winLine].len;

        if (this.reelId < len)
        {
            let v = this.winLines[this.winLine]["line"][this.reelId] + 1;

            this.sprites[v].playAnimation();

            this.container.bringToTop(this.sprites[v]);

            this.rm.container.bringToTop(this.container);
        }
    }

    playScatterAnimation ()
    {
        let anims = 0;

        for (let i = 0; i < this.sprites.length - 1; ++i)
        {
            if (this.sprites[i].getTextureKey() == "scatter")
            {
                this.sprites[i].playAnimation(-1);
                
                this.container.bringToTop(this.sprites[i]);

                ++anims;

                return anims;
            }
        }

        return anims;
    }

    playGroupAnimations (v = 0)
    {
        let anims = 0;

        for (let i = 0; i < this.winGroup.length; ++i)
        {
            if (this.winGroup[i] == 1)
            {
                this.sprites[i].playAnimation(v);

                this.container.bringToTop(this.sprites[i]);

                ++anims;
            }
        }

        return anims;
    }

    animationComplete ()
    {
        this.rm.animationComplete();
    }

    startLandingAnimation ()
    {
        this.landingSprite.play("landingAnimation");

        for (let i = 0; i < this.container.list.length; ++i)
        {
            if ((this.direction == REEL_DOWN && i == 0) || (this.direction == REEL_UP && i == 3))
            {
                continue;
            }

            if (this.container.list[i].getTextureKey() == "scatter" && this.container.list[i].visible)
            {
                this.container.list[i].startLandingAnimation();

                break;
            }
        }
    }

    landingAnimationComplete ()
    {
        for (let i = 0; i < this.container.list.length; ++i)
        {
            if ((this.direction == REEL_DOWN && i == 0) || (this.direction == REEL_UP && i == 3))
            {
                continue;
            }

            if (this.container.list[i].getTextureKey() == "scatter" && this.container.list[i].visible && this.container.list[i].isLanding)
            {
                this.container.list[i].stopLandingAnimation();

                break;
            }
        }
    }

    resetSymbolsZOrder ()
    {
        this.container.bringToTop(this.sprites[0]);
        this.container.bringToTop(this.sprites[1]);
        this.container.bringToTop(this.sprites[2]);

        this.rm.container.bringToTop(this.container);
    }

    setSymbolField (symbolField, symbolFieldAS)
    {
        this.symbolField = [...symbolField];
        this.symbolField = this.symbolField.reverse();

        this.symbolFieldAS = [...symbolFieldAS];

        for (let i = 0; i < this.symbolField.length; ++i)
        {
            if (this.symbolField[i] == WILD)
            {
                this.haveWild = true;
                
                return;
            }
        }
    }

    setScatter ()
    {
        this.haveScatter = true;
    }

    setWinLines (winLines, winLinesAS)
    {
        this.winLines = winLines;
        this.winLinesAS = winLinesAS;

        this.winLine = 0;
    }

    setWinGroup (winGroup)
    {
        this.winGroup = [...winGroup];
    }

    setState (state)
    {
        this.state = state;
    }

    getState ()
    {
        return this.state;
    }

    setSpinTime (time, extraTime)
    {
        this.spinTime = time;
        this.extraSpinTime = extraTime;
    }

    setNGReel ()
    {
        this.reel = [...this.ngreel];
    }

    setFGReel ()
    {
        this.reel = [...this.fgreel];
    }

    stop ()
    {
        this.forceStopped = true;

        this.rm.hideStopBtn();

        this.fastSpin();
    }

    errStop ()
    {
        this.reeltween.errStop();
    }

    setVisible (v)
    {
        this.container.setVisible(v);

        if (v)
        {
            this.resetSymbolsZOrder();
        }
    }

    setScale (v)
    {
        this.container.setScale(v);
    }

    resetContainer ()
    {
        this.container.setPosition(this.containerX, this.containerY);

        if (!this.reeltween.getDirection())
        {
            this.container.list[0].setPosition(0, -REEL_RECT_HEIGHT * 2 - 19);
            this.container.list[1].setPosition(0, -REEL_RECT_HEIGHT - 19);
            this.container.list[2].setPosition(0, -19);
            this.container.list[3].setPosition(0, REEL_RECT_HEIGHT - 19);
        }
        else
        {
            this.container.list[0].setPosition(0, -REEL_RECT_HEIGHT - 19);
            this.container.list[1].setPosition(0, -19);
            this.container.list[2].setPosition(0, REEL_RECT_HEIGHT - 19);
            this.container.list[3].setPosition(0, REEL_RECT_HEIGHT * 2 - 19);
        }
    }

    onOrientationChange ()
    {
        this.container.setPosition(this.containerX, this.containerY);

        if (this.parentScene.isLandscapeMode())
        {
            this.clippingRect.setPosition(this.shapeX, this.shapeY);
        }
        else
        {
            this.clippingRect.setPosition(this.shapeX - 420, this.shapeY + 336);
        }
    }
}
