import {cols, drawAll, drawHamsters, rows, territoryContent} from "./canvas.js";
import {
    closeAddGrainsHamsterDialog,
    createPromiseForReadNumberDialog, createPromiseForReadStringDialog,
    createPromiseForWriteStringDialog,
    errorDialogOpen, numberOfGrainsHamsterInput,
    openErrorDialog
} from "./dialogs.js";
import {languagesStrings, userLanguage} from "./language.js";
import {stopCode} from "./controls";

const MAX_HAMSTER = 11;
export let hamster;
export let hamsters = [];
export let hamsterCounter = 0;
export let hamsterNameAlreadyInUseError = false;
export let maxHamsterError = false;
export let error = false;

/**
 * Liefert das Hamster-Array und alle darin enthaltenen Hamster-Objekte
 * 
 * @return {*[]}, alle Hamster-Objekte, die erzeugt wurden.
 */
export function getHamster() {
    return hamsters;
}

/**
 * Setter für das Error-Attribut
 *
 * @param value, true/false.
 */
export function setError(value) {
    error = value;
}

/**
 * Setter für das Error-Attribut
 *
 * @param value, true/false.
 */
export function setMaxHamsterError(value) {
    maxHamsterError = value;
}

/**
 * Setter für das Error-Attribut
 *
 * @param value, true/false.
 */
export function setHamsterNameAlreadyInUseError(value) {
    hamsterNameAlreadyInUseError = value;
}

/**
 * Setzt den Hamster-Counter auf den Standard-Wert
 *
 */
export function resetHamsterCounter() {
    hamsterCounter = 1;
}

/**
 * Leert das Hamster-Array
 */
export function clearHamsterArray() {
    hamsters = [];
}

/**
 * Erzeugt einen neuen Standard-Hamster
 */
export function createNewDefaultHamster() {
    hamster = new Hamster();
}

/**
 * Setter für das hamster-Attribut
 *
 * @param value, neuer Wert.
 */
export function setDefaultHamster(value) {
    hamster = value;
}

/**
 * Setter für das hamsterCounter-Attribut
 *
 * @param value
 */
export function setHamsterCounter(value) {
    hamsterCounter = value;
}

/**
 * Liefert den Hamster mit der gewünschten Hamster-ID.
 * 
 * @param hamsterId, die gesuchte Hamster-ID.
 * @return {null|*}: das Hamster-Objekt mit der gesuchten ID bei Erfolg; sonst null.
 */
export function getHamsterWithId(hamsterId) {
    for (let i = 0; i<hamsters.length; i++) {
        if (hamsters[i].getHamsterId() === hamsterId) {
            return hamsters[i];
        }
    }
    
    return null;
}

/**
 * Initialisiert beim übergebenen Hamster-Objekt die Image-Paths, die für das Zeichnen des Hamsters benötigt werden.
 * 
 * @param hamster, bei dem die Image-Paths initialisiert werden sollen.
 * @return {boolean}: true bei Erfolg; sonst false.
 */
function initializeImagePaths(hamster) {
    if (hamsterCounter < MAX_HAMSTER) {
        hamsterCounter++;
        hamster.hamsterId = hamsterCounter;
        hamster.imagePaths = {
            0: `./img/hamster/hamster-${hamsterCounter}/hamster-mit-Pfeil-north-${hamsterCounter}-40x40.svg`,
            1: `./img/hamster/hamster-${hamsterCounter}/hamster-mit-Pfeil-east-${hamsterCounter}-40x40.svg`,
            2: `./img/hamster/hamster-${hamsterCounter}/hamster-mit-Pfeil-south-${hamsterCounter}-40x40.svg`,
            3: `./img/hamster/hamster-${hamsterCounter}/hamster-mit-Pfeil-west-${hamsterCounter}-40x40.svg`
        }
        return true;
    } else {
        maxHamsterError = true;
        stopCode();
        if (!errorDialogOpen) {
            openErrorDialog(languagesStrings[userLanguage]['error-to-many-hamster']);
        }
        return false;
    }
}

/**
 * Repräsentiert den Akteur und alle seine Funktionen.
 */
