import { type Tile, TileType, getPortDirections, PortDirection, type Resource, getTileOutput, ResourceType } from "./world";

import { ALL_DIRECTIONS, Direction, movePoint } from "./utils";

import emptyImage from './assets/img/empty.png';
import extractorImage from './assets/img/extractor.png';
import notImage from './assets/img/not.png';
import andImage from './assets/img/and.png';
import orImage from './assets/img/or.png';

export interface ViewConfig {
    tileSize: number;
}

export type Renderer<T extends Tile> = (ctx: CanvasRenderingContext2D, view: ViewConfig, tile: T) => void;
export type NullRenderer = (ctx: CanvasRenderingContext2D, view: ViewConfig) => void;

type Renderers = {
    [K in Tile['type']]?: Renderer<Extract<Tile, { type: K }>>
}

export const renderResource = (ctx: CanvasRenderingContext2D, view: ViewConfig, tile: Tile) => {
    let resources: [Direction, Resource | undefined][] | undefined;
    if (tile.type === TileType.SOURCE) {
        resources = [[Direction.NONE, tile.resource]]
    } else if (tile.type === TileType.EXTRACTOR) {
        resources = [[Direction.NONE, tile.source.resource]]
    } else if ((tile.timer ?? 0) <= 0 && tile.type !== TileType.DESTINATION) { // when resource was precessed by machine
        const [output] = getTileOutput(tile);
        if (output && (tile.timer ?? 0) <= 0) {
            resources = [[Direction.NONE, output]];
        }
    }
    
    if (!resources) {
        resources = [...ALL_DIRECTIONS, Direction.NONE].map((d) => [d, tile.inv[d]]);
    }
    
    const fontScale = Math.max(Math.pow(32 / view.tileSize, 1.5), 1);
    const px = 1 / 32;

    let wasOtherDrawn = false;
    for (const [direction, res] of resources) {
        if (direction === Direction.NONE && wasOtherDrawn) continue;
        if (res?.type === ResourceType.NUMBER) {
            const str = res.value.toString(2);
            const [timer, timerMax] = tile.inputAnimations?.get(direction) ?? [0, 1];
            const amount = timer / timerMax;

            const [x, y] = movePoint([0.5, 0.52], direction, amount);

            ctx.strokeStyle = 'white';
            ctx.lineWidth = px * 2;
            ctx.miterLimit = 2;
            if (tile.type === TileType.SOURCE) {
                ctx.font = `${0.4 * fontScale}px "IBM VGA 8x16"`;
            }
            ctx.strokeText(str, x, y);
            ctx.fillText(str, x, y);
            wasOtherDrawn = true;
        }
    }
}

export const renderPorts = (ctx: CanvasRenderingContext2D, _view: ViewConfig, tile: Tile) => {
    if (tile.type === TileType.DESTINATION || tile.type === TileType.SOURCE) {
        return;
    }
    for (const direction of getPortDirections(tile.ports)) {
        const portDirection = tile.ports[direction]?.direction;
        if (portDirection === PortDirection.INPUT) {
            ctx.fillStyle = 'lightgreen';
        } else if (portDirection === PortDirection.OUTPUT) {
            ctx.fillStyle = 'red';
        }
        if (direction === Direction.NORTH) {
            ctx.fillRect(0.2, 0, 0.6, 0.2);
        } else if (direction === Direction.SOUTH) {
            ctx.fillRect(0.2, 0.8, 0.6, 0.2);
        } else if (direction === Direction.WEST) {
            ctx.fillRect(0, 0.2, 0.2, 0.6);
        } else if (direction === Direction.EAST) {
            ctx.fillRect(0.8, 0.2, 0.2, 0.6);
        }
    }
}

export const defaultRenderer = (ctx: CanvasRenderingContext2D) => {
    ctx.drawImage(emptyImage, 0, 0, 1, 1);
}

const imageRenderer = <T extends Tile>(image: HTMLImageElement): Renderer<T> => (ctx, tile) => {
    ctx.drawImage(image, 0, 0, 1, 1);
}

export const renderers: Renderers = {
    [TileType.SOURCE]: (ctx, view, tile) => {
        ctx.fillStyle = '#bbffff7f';
        ctx.fillRect(0, 0, 1, 1);
        ctx.fillStyle = 'black';
        renderResource(ctx, view, tile);
    },
    [TileType.DESTINATION]: (ctx, _view, tile) => {
        if (tile.center) {
            ctx.fillStyle = '#bbffbb';
            ctx.fillRect(-2, -2, 5, 5);
            ctx.fillStyle = 'black';
            ctx.fillText('Deploy', 0.5, 0.65);
        }
    },
    [TileType.EXTRACTOR]: (ctx, view, tile) => {
        renderers[TileType.SOURCE]?.(ctx, view, tile.source);
        ctx.drawImage(extractorImage, 0, 0, 1, 1);
    },
    [TileType.CONVEYOR]: (ctx, _view, _tile) => {
        ctx.fillStyle = 'lightgray';
        ctx.fillRect(0.2, 0.2, 0.6, 0.6);
    },
    [TileType.NOT]: imageRenderer(notImage),
    [TileType.AND]: imageRenderer(andImage),
    [TileType.OR]: imageRenderer(orImage),
};