const TW_MODE_NONE = 0;
const TW_MODE_ANIM = 1;
const TW_MODE_ROOM = 2;

function Twilight(canvas_id, config_override)
{
    this.config = {
        data_path: '',
        map_processor: false, 
        text_processor: false,
        wav_to_mp3: false,
        volume_master: 1,
        volume_voice: 1,
        volume_music: 0.5,
        virtual_cursor: false,
    }

    for (var key in config_override) {
        this.config[key] = config_override[key];
    }

    this.safe = null;
    this.storage = null;
    this.menu = null;
    this.touchpad = null;
    this.common_data_loaded = false;
    this.immersive_mode = true;
    this.game_element = null;
    this.text_element = null;
    this.inventory_element = null;
    this.inventory_items = null;
    this.inventory_handle = null;
    this.inventory_visible = false;
    this.canvas = null;
    this.ctx = null;
    this.intro_timeout = null;
    this.is_loading = false;
    this.playing_audio = null;
    this.platform_name = 'unknown';
    this.virtual_cursor = null;
    this.image_ignores = [
        'anim_data/puda/1983_0015.jpg',
        'anim_data/puda/5087_0018.jpg',
        'anim_data/puda/5087_0020.jpg',
        'anim_data/puda/5087_0033.jpg',
        'anim_data/puda/5090_0034.jpg',
        'anim_data/puda/5090_0035.jpg',
        'anim_data/puda/5090_0043.jpg',
        'anim_data/puda/5090_0044.jpg',
        'anim_data/saleho_byt/1399_0003.jpg',
    ];

    this.map_canvas = null;
    this.map_ctx = null;

    this.cache = {};

    this.music = [];
    this.rooms = [];
    this.items = [];

    this.current_music = 0;
    this.flags = [];
    this.inventory = [];
    this.hand = -1;

    this.animation = null;
    this.room = null;
    this.mode = TW_MODE_NONE;

    this.mouse = { x:0, y:0, click:false, rclick:false, moved:false, cursor:-1 };

    this.init(canvas_id);
}

Twilight.prototype.init = function (canvas_id)
{
    if (localStorage.volume_master != undefined) this.config.volume_master = (localStorage.volume_master);
    if (localStorage.volume_voice != undefined) this.config.volume_voice = (localStorage.volume_voice);
    if (localStorage.volume_music != undefined) this.config.volume_music = (localStorage.volume_music);
    this.update_volume();

    this.storage = new Storage();
    this.menu = new Menu(this);
    this.safe = new Safe(this);

    Platform.getPlatformName().then(((name) => {
        console.log(name);
        this.platform_name = name;
        document.getElementsByTagName('body')[0].classList.add('platform-' + name);
    }).bind(this));

    this.game_element = document.getElementById(canvas_id);
    this.loading(true);

    // create game canvas
    this.canvas = document.createElement('canvas');
    this.game_element.append(this.canvas);
    this.canvas.width = 800;
    this.canvas.height = 600;
    this.ctx = this.canvas.getContext('2d');

    this.canvas.addEventListener('mousemove', ((e) => {
        var xf = this.game_element.clientWidth / 800;
        var yf = this.game_element.clientHeight / 600;
        this.mouse.x = e.offsetX / xf;
        this.mouse.y = e.offsetY / yf;
        this.mouse.moved = true;
    }).bind(this));
    this.canvas.addEventListener('click', ((e) => {
        if (this.mode == TW_MODE_NONE) return;
        this.mouse.click = true;
    }).bind(this));
    this.canvas.addEventListener('contextmenu', ((e) => {
        e.preventDefault();
        if (this.mode == TW_MODE_NONE) return;
        this.mouse.rclick = true;
    }).bind(this));

    // virtual cursor
    if (this.config.virtual_cursor) {
        this.virtual_cursor = document.createElement('div');
        this.virtual_cursor.append(document.createElement('span'));
        this.virtual_cursor.classList.add('v-cursor');
        this.game_element.append(this.virtual_cursor);
        this.touchpad = new Touchpad(this);
        this.mouse.x = 400;
        this.mouse.y = 300;
        this.mouse.moved = true;
        setTimeout(this.update_virtual_cursor.bind(this), 500);
    }

    // cursor keys
    document.addEventListener('keydown', this.key_down.bind(this));

    // create inventory
    this.inventory_element = document.createElement('div');
    this.inventory_element.classList.add('inventory');
    this.inventory_items = document.createElement('div');
    this.inventory_items.classList.add('items');
    this.inventory_handle = document.createElement('div');
    this.inventory_handle.classList.add('handle');
    this.inventory_element.append(this.inventory_items);
    this.inventory_element.append(this.inventory_handle);
    this.game_element.append(this.inventory_element);
    this.inventory_handle.addEventListener('click', (() => {
        this.toggle_inventory();
    }).bind(this));

    // create map canvas
    this.map_canvas = document.createElement('canvas');
    this.map_canvas.width = 800;
    this.map_canvas.height = 600;
    this.map_ctx = this.map_canvas.getContext('2d');

    // assign menu buttons
    /*document.getElementById('menu-new-game').addEventListener('click', (() => {
        this.new_game();
        this.hide_menu();
    }).bind(this));
    document.getElementById('menu-continue').addEventListener('click', (() => {
        if (this.can_auto_load()) {
            this.auto_load();
        } else {
            this.new_game();
        }
        this.hide_menu();
    }).bind(this));*/

    // create subtitles
    this.text_element = document.createElement('div');
    this.text_element.classList.add('subtitles');
    this.game_element.append(this.text_element);

    // initialize global cache 
    this.init_cache();

    // load common data
    this.load_common_data().then((() => {
        this.loop();
        this.loading(false);
        this.show_menu();
    }).bind(this));
}

