import { ILevelConfig, ScrollDirection } from '../data/Interfaces';

/**
 * Camera-scrolling model (task 7.1).
 *
 * Captures the level's camera/scrolling state without depending on `cc`. The
 * Cocos view layer maps `CameraScroller.offsetX / offsetY` into a `Camera`
 * component position every frame.
 *
 * Supported scroll modes (req 8.1-8.5, 8.8):
 *  - `horizontal`      — scroll never rewinds (森林/魔城).
 *  - `horizontal_bi`   — left/right both allowed (洞穴水路).
 *  - `vertical`        — scrolls upward as the player climbs (城壁).
 *
 * Values are in **landscape design pixels** (960x540 baseline).
 */

export interface ICameraConfig {
    /** Scroll direction, mirrors `ILevelConfig.scrollDirection`. */
    direction: ScrollDirection;
    /** Horizontal level length (for `horizontal` and `horizontal_bi`). */
    lengthX: number;
    /** Vertical level length (for `vertical`). */
    lengthY?: number;
    /** Camera viewport (design px). */
    viewportW: number;
    viewportH: number;
}

/** Four-layer parallax scroller (req 8.8). Speed ratios 1 : 2 : 4 : 4. */
export const PARALLAX_RATIOS = [1, 2, 4, 4] as const;
export type ParallaxLayer = 'far' | 'mid' | 'near' | 'fx';
export const PARALLAX_LAYERS: ParallaxLayer[] = ['far', 'mid', 'near', 'fx'];

export class CameraScroller {
    private _offsetX = 0;
    private _offsetY = 0;
    private readonly cfg: ICameraConfig;

    constructor(cfg: ICameraConfig) {
        this.cfg = cfg;
    }

    public get offsetX(): number {
        return this._offsetX;
    }

    public get offsetY(): number {
        return this._offsetY;
    }

    /** Camera target follows the player but never rewinds on `horizontal`. */
    public followPlayer(playerX: number, playerY: number): void {
        const halfW = this.cfg.viewportW / 2;
        const halfH = this.cfg.viewportH / 2;
        if (this.cfg.direction === 'horizontal') {
            const desired = Math.max(0, playerX - halfW);
            this._offsetX = Math.min(
                Math.max(this._offsetX, desired),
                Math.max(0, this.cfg.lengthX - this.cfg.viewportW)
            );
        } else if (this.cfg.direction === 'horizontal_bi') {
            const desired = Math.max(0, playerX - halfW);
            this._offsetX = Math.min(desired, Math.max(0, this.cfg.lengthX - this.cfg.viewportW));
        } else if (this.cfg.direction === 'vertical') {
            const ly = this.cfg.lengthY ?? this.cfg.viewportH;
            const desiredY = Math.max(0, playerY - halfH);
            this._offsetY = Math.min(desiredY, Math.max(0, ly - this.cfg.viewportH));
        }
    }

    /** Compute the world offset for a given parallax layer. */
    public offsetForLayer(layer: ParallaxLayer): { x: number; y: number } {
        const ratio = PARALLAX_RATIOS[PARALLAX_LAYERS.indexOf(layer)];
        return { x: this._offsetX / ratio, y: this._offsetY / ratio };
    }

    /** Return the level's culling rect in world coordinates. */
    public cullRect(): { leftX: number; rightX: number; topY: number; bottomY: number } {
        return {
            leftX: this._offsetX,
            rightX: this._offsetX + this.cfg.viewportW,
            topY: this._offsetY + this.cfg.viewportH,
            bottomY: this._offsetY,
        };
    }
}

/** Build a CameraScroller from a level config. */
export function cameraFromLevel(level: ILevelConfig, viewportW = 960, viewportH = 540): CameraScroller {
    return new CameraScroller({
        direction: level.scrollDirection,
        lengthX: level.levelLengthPx,
        lengthY: level.scrollDirection === 'vertical' ? level.levelLengthPx : undefined,
        viewportW,
        viewportH,
    });
}
