const LOCAL_GRID_TYPE = "GRID_DASH_TYPE";
const LOCAL_GRID_COLOR = "GRID_COLOR";
const LOCAL_GRID_WIDTH = "GRID_WIDTH";

// init - game view (map)
var mapScale = 1.0;
var mapOffsetX = 0.0;
var mapOffsetY = 0.0;
var draggedToken = { token: null, offX: 0, offY: 0 };
var draggedDiv = { div: null, offX: 0, offY: 0 };
var gridDashType = 1;
var gridLineWidth = 0;
var gridColor = 'black';
var gridSize = 200; // Grid size in pixels
var gridOffset = [0, 0];
var showGrid = true;
const LINE_DASH_TYPES = [
    [1.0, 0.0],
    [0.1, 0.8, 0.1, 0.0],
    [0, 0.1, 0.1, 0.6, 0.1, 0.1],
    [0, 0.1, 0.3, 0.2, 0.3, 0.1],
    [0.1, 0.8, 0.1],
];

function init() {
    let view = document.getElementById('game-view');
    view.onwheel = onGameViewScroll;
    view.onmousemove = onGameMouseMove;
    view.onmouseup = onGameMouseUp;
    view.oncontextmenu = () => false;
    // allow sending chat message using enter (and shift-enter for new line)
    document.getElementById('newmsg-content').onkeydown = (e) => {
        if (e.key == "Enter" && !e.shiftKey) {
            sendChatMessage();
            return false;
        }
    }
    document.getElementById('login-username').onkeydown = (e) => {
        if (e.key == 'Enter') {
            document.getElementById('login-pass').focus();
            return false;
        }
    }
    document.getElementById('login-pass').onkeydown = (e) => {
        if (e.key == 'Enter') {
            onLoginClick();
            return false;
        }
    }
    // focus on the username field for the sake of just pressing enter multiple times
    document.getElementById('login-username').focus();
    document.body.onclick = (e) => {
        document.getElementById('msg-context-menu').style.display = 'none';
    }
    document.body.onmousemove = onMoveableDivDrag;
    document.body.onmouseup = onMoveableDivMouseUp;

    // Load user grid settings from local storage
    document.getElementById('settings-grid-color').value = gridColor = localStorage.getItem(LOCAL_GRID_COLOR) ?? 'black';
    let savedGridWidth = parseInt(localStorage.getItem(LOCAL_GRID_WIDTH));
    let savedGridType = parseInt(localStorage.getItem(LOCAL_GRID_TYPE));
    document.getElementById('settings-grid-width').value = gridLineWidth = isNaN(savedGridWidth) ? 2 : savedGridWidth;
    document.getElementById('settings-grid-type').selectedIndex = gridDashType = isNaN(savedGridType) ? 0 : savedGridType;
}

function updateGrid() {
    // Draw the grid on the grid thing
    let mapBackground = document.getElementById('map-background');
    let width = mapBackground.width;
    let height = mapBackground.height;
    let svg = document.getElementById('map-grid');
    if (gridLineWidth <= 0) { gridLineWidth = 1; }

    svg.setAttribute('width', width);
    svg.setAttribute('height', height);
    svg.setAttribute('stroke', gridColor);
    svg.setAttribute('stroke-width', gridLineWidth);
    svg.setAttribute('stroke-dasharray', LINE_DASH_TYPES[gridDashType].map(v => v * gridSize).join(' '))
    svg.innerHTML = '';

    let i = 0;
    while (i <= Math.max(width, height)) {
        svg.innerHTML += `<line x1="${gridOffset[0] - gridSize}" x2="${width + gridOffset[0]}" y1="${i + gridOffset[1]}" y2="${i + gridOffset[1]}" vector-effect="non-scaling-stroke" />`;
        svg.innerHTML += `<line x1="${i + gridOffset[0]}" x2="${i + gridOffset[0]}" y1="${gridOffset[1] - gridSize}" y2="${height + gridOffset[1]}" vector-effect="non-scaling-stroke" />`;
        i += gridSize;
    }
}
function updateGridDashType(type) {
    gridDashType = Math.max(0, Math.min(LINE_DASH_TYPES.length - 1, type));
    let svg = document.getElementById('map-grid');
    svg.setAttribute('stroke-dasharray', LINE_DASH_TYPES[gridDashType].map(v => v * gridSize).join(' '))
    localStorage.setItem(LOCAL_GRID_TYPE, gridDashType);
}
function updateGridColor(color) {
    let svg = document.getElementById('map-grid');
    svg.setAttribute('stroke', color);
    gridColor = color;
    localStorage.setItem(LOCAL_GRID_COLOR, color);
}
function updateGridWidth(width) {
    let svg = document.getElementById('map-grid');
    gridLineWidth = width;
    localStorage.setItem(LOCAL_GRID_WIDTH, width);
    svg.setAttribute('stroke-width', gridLineWidth);
}