Twilight.prototype.key_down = function (e)
{
    if (this.mode == TW_MODE_ROOM) {
        switch (e.keyCode) {
            case 37:
                var area = this.get_room_area(15);
                if (area) this.process_area(area);
                break;
            case 38:
                var area = this.get_room_area(55);
                if (area) this.process_area(area);
                break;
            case 39:
                var area = this.get_room_area(35);
                if (area) this.process_area(area);
                break;
            case 40:
                var area = this.get_room_area(75);
                if (area) this.process_area(area);
                break;
        }
    }
}

Twilight.prototype.update_virtual_cursor = function ()
{
    if (!this.config.virtual_cursor) return;

    var xf = this.game_element.clientWidth / 800;
    var yf = this.game_element.clientHeight / 600;

    this.virtual_cursor.style.left = (this.mouse.x * xf) + 'px';
    this.virtual_cursor.style.top = (this.mouse.y * yf) + 'px';
}

Twilight.prototype.set_mode = function (mode)
{
    this.mode = mode;
    this.menu.mode_changed(mode);
}

Twilight.prototype.show_menu = function ()
{
    this.menu.show();
}

Twilight.prototype.hide_menu = function ()
{
    this.menu.hide();
}

Twilight.prototype.play_audio = function (audio)
{
    if (!audio) return;

    if (this.playing_audio) {
        this.playing_audio.pause();
        this.playing_audio.currentTime = 0;
    }
    this.playing_audio = audio;
    audio.volume = this.config.volume_master * this.config.volume_voice;
    audio.play();
}

Twilight.prototype.new_game = function ()
{
    this.safe.hide();
    this.room = null;
    this.flags = [];
    this.inventory = [];
    this.hand = -1;
    if (this.intro_timeout) clearTimeout(this.intro_timeout);
    this.intro_timeout = null;

    this.update_inventory();

    this.play_anim('anim_data/tittles/main.ani', 12).then((() => {
        this.play_anim('anim_data/intro/intro.ani', 9).then((() => { 
            this.enter_room(0);
            this.intro_timeout = setTimeout(this.intro_detective.bind(this), 1000 * 90);
        }).bind(this));
    }).bind(this));
}

Twilight.prototype.intro_detective = function ()
{
    this.intro_timeout = null;
    if (this.mode != TW_MODE_ROOM || this.is_loading) {
        this.intro_timeout = setTimeout(this.intro_detective.bind(this), 1000 * 10);
        return;
    }
    this.play_anim('anim_data/atelier_bradna/main.ani', 2).then((() => {
        this.set_flag(30);
        this.enter_room(this.room.id);
    }).bind(this));
}

Twilight.prototype.can_auto_load = function ()
{
    if (localStorage.autosave) {
        return true;
    }
    return false;
}

Twilight.prototype.auto_load = function ()
{
    if (localStorage.autosave) {
        this.load(localStorage.autosave);
        return true;
    }
    return false;
}

Twilight.prototype.auto_save = function ()
{
    if (this.room && this.mode == TW_MODE_ROOM) {
        var save_data = this.save();
        if (save_data) {
            localStorage.setItem('autosave', save_data);
            console.log('Game automatically saved!');
        }
    }
}

Twilight.prototype.set_immersive = function ()
{
    if (!this.immersive_mode) {
        this.immersive_mode = true;
        this.game_element.classList.add('immersive');
    }
}

Twilight.prototype.unset_immersive = function ()
{
    if (this.immersive_mode) {
        this.immersive_mode = false;
        this.game_element.classList.remove('immersive');
    }
}

Twilight.prototype.save = function ()
{
    if (!this.get_flag(30)) return false; // dokud se neprehraje rozhovor s komisarem, nejde ukladat
    
    var save_data = {
        room_id: this.room.id,
        flags: this.flags,
        inventory: this.inventory,
        music: this.current_music,
    }

    save_data = JSON.stringify(save_data);
    save_data = btoa(save_data);

    return save_data;
}

Twilight.prototype.load = function (save_data)
{
    this.safe.hide();
    this.set_subtitles(['']);
    if (this.intro_timeout) clearTimeout(this.intro_timeout);
    save_data = atob(save_data);
    save_data = JSON.parse(save_data);

    this.flags = save_data.flags;
    this.inventory = save_data.inventory;
    this.update_inventory();
    this.enter_room(save_data.room_id);

    if (save_data.music != undefined) {
        if (save_data.music == 3) save_data.music = 33;
        this.play_music(save_data.music);
    }
}

Twilight.prototype.random_anim = function ()
{
    var anim_file = anims[Math.floor(Math.random() * anims.length)];
    this.play_anim(anim_file).then(this.random_anim.bind(this));
}

Twilight.prototype.load_common_data = async function ()
{
    if (this.common_data_loaded) return;
    this.common_data_loaded = true;
    console.log('Loading game data...');
    this.loading(true);

    // seznam definic mistnosti
    var game_files = await this.load_text('game_data/game.txt');
    game_files = game_files.split('\n');

    // načtení a zpracování definic místností
    for (var line of game_files) {
        line = line.trim().replace(/\\/g, '/');
        if (line == 'END') break;
        if (line == '') continue;

        await this.process_game_file(line);
    }

    // předměty do inventáře
    var lines = await this.load_text('game_data/inventory.gen');
    lines = lines.split('\n');
    for (var line of lines) {
        line = line.replace(/;.*/, '').trim();
        if (line == '') continue;
        if (line == 'EOI') break;
        var parts = line.split(' ');
        if (parts.length != 5) continue;
        parts[0] = parts[0].replace(/\\/g, '/');
        parts[1] = parts[1].replace(/\\/g, '/');
        var item = {
            image: await this.load_image(parts[0]),
            image_path: parts[0],
            ani_path: parts[1],
            dispose: (parts[2] == 1),
            cursor_1: parts[3],
            cursor_2: parts[4],
        }
        this.items.push(item);
    }

    // hudba
    for (var i = 1; i <= 11; i++) {
        var song = await this.load_audio('app_data/music/' + i + '.mp3');
        song.loop = true;
        song.volume = 0.5;
        this.music.push(song);
    }

    this.loading(false);
    console.log('Game data loaded!');
}