export class Hamster {
    constructor(row, column, direction, numberOfGrains) {
        switch (arguments.length) {
            case 0:
                this.imagePaths = {};
                this.hamsterId = NaN;
                if (!initializeImagePaths(this)) {
                    return;
                }

                this.initialized = false;
                break;
            case 1:
                if (typeof arguments[0] === "object" || arguments[0] instanceof Hamster) {
                    // Übertrage Attribute von übergebenen Objekt auf neuen Hamster
                    Object.assign(this, row);
                    
                    // Wenn der Typ Hamster ist, wird ein neuer Hamster erstellt.
                    // Deswegen müssen die ImagePaths neu initialisiert und die HamsterId geändert werden!
                    if (arguments[0] instanceof Hamster) {
                        this.hamsterId = NaN;
                        this.imagePaths = {};
                        if (!initializeImagePaths(this)) {
                            return;
                        }
                    }
                    this.actorImage = new Image();
                    this.getActorImageSrc();
                    break;
                } else {
                    error = true;
                    stopCode();
                    if (!errorDialogOpen) {
                        openErrorDialog(languagesStrings[userLanguage]['error-wrong-parameter']);
                    }
                    return;
                }
            case 4:
                direction = convertDirection(direction);

                if (!validInputs(row, column, direction, numberOfGrains)) {
                    return;
                }
             
                this.hamsterId = NaN;
                this.imagePaths = {};
                if (!initializeImagePaths(this)) {
                    return;
                }
                this.init(row, column, direction, numberOfGrains);
                break;
            default:
                error = true;
                stopCode();
                if (!errorDialogOpen) {
                    openErrorDialog(languagesStrings[userLanguage]['error-wrong-parameter']);
                }
                return;
        }
    }

    isInitialized() {
        return this.initialized;
    }
    
    getHamsterId() {
        return this.hamsterId;
    }