tavern.onlogin = (s) => {
    if (s) {
        let login = document.getElementById('login-screen');
        let game = document.getElementById('game');
        login.style.display = 'none';
        game.style.display = 'flex';
    }
    else {
        alert("Invalid username or password!");
    }
}
tavern.onmessage = (m) => {
    console.log(m);
    let msg = document.createElement('div');
    msg.className = 'chat-message';
    // #abusing_style_order_as_both_id_variable_and_forcing_chronological_order
    msg.style.order = m.id;
    msg.innerHTML = `
    <p style='align-self: center; margin: 4px 0;'>
        <b>${m.character ?? ''}</b>
        (${m.source})
    </p>
    <hr style='width: 75%;' />
    <p style='margin: 4px 2px;'>
        ${m.text.replace('\n', '\n<br>\n')}
    </p>
    `
    msg.oncontextmenu = (e) => {
        if (e.shiftKey) { return true; }
        let cm = document.getElementById('msg-context-menu');
        cm.style.display = 'flex';
        cm.style.top = `${e.pageY}px`;
        cm.style.left = `${e.pageX}px`;
        return false;
    }
    if (m.actions) {
        let holder = document.createElement('div');
        holder.style.display = 'flex';
        holder.style.flexWrap = 'wrap';
        msg.appendChild(holder);
        for (const act of m.actions) {
            let button = document.createElement('button');
            button.innerText = act.display_name ?? act.name;
            button.action = act;
            button.onclick = () => console.log(button.action);
            button.style.margin = '2px';
            button.onclick = () => openRollsPopup(act);
            holder.appendChild(button);
        }
    }
    let history = document.getElementById('chat-history');
    // this is to force update everytime we get a duplicate msg to allow msg editing (yay)
    let exists = Array.from(history.children).filter(e => e.style.order == m.id)[0];
    if (exists) {
        history.removeChild(exists);
    }
    history.appendChild(msg);
    msg.scrollIntoView();
}
tavern.onspawntoken = (t) => {
    console.log(t);
    let map = document.getElementById('map');
    let token = document.createElement('div');
    token.className = 'token token-transition';
    token.style.top = `${t.y * gridSize + gridOffset[1]}px`;
    token.style.left = `${t.x * gridSize + gridOffset[0]}px`;
    token.token_id = t.token_id;
    token.innerHTML = `
    <img src='${t.img}' style="width: ${gridSize}px; height: ${gridSize}px;" ondragstart='return false;'>
    `
    token.onmousedown = (e) => {
        token.classList.remove('token-transition');
        draggedToken.token = token;
        draggedToken.offX = ((e.clientX - mapOffsetX) / mapScale) - parseInt(token.style.left);
        draggedToken.offY = ((e.clientY - mapOffsetY) / mapScale) - parseInt(token.style.top);
        token.children[0].style.cursor = 'grabbing';
    }
    map.appendChild(token);
}
tavern.onmovetoken = (m) => {
    let token = Array.from(document.getElementsByClassName('token')).filter(t => t.token_id == m.token_id)[0]
    if (token) {
        token.style.top = `${(m.y * gridSize) + gridOffset[1]}px`;
        token.style.left = `${(m.x * gridSize) + gridOffset[0]}px`;
    }
}
tavern.onshowscene = (show) => {
    let map = document.getElementById('map');
    map.style.visibility = show.background == undefined ? 'hidden' : 'visible';
    // Remove existing tokens
    Array.from(map.children).filter(c => c.classList.contains('token')).forEach(c => map.removeChild(c));

    let background = document.getElementById('map-background');
    gridOffset = show.grid_offset;
    // I think this is pointless as we remove all the tokens just a couple of lines above
    // Array.from(document.getElementsByClassName('token')).forEach(t => {
    //     t.children[0].style.width = `${gridSize}px`;
    //     t.children[0].style.height = `${gridSize}px`;
    // });
    gridSize = show.grid_cell_size;
    if (show.background != undefined) {
        background.src = show.background;
        for (let token of show.tokens) {
            tavern.onspawntoken(token);
        }
    }
}
tavern.onscenelist = (list) => {
    console.log(list);
    let div = document.getElementById('scene-list');
    div.innerHTML = '';
    for (let scene of list.scenes) {
        div.innerHTML += `<button onclick='tavern.get_scene(${scene[0]});'>${scene[1]}</button>`;
    }
}
function onLoginClick() {
    let username = document.getElementById('login-username').value;
    let pass = document.getElementById('login-pass').value;
    if (username == 'test') {
        // TODO: Remove this for when im done dev-ing with this file
        tavern.onlogin(true);
    }
    tavern.login(username, pass);
}
function onGameViewScroll(event) {
    let map = document.getElementById('map');
    mapScale += (event.wheelDelta / 1800.0);
    if (mapScale < 0.1) { mapScale = 0.1; }
    map.style.transform = `scale(${mapScale})`;
}
function onGameMouseMove(event) {
    if (event.buttons == 2) {
        // right click
        let map = document.getElementById('map');
        let mult = event.ctrlKey ? 2.0 : 1.0;
        mapOffsetX += event.movementX * mult;
        mapOffsetY += event.movementY * mult;
        map.style.left = `${mapOffsetX}px`;
        map.style.top = `${mapOffsetY}px`;
    }
    else if (draggedToken.token != null && event.buttons == 1) {
        let top = (event.clientY - mapOffsetY) / mapScale - draggedToken.offY;
        let left = (event.clientX - mapOffsetX) / mapScale - draggedToken.offX;
        draggedToken.token.style.top = `${top}px`;
        draggedToken.token.style.left = `${left}px`;
    }
}
function onGameMouseUp() {
    if (draggedToken.token != null) {
        let t = draggedToken.token;
        let x = Math.floor(0.5 + (-gridOffset[0] + parseInt(t.style.left)) / gridSize);
        let y = Math.floor(0.5 + (-gridOffset[1] + parseInt(t.style.top)) / gridSize);
        let id = t.token_id;
        t.classList.add('token-transition');
        t.children[0].style.cursor = '';
        tavern.move_token(id, x, y);
        draggedToken.token = null;
    }
}
function sendChatMessage() {
    let tb = document.getElementById('newmsg-content');
    // get the msg and reset the textarea
    let text = tb.value;
    tb.value = '';
    tavern.simple_msg(text);
}
function openRollsPopup(action) {
    let holder = document.getElementById('dice-roll-holder');
    holder.innerHTML = ''; // remove all holder children
    holder.action = action;
    if (action.rolls != undefined && Array.isArray(action.rolls)) {
        let i = 0;
        for (const r of action.rolls) {
            // name (extra)     (dice_amount)d(dice_type) + constant | enabled
            console.log(r);
            let row = document.createElement('div');
            row.className = 'dice-roll-row';
            row.roll = r;
            row.innerHTML = `
            <p style='flex-grow: 1;'>${r.name} ${r.extra != null ? '(' + r.extra + ')' : ''}</p>
            <label for='dice-roll-row-${i}'>${r.dice_amount}d${r.dice_type} + ${r.constant}</label>
            <input id='dice-roll-row-${i}' type='checkbox' ${r.enabled ? 'checked' : ''} />
            `;
            holder.appendChild(row);
            i += 1;
        }
    }
    document.getElementById('dice-roll-title').innerText = action.display_name ?? action.name;
    document.getElementById('dice-roll-popup').style.display = 'flex';
}
function rollPopup() {
    // TODO: Maybe let the server roll the dice?
    // first - hide the popup
    document.getElementById('dice-roll-popup').style.display = 'none';
    // get the holder and start rolling dice
    let rolls = [];
    let holder = document.getElementById('dice-roll-holder');
    for (const h of holder.children) {
        if (h.roll && h.children[2].checked) {
            let roll = { name: h.roll.name, extra: h.roll.extra };
            let msg = '';
            let sum = 0;
            for (let i = 0; i < h.roll.dice_amount; i++) {
                // Math.random gives a value in [0, 1), so we need to add 1 at the end      
                let roll = Math.floor(Math.random() * h.roll.dice_type) + 1;
                sum += roll;
                msg += `${roll}`;
                if (i != h.roll.dice_amount - 1 || h.roll.constant != 0) {
                    msg += ' + ';
                }
            }
            if (h.roll.constant != 0) {
                sum += h.roll.constant;
                msg += `${h.roll.constant}`;
            }
            roll.result = sum;
            roll.result_text = msg;
            rolls.push(roll);
        }
    }
    console.log(rolls);
    tavern.action_result(holder.action.name, 'Louise', [], rolls);
}
function onMoveableDivMouseDown(e, id) {
    if (e.buttons == 1) {
        let div = document.getElementById(id);
        let rect = div.getBoundingClientRect();
        draggedDiv.div = div;
        draggedDiv.offX = e.clientX - rect.x;
        draggedDiv.offY = e.clientY - rect.y;

    }
}
function onMoveableDivDrag(e) {
    if (draggedDiv.div) {
        draggedDiv.div.style.right = '';
        draggedDiv.div.style.top = `${e.clientY - draggedDiv.offY}px`;
        draggedDiv.div.style.left = `${e.clientX - draggedDiv.offX}px`;
    }
}
function onMoveableDivMouseUp(e, id) {
    draggedDiv.div = null;
}
function showHideDiv(id) {
    let div = document.getElementById(id);
    div.style.display = div.style.display == 'none' ? 'flex' : 'none';
}