Twilight.prototype.update_volume = function ()
{
    localStorage.setItem('volume_master', this.config.volume_master);
    localStorage.setItem('volume_voice', this.config.volume_voice);
    localStorage.setItem('volume_music', this.config.volume_music);

    for (var i = 0; i < this.music.length; i++) {
        this.music[i].volume = this.config.volume_master * this.config.volume_music;
    }

    if (this.playing_audio) this.playing_audio.volume = this.config.volume_voice * this.config.volume_master;
}

Twilight.prototype.play_music = function (i)
{
    if (i == 33) i = 3;
    if (this.current_music == i) return;

    if (this.current_music > 0) {
        this.music[this.current_music - 2].pause();
        this.music[this.current_music - 2].currentTime = 0;
    }
    
    if (i >= 2 && i <= 12) {
        this.music[i - 2].volume = this.config.volume_master * this.config.volume_music;
        this.music[i - 2].play();
        this.current_music = i;
    }
}

Twilight.prototype.process_game_file = async function (fn)
{
    var lines = await this.load_text(fn);
    lines = lines.split('\n');

    var room = { image: false, map: false, areas: [], };

    for (var line of lines) {
        line = line.replace(/;.*/g, '').replace(/\\/g, '/').trim();
        if (line == 'EOR') {
            if (room.image) this.rooms.push(room);
            room = { image: false, map: false, areas: [], };
            continue;
        }
        if (line == 'END') break;
        if (line == '') continue;
        if (line[0] == ';') continue;

        if (!room.image) room.image = line;
        else if (!room.map) room.map = line;
        else {
            // map_id action ? item_id ? ? ? ? ? file ? sub_1 sub_2
            var m = line.match(/^([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([0-9-]+)[ ]+([^ ]+)[ ]+([0-9-]+)[ ]+"([^"]*)"[ ]+"([^"]*)"/);
            if (m) {
                room.areas.push({
                    map_id: parseInt(m[1]),
                    cursor: parseInt(m[2]),
                    room_id: parseInt(m[3]),
                    item_add: parseInt(m[4]),
                    item_remove: parseInt(m[5]),
                    flag_set: parseInt(m[6]),
                    flag_unset: parseInt(m[7]),
                    item_use: parseInt(m[8]),
                    flag_check: parseInt(m[9]),
                    file: m[10],
                    cd_track: parseInt(m[11]),
                    subtitle_1: m[12],
                    subtitle_2: m[13],
                });
            }
        }
    }
}

Twilight.prototype.loading = function (state)
{
    if (state) {
        this.is_loading = true;
        this.game_element.classList.add('loading');
    } else {
        this.is_loading = false;
        this.game_element.classList.remove('loading');
    }
}

Twilight.prototype.init_cache = function ()
{
    this.cache.image = {};
    this.cache.audio = {};
    this.cache.text = {};
}

Twilight.prototype.load_image = async function (src, is_map)
{
    src = src.replace(/\\/g, '/');
    var ignore = (this.image_ignores.indexOf(src) >= 0);
    if (this.config.map_processor && is_map) {
        src = this.config.map_processor + src;
    } else {
        src = this.config.data_path + src;
    }
    return new Promise((resolve, reject) => {
        let img = new Image();
        if (ignore) {
            console.log('Ignoring missing image: ' + src);
            resolve(img);
            return;
        }
        img.onload = () => resolve(img);
        img.onerror = () => {
            resolve(img);
        } // on error we pretend nothing happened
        img.src = src;
    });
}

Twilight.prototype.load_audio = async function (src)
{
    src = this.config.data_path + src.replace(/\\/g, '/');
    if (this.config.wav_to_mp3) src = src.replace('.wav', '.mp3');
    return new Promise((resolve, reject) => {
        let audio = new Audio(src);
        audio.preload = 'auto';
        audio.addEventListener("canplaythrough", () => resolve(audio));
    });
}

Twilight.prototype.load_text = async function (src)
{
    src = src.replace(/\\/g, '/');
    if (this.config.text_processor) {
        src = this.config.text_processor + src;
    } else {
        src = this.config.data_path + src;
    }
    if (typeof fetchLocal != 'undefined') {
        const response = await fetchLocal(src);
        return response.text();
    } else {
        const response = await fetch(src);
        return response.text();
    }
}

Twilight.prototype.preload = async function (src)
{
    src = src.replace(/\\/g, '/');
    src = this.config.data_path + src;

    if (typeof fetchLocal != 'undefined') {
        const response = await fetchLocal(src);
        return response;
    } else {
        const response = await fetch(src);
        return response;
    }
}

Twilight.prototype.load_anim = async function (anim_file)
{
    console.log('Loading animation assets [' + anim_file + ']');
    this.loading(true);
    var animation = {
        audio: null,
        images: [],
        timeline: [],
        texts: [],
        image_index: 0,
        text_index: 0,
        next: false,
    };

    if (!this.cache.text[anim_file]) {
        this.cache.text[anim_file] = await this.load_text(anim_file);
    }

    var lines = this.cache.text[anim_file].split("\n");
    
    // parse animation file
    var mode = 0; // 0=assets 1=timeline 2=texts 3=next
    var text_time = 0;
    var text_lines = [];
    for (var i in lines) {
        var line = lines[i];
        line = lines[i].replace(/;.*/, '');
        if (line.trim() == '' || line[0] == ';') continue; // ignore comment or empty line
        
        if (mode == 0) { // load image and audio assets
            line = line.trim();
            if (line.trim() == 'EOJ') {
                mode++;
                continue;
            } 
            var ext = line.substring(line.length - 3, line.length);
            
            if (ext == 'wav' || ext == 'mp3') { // load audio asset
                if (!this.cache.audio[line]) this.cache.audio[line] = await this.load_audio(line);
                animation.audio = this.cache.audio[line];
            }

            if (ext == 'jpg' || ext == 'png' || ext == 'gif') { // load image asset
                if (!this.cache.image[line]) this.cache.image[line] = await this.load_image(line);
                animation.images.push(this.cache.image[line]);
            }
        }

        if (mode == 1) { // load timeline data
            if (line.trim() == 'EOFL') {
                mode++;
                continue;
            }
            var parts = line.replace(/\t+/, '|').trim().split('|');
            animation.timeline.push({ time: (parts[0] ? parseInt(parts[0]) : 0), index: (parts[1] ? parseInt(parts[1]) : false) });
        }

        if (mode == 2) { // load texts
            line = line.trim();
            if (line == '"EOTL"') { // loading finished
                animation.texts.push({
                    time: text_time,
                    lines: text_lines,
                });
                mode++;
                continue;
            }
            if (line[0] == '"') {
                line = (line.substring(1, line.length - 1));
                text_lines.push(line);
            }
            if (line.match(/^[0-9]+$/)) {
                animation.texts.push({
                    time: text_time,
                    lines: text_lines,
                });
                text_lines = [];
                text_time = parseInt(line);
            }
        }

        if (mode == 3) {
            line = line.trim();
            if (line == '') continue;
            if (line == 'EOF') { // konec filmu
                break;
            }
            animation.next = line;
        }
    }

    console.log('Animation assets loaded [' + anim_file + ']');
    this.loading(false);
    return animation;
}

Twilight.prototype.enter_room = async function (room_id)
{
    if (room_id == 1000) { // konec hry
        this.play_anim('anim_data/tittlesout/main.ani').then((() => {
            this.set_mode(TW_MODE_NONE);
            this.clear_canvas();
            this.show_menu();
        }).bind(this));
        return;
    }
    this.set_mode(TW_MODE_NONE);
    return new Promise((resolve, reject) => {
        this.load_room(room_id).then(function (room) {
            this.room = room;
            this.room.resolve = resolve;
            this.room.timestamp = Date.now();
            this.room.rendered = false;
            this.set_mode(TW_MODE_ROOM);
            this.auto_save();
            resolve();
        }.bind(this));
    });
}

Twilight.prototype.load_room = async function (room_id)
{
    console.log('Loading room assets [' + room_id + ']');
    this.loading(true);
    
    var template = this.rooms[room_id];

    var room = {
        id: room_id,
        image: null,
        map: null,
        areas: [],
    };

    if (!this.cache.image[template.image]) {
        this.cache.image[template.image] = await this.load_image(template.image);
    }
    if (!this.cache.image[template.map]) {
        this.cache.image[template.map] = await this.load_image(template.map, true);
    }

    room.image = this.cache.image[template.image];
    room.map = this.cache.image[template.map];

    this.map_ctx.clearRect(0, 0, 800, 600);
    this.map_ctx.drawImage(room.map, 0, 0);

    for (var area of template.areas) {
        area.audio = null;
        area.animation = null;
        // @TO-DO: az budu vedet kdy ocekavat zvuk, nahrati tenhle check na wav
        if (area.file.substring(area.file.length - 3, area.file.length) == 'wav') {
            if (!this.cache.audio[area.file]) {
                this.cache.audio[area.file] = await this.load_audio(area.file);
            }
            area.audio = this.cache.audio[area.file];
        }
        if (area.file.substring(area.file.length - 3, area.file.length) == 'ani') {
            area.animation = area.file;
        }
        room.areas.push(area);
    }

    console.log('Room assets loaded [' + room_id + ']');
    this.loading(false);
    return room;
}

Twilight.prototype.play_anim = async function (anim_file, cd_track)
{
    this.safe.hide();
    this.mouse.rclick = this.mouse.click = false;
    this.set_mode(TW_MODE_NONE);
    return new Promise((resolve, reject) => {
        this.load_anim(anim_file).then(function (anim) {
            this.animation = anim;
            this.animation.resolve = resolve;
            this.animation.timestamp = Date.now();
            this.set_mode(TW_MODE_ANIM);
            if (this.animation.audio) this.play_audio(this.animation.audio);
            if (cd_track != undefined) this.play_music(cd_track);
        }.bind(this));
    });
}

Twilight.prototype.clear_canvas = function ()
{
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}

Twilight.prototype.loop = function ()
{
    switch (this.mode) {
        case TW_MODE_NONE:
            this.set_immersive();
            if (this.inventory_visible) this.hide_inventory();
            this.mouse.click = this.mouse.rclick = false;
            break;
        case TW_MODE_ANIM:
            this.set_immersive();
            if (this.inventory_visible) this.hide_inventory();
            this.loop_anim();
            break;
        case TW_MODE_ROOM:
            this.unset_immersive();
            this.loop_room();
            break;
        default:
            break;
    }

    // reset mouse for next frame
    this.mouse.moved = this.mouse.click = this.mouse.rclick = false;

    window.requestAnimationFrame(this.loop.bind(this));
}

Twilight.prototype.show_inventory = function ()
{
    this.inventory_visible = true;
    this.inventory_element.classList.add('active');
}

Twilight.prototype.hide_inventory = function ()
{
    this.inventory_visible = false;
    this.inventory_element.classList.remove('active');
}

Twilight.prototype.toggle_inventory = function ()
{
    if (this.inventory_visible) {
        this.hide_inventory();
    } else {
        this.show_inventory();
    }
}

Twilight.prototype.loop_room = function ()
{
    if (!this.room.rendered) {
        this.ctx.drawImage(this.room.image, 0, 0);
        this.room.rendered = true;
    }

    if (this.mouse.rclick) {
        this.toggle_inventory();
    }

    var hand_item = this.get_hand_item();

    if (this.mouse.moved || this.mouse.click || true) {
        var map_id = this.map_ctx.getImageData(this.mouse.x, this.mouse.y, 1, 1).data[1];
        if (map_id > 0) { // jsme nad aktivní zónou
            var area = this.get_room_area(map_id);
            if (area) { // jsme nad platnou oblastí
                if (hand_item) {
                    this.set_cursor(hand_item.cursor_2);
                } else {
                    this.set_cursor(area.cursor);
                }
                if (this.mouse.click) { // hráč kliknul
                    this.process_area(area);
                }
            } else {
                if (this.mouse.click && hand_item) {
                    hand_item = false;
                    this.hand = -1;
                }
                if (hand_item) {
                    this.set_cursor(hand_item.cursor_1);
                } else {
                    this.set_cursor(-1);
                }
            }
        } else { // nejsme nad aktivní zónou
            if (this.mouse.click && hand_item) {
                hand_item = false;
                this.hand = -1;
            }
            if (hand_item) {
                this.set_cursor(hand_item.cursor_1);
            } else {
                this.set_cursor(-1);
            }
        }
    }
}

Twilight.prototype.get_hand_item = function ()
{
    if (this.hand >= 0) {
        return this.items[this.hand];
    }
    return false;
}

Twilight.prototype.set_cursor = function (c)
{
    if (this.mouse.cursor != c) {
        if (this.mouse.cursor >= 0) this.game_element.classList.remove('cursor-' + this.mouse.cursor);
        if (c >= 0) this.game_element.classList.add('cursor-' + c);

        if (this.config.virtual_cursor) {
            this.virtual_cursor.classList.remove('v-cursor-' + this.mouse.cursor);
            if (c >= 0) this.virtual_cursor.classList.add('v-cursor-' + c);
        }

        this.mouse.cursor = c;
    }
}

Twilight.prototype.set_flag = function (i)
{
    console.log('+FLAG ' + i);
    if (!this.flags.includes(i)) this.flags.push(i);
    if (i == 23 && this.get_flag(50)) this.set_flag(52);
    if (i == 58 && this.get_flag(45)) this.set_flag(56);
}

Twilight.prototype.unset_flag = function (i)
{
    console.log('-FLAG ' + i);
    if (this.flags.includes(i)) this.flags.splice(this.flags.indexOf(i), 1);
}

Twilight.prototype.get_flag = function (i)
{
    return this.flags.includes(i);
}

Twilight.prototype.add_item = function (i)
{
    console.log('+ITEM ' + i);
    if (!this.inventory.includes(i)) {
        this.inventory.push(i);
        this.update_inventory();
    }
}

Twilight.prototype.remove_item = function (i)
{
    console.log('-ITEM ' + i);
    if (this.inventory.includes(i)) {
        this.inventory.splice(this.inventory.indexOf(i), 1);
        this.update_inventory();
    }
}

Twilight.prototype.has_item = function (i)
{
    return this.inventory.includes(i);
}

Twilight.prototype.get_room_area = function (map_id)
{
    for (var area of this.room.areas) {
        if (area.map_id == map_id) {
            if (area.flag_check >= 0 && !this.get_flag(area.flag_check)) continue;
            if (area.item_use >= 0 && this.hand != area.item_use) continue;
            if (area.item_use < 0 && this.hand >= 0) continue;
            return area;
        }
    }
    return false;
}

Twilight.prototype.cd_stats = function ()
{
    var min = 9999;
    var max = -9999;
    var list = [];
    for (var i = 0; i < this.rooms.length; i++) {
        var room = this.rooms[i];
        for (var l = 0; l < room.areas.length; l++) {
            var area = room.areas[l];
            if (area.cd_track > max) max = area.cd_track;
            if (area.cd_track < min) min = area.cd_track;
            if (!list.includes(area.cd_track)) list.push(area.cd_track);
        }
    }
    return [min,max,list];
}

Twilight.prototype.find_room_by_setflag = function (needle)
{
    for (var i = 0; i < this.rooms.length; i++) {
        var room = this.rooms[i];
        for (var l = 0; l < room.areas.length; l++) {
            var area = room.areas[l];
            if (area.flag_set == needle) return i;
        }
    }
    return false;
}

Twilight.prototype.process_area = function (area)
{
    if (this.room.id == 90 && area.map_id == 255) {
        this.safe.show().then((() => {
            this.play_anim('anim_data/puda/main.ani').then((() => {
                if (this.get_flag(23)) {
                    this.set_flag(51);
                } else {
                    this.set_flag(50);
                }
                this.enter_room(89);
                this.add_item(16);
            }).bind(this));
        }).bind(this), (() => { console.log('Safe closed...'); }).bind(this));
        return;
    }

    var hand_item = this.get_hand_item();
    if (hand_item) {
        if (hand_item.dispose) this.remove_item(this.hand);
        this.hand = -1;
    }
    if (area.cd_track != 3) this.play_music(area.cd_track);
    if (area.flag_set >= 0) this.set_flag(area.flag_set);
    if (area.flag_unset >= 0) this.unset_flag(area.flag_unset);
    if (area.item_add >= 0) this.add_item(area.item_add);
    if (area.item_remove >= 0) this.remove_item(area.item_remove);
    if (area.audio) this.play_audio(area.audio);
    this.set_subtitles([area.subtitle_1, area.subtitle_2]);
    if (area.room_id >= 0) {
        if (area.animation) {
            this.play_anim(area.animation).then((() => {
                this.enter_room(area.room_id);
            }).bind(this));
        } else {
            this.enter_room(area.room_id);
        }
    } else {
        if (area.animation) {
            this.play_anim(area.animation).then((() => {
                this.enter_room(this.room.id);
            }).bind(this));
        }
    }
}

Twilight.prototype.loop_anim = function ()
{
    var time = (Date.now() - this.animation.timestamp) / 1000;
    var image = this.animation.timeline[this.animation.image_index];
    var text = this.animation.texts[this.animation.text_index];

    if (this.mouse.rclick) {
        this.set_mode(TW_MODE_NONE);
        if (this.animation.audio) this.animation.audio.pause();
        if (this.animation.audio) this.animation.audio.currentTime = 0;
        this.animation.resolve();
        this.set_subtitles(['']);
        return;
    }

    if (image.time <= time) {
        if (image.index === false) {
            this.set_mode(TW_MODE_NONE);
            if (this.animation.next) { // navazující animace
                this.load_anim(this.animation.next).then(((anim) => {
                    anim.resolve = this.animation.resolve;
                    anim.timestamp = Date.now();
                    this.animation = anim;
                    this.set_mode(TW_MODE_ANIM);
                    if (this.animation.audio) this.play_audio(this.animation.audio);
                }).bind(this));
            } else { // konec animace
                this.animation.resolve();
                this.set_subtitles(['']);
            }
        } else {
            this.clear_canvas();
            this.ctx.drawImage(this.animation.images[image.index], 0, 0);
            this.animation.image_index++;
        }
    }

    if (text.time <= time) {
        this.set_subtitles(text.lines);
        this.animation.text_index++;
    }
}

Twilight.prototype.set_subtitles = function (lines)
{
    this.text_element.innerHTML = lines.join('<br />');
}

Twilight.prototype.update_inventory = function ()
{
    // remove all elements
    while (this.inventory_items.firstChild) {
        this.inventory_items.removeChild(this.inventory_items.lastChild);
    }

    // create item elements
    for (var i of this.inventory) {
        var el = document.createElement('div');
        el.classList.add('item');
        el.style.backgroundImage = 'url(' + this.config.data_path + this.items[i].image_path + ')';
        el.dataset.itemId = i;
        el.addEventListener('click', ((e) => {
            var item_id = parseInt(e.target.dataset.itemId);
            this.hand = item_id;
            this.hide_inventory();
        }).bind(this));
        el.addEventListener('contextmenu', ((e) => {
            e.preventDefault();
            var item_id = parseInt(e.target.dataset.itemId);
            var item = this.items[item_id];
            if (item.ani_path) {
                this.play_anim(item.ani_path).then((() => {
                    this.enter_room(this.room.id);
                }).bind(this));
            }
        }).bind(this));
        this.inventory_items.append(el);
    }
}

Twilight.prototype.pause = function ()
{
    this.music[this.current_music - 2].pause();
    if (this.playing_audio) {
        this.playing_audio.pause();
    }
}

Twilight.prototype.resume = function ()
{
    this.music[this.current_music - 2].play();
}
function Menu(game)
{
    this.game = game;
    this.storage = game.storage;
    this.element = null;
    this.screen_element = null;

    this.menu_switch = null;
    this.skip_switch = null;

    this.slider_master = null;
    this.slider_voice = null;
    this.slider_music = null;

    this.book_set = 0;

    this.init();
}

Menu.prototype.init = function ()
{
    this.element = document.getElementById('menu');
    this.screen_element = document.getElementById('menu-screen');
    this.menu_switch = document.getElementById('mobile-menu-switch');
    this.skip_switch = document.getElementById('mobile-skip-switch');

    document.getElementById('menu-new').addEventListener('click', this.main_new.bind(this));
    document.getElementById('menu-continue').addEventListener('click', this.main_continue.bind(this));
    document.getElementById('menu-save').addEventListener('click', this.main_save.bind(this));
    document.getElementById('menu-load').addEventListener('click', this.main_load.bind(this));

    document.getElementById('menu-settings').addEventListener('click', this.main_settings.bind(this));
    document.getElementById('menu-about').addEventListener('click', this.main_about.bind(this));
    document.getElementById('settings-back').addEventListener('click', this.settings_back.bind(this));
    document.getElementById('book-back').addEventListener('click', this.book_back.bind(this));
    document.getElementById('menu-quit').addEventListener('click', this.main_quit.bind(this));

    document.getElementById('menu-fullscreen').addEventListener('click', this.main_fullscreen.bind(this));

    document.getElementById('book-left-handle').addEventListener('click', this.book_left.bind(this));
    document.getElementById('book-right-handle').addEventListener('click', this.book_right.bind(this));

    console.log('Menu initialized');

    document.addEventListener('keydown', this.key_down.bind(this));

    this.menu_switch.addEventListener('click', this.show.bind(this));
    this.skip_switch.addEventListener('click', this.skip_animation.bind(this));

    this.mode_changed(this.game.mode);

    this.slider_master = new MenuSlider('volume-master-slider');
    this.slider_voice = new MenuSlider('volume-voice-slider');
    this.slider_music = new MenuSlider('volume-music-slider');

    this.slider_master.set_value(this.game.config.volume_master);
    this.slider_voice.set_value(this.game.config.volume_voice);
    this.slider_music.set_value(this.game.config.volume_music);

    this.slider_master.onchange = ((i) => { this.game.config.volume_master = i; this.game.update_volume(); }).bind(this);
    this.slider_voice.onchange = ((i) => { this.game.config.volume_voice = i; this.game.update_volume(); }).bind(this);
    this.slider_music.onchange = ((i) => { this.game.config.volume_music = i; this.game.update_volume(); }).bind(this);
}

Menu.prototype.book_left = function ()
{
    if (this.book_set > 0) {
        document.getElementById('book').classList.remove('show-'+this.book_set);
        this.book_set--;
        document.getElementById('book').classList.add('show-'+this.book_set);
    }
}

Menu.prototype.book_right = function ()
{
    document.getElementById('book').classList.remove('show-'+this.book_set);
    this.book_set++;
    document.getElementById('book').classList.add('show-'+this.book_set);
}

Menu.prototype.mode_changed = function (mode)
{
    if (mode == TW_MODE_ANIM) {
        this.skip_switch.classList.add('active');
        this.menu_switch.classList.remove('active');
    } else {
        this.skip_switch.classList.remove('active');
        if (!this.visible()) this.menu_switch.classList.add('active');
    }
}

Menu.prototype.skip_animation = function ()
{
    if (this.game.mode == TW_MODE_ANIM) {
        this.game.mouse.rclick = true;
    }
}

Menu.prototype.key_down = function (e)
{
    if (e.keyCode == 27) {
        if (this.game.mode == TW_MODE_ANIM) {
            this.game.mouse.rclick = true;
            return;
        }
        if (this.element.classList.contains('active') && this.game.room) {
            this.hide();
        } else {
            this.show();
        }
        return;
    }
}

Menu.prototype.hide = function ()
{
    this.element.classList.remove('active');
    this.mode_changed(this.game.mode);
}

Menu.prototype.show = function ()
{
    this.menu_switch.classList.remove('active');
    this.element.classList.add('active');
}

Menu.prototype.visible = function ()
{
    return this.element.classList.contains('active');
}

Menu.prototype.main_new = function ()
{
    this.game.new_game();
    this.hide();
}

Menu.prototype.main_continue = async function ()
{
    if (this.game.room) {
        this.hide();
        return;
    }
    if (await this.storage.has_auto()) {
        this.game.load(await this.storage.load_auto());
        this.hide();
        return;
    }
    if (await this.storage.has_user()) {
        this.game.load(await this.storage.load_user());
        this.hide();
        return;
    }
    this.game.new_game();
    this.hide();
}

Menu.prototype.main_save = async function ()
{
    var save_data = this.game.save();
    if (save_data) {
        this.storage.save_user(save_data);
        this.game.set_subtitles(['Hra uložena.']);
        this.hide();
    } else {
        this.game.set_subtitles(['Hru teď nelze uložit.']);
        if (this.game.mode == TW_MODE_ROOM) this.hide();
    }
}

Menu.prototype.main_load = async function ()
{
    if (await this.storage.has_user()) {
        this.game.load(await this.storage.load_user());
        this.hide();
    }
}

Menu.prototype.main_settings = function ()
{
    this.element.classList.add('settings-mode');
}

Menu.prototype.settings_back = function ()
{
    this.element.classList.remove('settings-mode');
}

Menu.prototype.book_back = function ()
{
    this.element.classList.remove('book-mode');
}

Menu.prototype.main_about = function ()
{
    this.element.classList.add('book-mode');
}

Menu.prototype.main_quit = function ()
{
    Platform.quit();
}

Menu.prototype.main_fullscreen = function ()
{
    Platform.getFullscreen().then((res) => {
        Platform.setFullscreen(!res);
    });
}

function MenuSlider(id)
{
    this.el = document.getElementById(id);
    this.knob = this.el.childNodes[0];
    this.moving = false;
    this.ox = 0;
    this.onchange = null;

    this.knob.addEventListener('mousedown', this.mdown.bind(this));
    document.addEventListener('mouseup', this.mup.bind(this));
    document.addEventListener('mousemove', this.move.bind(this));

    this.knob.addEventListener('touchstart', this.tstart.bind(this));
    this.knob.addEventListener('touchend', this.tend.bind(this));
    this.knob.addEventListener('touchmove', this.tmove.bind(this));
}

MenuSlider.prototype.tstart = function (e)
{
    var f = (100 / window.innerHeight);
    this.ox = 2 / f;
    this.moving = true;
    this.update(e.touches[0].clientX);
}

MenuSlider.prototype.tend = function (e)
{
    if (!this.moving) return;
    this.moving = false;
}

MenuSlider.prototype.tmove = function (e)
{    
    if (this.moving) {
        this.update(e.touches[0].clientX);
    }
}

MenuSlider.prototype.set_value = function (i)
{
    var x = 58 * i;
    this.knob.style.left = x + 'vh';
}

MenuSlider.prototype.update = function (cx)
{
    var msb = this.el.getBoundingClientRect()
    var f = (100 / window.innerHeight);
    var x = (cx - msb.x - this.ox) * f;
    if (x > 58) x = 58;
    if (x < 0) x = 0;
    var value = x / 58;
    if (this.onchange) this.onchange(value);
    this.knob.style.left = x + 'vh';
}

MenuSlider.prototype.move = function (e)
{
    if (this.moving) this.update(e.clientX);
}

MenuSlider.prototype.mdown = function (e)
{
    this.ox = e.offsetX;
    this.moving = true;
    this.update(e.clientX);
}

MenuSlider.prototype.mup = function (e)
{
    if (!this.moving) return;
    this.update(e.clientX);
    this.moving = false;
}

function Storage()
{

}

Storage.prototype.has_auto = async function ()
{
    return (localStorage.autosave ? true : false);
}

Storage.prototype.load_auto = async function ()
{
    if (localStorage.autosave) {
        return localStorage.autosave;
    }
    return false;
}

Storage.prototype.save_auto = async function (data)
{
    localStorage.setItem('autosave', data);
}

Storage.prototype.has_user = async function ()
{
    return (localStorage.usersave ? true : false);
}

Storage.prototype.load_user = function ()
{
    if (localStorage.usersave) {
        return localStorage.usersave;
    }
    return false;
}

Storage.prototype.save_user = function (data)
{
    localStorage.setItem('usersave', data);
}

function Safe(game)
{
    this.game = game;
    this.click = null;
    this.pwd = [0,0,0,0];
    this.letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    this.resolve = null;
    this.reject = null;

    this.ok_timeout = null;

    this.init();
}

Safe.prototype.init = function ()
{
    this.game.load_audio('app_data/sfx/lock-click.mp3').then(((audio) => {
        this.click = audio;
    }).bind(this));
    
    for (var i = 1; i <= 4; i++) {
        document.getElementById('safe-switch-' + i + '-u').addEventListener('click', this.up_click.bind(this));
        document.getElementById('safe-switch-' + i + '-d').addEventListener('click', this.dn_click.bind(this));
    }

    document.getElementById('safe-close').addEventListener('click', this.hide.bind(this));
    
    this.update();
}

Safe.prototype.show = function ()
{
    this.pwd = [0,0,0,0];
    this.update();
    document.getElementById('safe').classList.add('active');
    return new Promise(((resolve, reject) => {
        this.resolve = resolve;
        this.reject = reject;
    }).bind(this));
}

Safe.prototype.hide = function ()
{
    if (this.reject) this.reject();
    this.reject = null;
    this.resolve = null;
    document.getElementById('safe').classList.remove('active');
}

Safe.prototype.get_letter = function (i)
{
    while (i < 0) i += this.letters.length;
    while (i >= this.letters.length) i -= this.letters.length;
    return this.letters[i];
}

Safe.prototype.up_click = function (e)
{
    if (this.click) {
        this.click.currentTime = 0;
        this.click.volume = this.game.config.volume_master * this.game.config.volume_voice;
        this.click.play();
    }
    var id = parseInt(e.target.id[12]);
    this.pwd[id - 1] = this.pwd[id - 1] - 1;
    this.update();
}

Safe.prototype.dn_click = function (e)
{
    if (this.click) {
        this.click.currentTime = 0;
        this.click.volume = this.game.config.volume_master * this.game.config.volume_voice;
        this.click.play();
    }
    var id = parseInt(e.target.id[12]);
    this.pwd[id - 1] = this.pwd[id - 1] + 1;
    this.update();
}

Safe.prototype.get_pwd_string = function ()
{
    return this.get_letter(this.pwd[0]) + this.get_letter(this.pwd[1]) + this.get_letter(this.pwd[2]) + this.get_letter(this.pwd[3]);
}

Safe.prototype.update = function ()
{
    var pwd = this.get_pwd_string();
    if (this.ok_timeout != null) {
        clearTimeout(this.ok_timeout);
    }
    if (pwd == 'JHWH') {
        this.ok_timeout = setTimeout((() => {
            if (this.resolve) this.resolve();
            this.reject = null;
            this.resolve = null;
            this.hide();
        }).bind(this), 500);
    }

    document.getElementById('safe-letter-1').innerHTML = this.get_letter(this.pwd[0]);
    document.getElementById('safe-letter-2').innerHTML = this.get_letter(this.pwd[1]);
    document.getElementById('safe-letter-3').innerHTML = this.get_letter(this.pwd[2]);
    document.getElementById('safe-letter-4').innerHTML = this.get_letter(this.pwd[3]);

    document.getElementById('safe-letter-1-p').innerHTML = this.get_letter(this.pwd[0] - 1);
    document.getElementById('safe-letter-2-p').innerHTML = this.get_letter(this.pwd[1] - 1);
    document.getElementById('safe-letter-3-p').innerHTML = this.get_letter(this.pwd[2] - 1);
    document.getElementById('safe-letter-4-p').innerHTML = this.get_letter(this.pwd[3] - 1);

    document.getElementById('safe-letter-1-a').innerHTML = this.get_letter(this.pwd[0] + 1);
    document.getElementById('safe-letter-2-a').innerHTML = this.get_letter(this.pwd[1] + 1);
    document.getElementById('safe-letter-3-a').innerHTML = this.get_letter(this.pwd[2] + 1);
    document.getElementById('safe-letter-4-a').innerHTML = this.get_letter(this.pwd[3] + 1);
}

const { ipcRenderer } = require('electron');

let Platform = { };

Platform.quit = function () {
    ipcRenderer.invoke('quit');
}

Platform.setFullscreen = function (state)
{
    ipcRenderer.invoke('set_fullscreen', state);
}

Platform.getFullscreen = async function ()
{
    return await ipcRenderer.invoke('is_fullscreen');
}

Platform.getPlatformName = async function ()
{
    return await ipcRenderer.invoke('platform');
}

var game;

window.addEventListener('load', () => {
    console.log('Initializing game');
    
    game = new Twilight('game', { data_path: './', wav_to_mp3: true });
});