    getNumberOfGrains() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }
            return false;
        }

        return this.numberOfGrains;
    }

    setNumberOfGrains(numberOfGrains) {
        this.numberOfGrains = numberOfGrains;
    }

    getDirection() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }            
            return false;
        }

        return this.direction;
    }

    setName(name) {
        this.name = name;
    }

    getActorImage() {
        return this.actorImage;
    }

    incrementNumberOfGrains() {
        this.numberOfGrains++;
    }

    decrementNumberOfGrains() {
        if (this.numberOfGrains > 0) {
            this.numberOfGrains--;
        }
    }

    /**
     * Überprüft, ob der Hamster keine Körner im Maul hat.
     * 
     * @returns {boolean}: true, falls Maul leer; sonst false.
     */
    mouthEmpty() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }            
            return false;
        }
        
        return this.numberOfGrains === 0;
    }

    /**
     * Überprüft, ob sich auf dem Feld, auf dem der Hamster steht, mindestens ein Korn liegt.
     * 
     * @returns {boolean}: true, falls mindestens ein Korn auf dem Feld; sonst false.
     */
    grainAvailable() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }            
            return false;
        }
        
        return territoryContent[this.row][this.column].hasGrain();
    }

    init(row, column, direction, numberOfGrains) {
        if (!validInputs(row, column, direction, numberOfGrains)) {
            return;
        }
        
        this.row = parseInt(row);
        this.column = parseInt(column);
        this.direction = parseInt(direction);
        this.numberOfGrains = parseInt(numberOfGrains);
        this.actorImage = new Image();
        this.getActorImageSrc();
        this.initialized = true;
        this.actorImage.onload = function () {
            drawHamsters();
        }
        drawAll();
    }
    
    getName() {
        return this.name;
    }

    getRow() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }           
            return false;
        }

        return this.row;
    }
    
    setRow(row) {
        this.row = row;
    }
    
    getColumn() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }            
            return false;
        }

        return this.column;
    }
    
    setColumn(column) {
        this.column = column;
    }

    /**
     * Bewegt den Hamster einen Schritt in seine Blickrichtung vor.
     */
    move() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }        
            return;
        }
        
        if (this.frontIsClear()) {
            territoryContent[this.row][this.column].removeHamster(this);

            switch (this.direction) {
                case 0:
                    this.row--;
                    break;
                case 1:
                    this.column++;
                    break;
                case 2:
                    this.row++;
                    break;
                case 3:
                    this.column--;
                    break;
                default:
                    error = true;
                    stopCode();
            }
            
            drawAll();
        } else {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-wall']);
            }
        }
    }

    /**
     * Dreht den Hamster um 90 Grad nach links.
     */
    turnLeft() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }       
            return;
        }
        
        switch (this.direction) {
            case 0:
                this.direction = 3;
                this.actorImage.src = this.imagePaths[3];
                break;
            case 1:
                this.direction = 0;
                this.actorImage.src = this.imagePaths[0];
                break;
            case 2:
                this.direction = 1;
                this.actorImage.src = this.imagePaths[1];
                break;
            case 3:
                this.direction = 2;
                this.actorImage.src = this.imagePaths[2];
                break;
            default:
                error = true;
                stopCode();
                if (!errorDialogOpen) {
                    openErrorDialog(languagesStrings[userLanguage]['error-unknown']);
                }
                return;
        }

        drawAll();
    }

    /**
     * Nimmt ein Korn von der aktuellen Zelle des Territoriums und gibt es dem Hamster.
     */
    pickGrain() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }           
            return;
        }
        
        if (territoryContent[this.row][this.column].hasGrain()) {
            this.incrementNumberOfGrains();
            territoryContent[this.row][this.column].decrementNumberOfGrains();
            drawAll();
        } else {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-empty-cell']);
            }
        }
    }

    /**
     * Nimmt ein Korn vom Hamster und platziert dieses auf der aktuellen Zelle des Territoriums.
     */
    putGrain() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }         
            return;
        }
        
        if (!this.mouthEmpty()) {

            territoryContent[this.row][this.column].setGrain(true);
            territoryContent[this.row][this.column].incrementNumberOfGrains();
            this.decrementNumberOfGrains();
            drawAll();
        } else {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-empty-mouth']);
            }
        }
    }
    
    /**
     * Überprüft, ob sich vor dem Hamster eine weitere Zelle befindet und ob diese eine Mauer enthält.
     *
     * @returns {boolean} true, wenn es eine Zelle gibt und diese keine Mauer enthält; sonst false.
     */
    frontIsClear() {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }          
            return false;
        }
        
        switch (this.direction) {
            case 0:
                if (this.row - 1 < 0) {
                    return false;
                }

                return !territoryContent[this.row - 1][this.column].hasWall();
            case 1:
                if (this.column + 1 >= cols) {
                    return false;
                }

                return !territoryContent[this.row][this.column + 1].hasWall();
            case 2:
                if (this.row + 1 >= rows) {
                    return false;
                }

                return !territoryContent[this.row + 1][this.column].hasWall();
            case 3:
                if (this.column - 1 < 0) {
                    return false;
                }

                return !territoryContent[this.row][this.column - 1].hasWall();
            default:
                error = true;
                stopCode();
                if (!errorDialogOpen) {
                    openErrorDialog(languagesStrings[userLanguage]['error-unknown']);
                }
                return false;
        }
    }

    /**
     * Gibt den übergebenen String in einer Dialogbox aus.
     */
    write(text) {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }      
            return;
        }
        
        return createPromiseForWriteStringDialog(text);
    }

    /**
     * Erstellt eine Benutzereingabeaufforderung mit dem übergebenen Text und liefert das Ergebnis anschließend als Wert.
     * 
     * Falls bereits ein Dialog für das Einlesen einer Zahl oder Zeichenkette geöffnet ist, wird die Funktion nach einem
     * Timeout erneut aufgerufen.
     */
    readNumber(text) {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }         
            return false;
        }
        
        if (text === undefined) {
            text = "Gib bitte eine Zahl ein";
        }
        
        return createPromiseForReadNumberDialog(text);
    }

    /**
     * Erstellt eine Benutzereingabeaufforderung mit dem übergebenen Text und liefert das Ergebnis anschließend als Wert.

     * Falls bereits ein Dialog für das Einlesen einer Zahl oder Zeichenkette geöffnet ist, wird die Funktion nach einem
     * Timeout erneut aufgerufen.
     */
    readString(text) {
        if (!this.initialized) {
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-hamster-init']);
            }
            return false;
        }

        if (text === undefined) {
            text = "Gib bitte eine Zeichenkette ein";
        }
        
        return createPromiseForReadStringDialog(text);
    }

    /**
     * Liefert den Standard-Hamster.
     *
     * @returns {hamster}, der Standard-Hamster.
     */
    static getDefaultHamster() {
        return hamster;
    }

    /**
     * Liefert die Anzahl der erzeugten und initialisierten Hamster.
     *
     * @returns {number}, Anzahl der erzeugten und initialisierten Hamster.
     */
    static getNumberOfHamster() {
        return hamsters.length;
    }

    getActorImageSrc() {
        switch (this.direction) {
            case 0:
                return this.actorImage.src = this.imagePaths[0];
            case 1:
                return this.actorImage.src = this.imagePaths[1];
            case 2:
                return this.actorImage.src = this.imagePaths[2];
            case 3:
                return this.actorImage.src = this.imagePaths[3];
            default:
                error = true;
                stopCode();
                if (!errorDialogOpen) {
                    openErrorDialog(languagesStrings[userLanguage]['error-unknown']);
                }
                return;
        }
    }
}

/**
 * Übersetzt die Blickrichtung in einen Integer-Wert.
 *
 * @param direction, Blickrichtung des Hamsters.
 * @return {null|number|*}: direction, falls es sich bereits um einen Integer handelt; 0,1,2,3 je nach Blickrichtung;
 * null falls unbekannter Wert.
 */
function convertDirection(direction) {
    if (Number.isInteger(direction)) {
        return direction;
    } else {
        switch (direction) {
            case "Hamster.NORD":
                return 0;
            case "Hamster.NORTH":
                return 0;
            case "Hamster.OST":
                return 1;
            case "Hamster.EAST":
                return 1;
            case "Hamster.SUED":
                return 2;
            case "Hamster.SOUTH":
                return 2;
            case "Hamster.WEST":
                return 3;
            default:
                return null;
        }
    }
}

/**
 * Validiert die Eingabeparameter beim Erstellen eines Hamsters.
 *
 * @param row, Reihe
 * @param column, Spalte
 * @param direction, Blickrichtung
 * @param numberOfGrains, Anzahl der Körner
 * @return {boolean}: true, wenn alle Eingaben korrekte Werte sind; sonst false.
 */
function validInputs(row, column, direction, numberOfGrains) {
    direction = convertDirection(direction);

    if (isNaN(row) || isNaN(column) || isNaN(direction) || isNaN(numberOfGrains) ||
        direction < 0 || direction > 3 || numberOfGrains < 0) {
        error = true;
        stopCode();
        if (!errorDialogOpen) {
            openErrorDialog(languagesStrings[userLanguage]['error-wrong-parameter']);
        }
        return false;
    } else if (row < 0 || row > rows-1 || column < 0 || column > cols-1 || territoryContent[row][column].hasWall()) {
        error = true;
        stopCode();
        if (!errorDialogOpen) {
            openErrorDialog(languagesStrings[userLanguage]['error-hamster-placement']);
        }
        return false;
    }
    
    return true;
}

/**
 * Fügt dem Hamster die gewünschte Anzahl an Körnern hinzu und schließt den Dialog.
 */
export function addGrainsToHamster(event) {
    event.preventDefault();
    hamster.setNumberOfGrains(Number(numberOfGrainsHamsterInput.value));
    closeAddGrainsHamsterDialog();
}

/**
 * Fügt dem Hamster-Array einen neuen Hamster hinzu.
 * Überprüft vorher, ob sich bereits ein Hamster mit dem gleichen Namen im Array befindet.
 *  
 * @param newHamster, der hinzugefügt werden soll.
 * @return {boolean}: true, wenn hinzufügen erfolgreich; false sonst.
 */
export function addToHamsterArray(newHamster) {
    if (hamsters.length === 0) {
        hamsters.push(newHamster);
        return true;
    }
    
    hamsters.forEach(function (currentHamster) {
        if (currentHamster.getName() === newHamster.getName()) {
            currentHamster.setName(undefined);
        }
    });
    hamsters.push(newHamster);
    
    return true;
}

/**
 * Liefert die aktuelle Blickrichtung des Hamsters in Deutsch.
 * 
 * @returns {string} Blickrichtung des Hamsters.
 */
export function getActorDirection() {
    switch (hamster.getDirection()) {
        case 0:
            return languagesStrings[userLanguage]['hamster-direction-north'];
        case 1:
            return languagesStrings[userLanguage]['hamster-direction-east'];
        case 2:
            return languagesStrings[userLanguage]['hamster-direction-south'];
        case 3:
            return languagesStrings[userLanguage]['hamster-direction-west'];
        default:
            error = true;
            stopCode();
            if (!errorDialogOpen) {
                openErrorDialog(languagesStrings[userLanguage]['error-unknown']);
            }
            return "";
    }
}