/*
 * mtsedit/edit.c
 *
 * Copyright (C) 2019 bzt (bztsrc@gitlab)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * @brief The main edit area
 *
 */

#include "main.h"

int dx = 0, dz = 0, cx = 0, cz = 0, ox = 0, oy = 0, up = 0, zoom = 128, zz = 0, zx = 0, zs = 0, grid = 1, curronly = 0;
char dstr[32];

/**
 * Place a node
 */
void edit_placenode(int y, int z, int x, int i)
{
    int j, k;

    if(x >= 0 && x < 256 && y >= 0 && y < 256 && z >=0 && z < 256 && i != nodes[y][z][x].param0) {
        mts_getbounds(0, NULL, NULL);
        hist_add(y, z, x, nodes[y][z][x].param0, nodes[y][z][x].param2, i, nodes[y][z][x].param2);
        if(x < mix) mix = x;
        if(x > max) max = x;
        if(y < miy) miy = y;
        if(y > may) may = y;
        if(z < miz) miz = z;
        if(z > maz) maz = z;
        if(nodes[y][z][x].param0) {
            for(k = 0, j = 1; j < 15; j++)
                if(palette[j] == nodes[y][z][x].param0) { k = j; break; }
            if(!k) {
                for(j = 15; j > activeblock + 1; j--) palette[j] = palette[j - 1];
                palette[activeblock + 1] = nodes[y][z][x].param0;
            }
        }
        blocks[nodes[y][z][x].param0].numref--;
        blocks[i].numref++;
        nodes[y][z][x].param0 = i;
        nodes[y][z][x].param1 = i ? 127 : 0;
    }
    status = i ? blocks[i].name : NULL;
}

/**
 * Add node at cursor to palette
 */
void edit_pipette(int y, int z, int x)
{
    int j, k;
    if(x >= 0 && x < 256 && y >= 0 && y < 256 && z >=0 && z < 256 && nodes[y][z][x].param0) {
        if(nodes[y][z][x].param0) {
            for(k = 0, j = 1; j < 15; j++)
                if(palette[j] == nodes[y][z][x].param0) { activeblock = k = j; break; }
            if(!k) {
                for(j = 15; j > 1; j--) palette[j] = palette[j - 1];
                activeblock = 1;
                palette[activeblock] = nodes[y][z][x].param0;
            }
        }
        status = blocks[nodes[y][z][x].param0].name;
    }
}

/**
 * Zoom in
 */
int edit_zoomin()
{
    if(zoom < 256) { zoom += 32; return 1; }
    return 0;
}

/**
 * Zoom out
 */
int edit_zoomout()
{
    if(zoom > 32) { zoom -= 32; return 1; }
    return 0;
}

/**
 * Redraw the Edit Area
 */
void edit_redraw(int full)
{
    int i, j, k = 160, l, X, Z, x, y, z, sx, sy, mx, zl, s;
    unsigned char *b = (uint8_t*)&theme[THEME_TABBG];
    SDL_Rect dst, src;

    zz = dz * zoom / 128; zx = dx * zoom / 128; zs = 32 * zoom / 128;
    if(zz < 1) zz = 1;
    if(zx < 1) zx = 1;
    if(zs < 1) zs = 1;
    zl = zs - 2*(2 + zz);

    mx = (((bg->w / 2 / zx) + (bg->h / 2 / zz)) / 2) + 1;
    sx = bg->w / 2;
    sy = bg->h / 2;

    if(full) {
        dst.x = dst.y = 0; dst.w = bg->w; dst.h = bg->h;
        SDL_FillRect(bg, &dst, theme[THEME_TABBG]);
        src.x = 0; src.y = 4 * 32; dst.x = bg->w - 32; dst.y = bg->h - 32;
        src.w = src.h = dst.w = dst.h = 32;
        SDL_BlitSurface(icons, &src, bg, &dst);
        if(!curronly) {
            src.x = src.y = 0;
            /* background, layers below */
            for(y = mx > currlayer ? 0 : currlayer - mx; y < currlayer; y++) {
                for(z = -mx; z < mx; z++) {
                    for(x = -mx; x < mx; x++) {
                        dst.w = dst.h = zs;
                        dst.x = sx + zx * (x - z);
                        dst.y = sy + zz * (z + x) + zl * (currlayer - y);
                        X = 127 + x + oy + ox;
                        Z = 127 + z + oy - ox;
                        if(X < 0 || X > 255 || Z < 0 || Z > 255 || dst.x < 0 || dst.x > bg->w || dst.y < 0 || dst.y > bg->h)
                            continue;
                        i = nodes[y][Z][X].param0;
                        if(i) {
                            j = nodes[y][Z][X].param2;
                            if(blocks[i].numpar2) j %= (int)blocks[i].numpar2; else j = 0;
                            if(!blocks[i].dr) {
                                s = 32 * (blocks[i].numpar2 ? blocks[i].numpar2 : 1)*32 * 4;
                                blocks[i].dr = (unsigned char *)malloc(s);
                                if(!blocks[i].dr) error(lang[ERR_MEM]);
                                memset(blocks[i].dr, 0, s);
                                if(blocks[i].img)
                                    memcpy(blocks[i].dr, blocks[i].img, s);
                                else {
                                    j = 0;
                                    s = 32 * 32 * 4;
                                    blocks[i].numpar2 = 1;
                                    if(!blocks[i].tr) {
                                        blocks[i].tr = (unsigned char *)malloc(s);
                                        if(!blocks[i].tr) error(lang[ERR_MEM]);
                                        memset(blocks[i].tr, 0, s);
                                    }
                                    memcpy(blocks[i].tr, (uint8_t*)icons->pixels + 32 * icons->pitch, s);
                                }
                                for(l = 0; l < s; l += 4) {
                                    blocks[i].dr[l+0] = (b[0]*k + (256 - k)*blocks[i].dr[l+0])>>8;
                                    blocks[i].dr[l+1] = (b[1]*k + (256 - k)*blocks[i].dr[l+1])>>8;
                                    blocks[i].dr[l+2] = (b[2]*k + (256 - k)*blocks[i].dr[l+2])>>8;
                                }
                            }
                            blk->pixels = blocks[i].dr + j * 32 * 32 * 4;
                            if(dst.w == src.w)
                                SDL_BlitSurface(blk, &src, bg, &dst);
                            else
                                SDL_BlitScaled(blk, &src, bg, &dst);
                        }
                    }
                }
            }
        }
        /* draw grid */
        if(grid && zz > 4) {
            k = (x0 * zoom / 128);
            y = (y3 * zoom / 128);
            for(z = -mx; z < mx; z++) {
                for(x = -mx; x < mx; x++) {
                    dst.x = sx + zx * (x - z) + k;
                    dst.y = sy + zz * (z + x) + y;
                    for(i = 0; i < zx; i++) {
                        l = dst.y + ((i+1) * zz) / zx;
                        if(dst.x + i > 0 && dst.x + zl+zx < bg->w && l > 0 && l < bg->h) {
                            *((uint32_t*)((uint8_t*)bg->pixels + (dst.x + i)*4 + l * bg->pitch)) = theme[THEME_INPBG];
                            *((uint32_t*)((uint8_t*)bg->pixels + (dst.x + zl+zx-i+1)*4 + l * bg->pitch)) = theme[THEME_INPBG];
                        }
                    }
                }
            }
        }
        if(!curronly) {
            /* foreground, layers above */
            memset(fg->pixels, 0, fg->pitch * fg->h);
            mts_getbounds(0, NULL, NULL);
            k = ((max - mix) + (maz - miz) + 1) / 2;
            if(up > k) up = k;
            for(y = currlayer + 1; y < (currlayer + mx < 255 ? currlayer + mx : 255); y++) {
                for(z = -mx; z < mx; z++) {
                    for(x = -mx; x < mx; x++) {
                        dst.w = dst.h = zs;
                        dst.x = sx + zx * (x - z);
                        dst.y = sy + zz * (z + x - k + up) + zl * ((currlayer - y));
                        X = 127 + x + oy + ox;
                        Z = 127 + z + oy - ox;
                        if(X < 0 || X > 255 || Z < 0 || Z > 255 || dst.x < 0 || dst.x > fg->w || dst.y < 0 || dst.y > fg->h)
                            continue;
                        i = nodes[y][Z][X].param0;
                        if(i) {
                            j = nodes[y][Z][X].param2;
                            if(blocks[i].numpar2) j %= (int)blocks[i].numpar2; else j = 0;
                            if(!blocks[i].tr) {
                                s = 32 * (blocks[i].numpar2 ? blocks[i].numpar2 : 1)*32 * 4;
                                blocks[i].tr = (unsigned char *)malloc(s);
                                if(!blocks[i].tr) error(lang[ERR_MEM]);
                                if(blocks[i].img)
                                    memcpy(blocks[i].tr, blocks[i].img, s);
                                else {
                                    j = 0;
                                    s = 32 * 32 * 4;
                                    blocks[i].numpar2 = 1;
                                    memcpy(blocks[i].tr, (uint8_t*)icons->pixels + 32 * icons->pitch, s);
                                }
                                for(l = 0; l < s; l += 4) blocks[i].tr[l+3] >>= 3;
                            }
                            blk->pixels = blocks[i].tr + j * 32 * 32 * 4;
                            if(dst.w == src.w)
                                SDL_BlitSurface(blk, &src, fg, &dst);
                            else
                                SDL_BlitScaled(blk, &src, fg, &dst);
                        }
                    }
                }
            }
        }
    }
    /* current layer */
    memset(cl->pixels, 0, cl->pitch * cl->h);
    for(z = -mx; z < mx; z++) {
        for(x = -mx; x < mx; x++) {
            src.w = src.h = 32; dst.w = dst.h = zs; src.x = src.y = 0;
            dst.x = sx + zx * (x - z);
            dst.y = sy + zz * (z + x);
            X = 127 + x + oy + ox;
            Z = 127 + z + oy - ox;
            if(X < 0 || X > 255 || Z < 0 || Z > 255 || dst.x < 0 || dst.x > cl->w || dst.y < 0 || dst.y > cl->h) continue;
            i = nodes[currlayer][Z][X].param0;
            if((X == cx && Z == cz) || brush_selected(X, Z)) {
                blk->pixels = (uint8_t*)icons->pixels + 64 * icons->pitch;
                if(dst.w == src.w)
                    SDL_BlitSurface(blk, &src, cl, &dst);
                else
                    SDL_BlitScaled(blk, &src, cl, &dst);
            }
            if(i) {
                j = nodes[currlayer][Z][X].param2;
                if(blocks[i].numpar2) j %= (int)blocks[i].numpar2; else j = 0;
                s = 32 * 32 * 4;
                if(nodes[currlayer][Z][X].param1 & 0x80) {
                    memcpy(tmpblk, blocks[i].img ? blocks[i].img + j * s : (uint8_t*)icons->pixels + 32 * icons->pitch, s);
                    for(k = 0; k < s; k += 4)
                        if(tmpblk[k + 3])
                            tmpblk[k] = (255*64 + (256 - 64)*tmpblk[k])>>8;
                    blk->pixels = tmpblk;
                } else {
                    blk->pixels = blocks[i].img ? blocks[i].img + j * s : (uint8_t*)icons->pixels + 32 * icons->pitch;
                }
                if(dst.w == src.w)
                    SDL_BlitSurface(blk, &src, cl, &dst);
                else
                    SDL_BlitScaled(blk, &src, cl, &dst);
            }
            if((X == cx && Z == cz) || brush_selected(X, Z)) {
                blk->pixels = (uint8_t*)icons->pixels + 96 * icons->pitch;
                if(dst.w == src.w)
                    SDL_BlitSurface(blk, &src, cl, &dst);
                else
                    SDL_BlitScaled(blk, &src, cl, &dst);
            }
        }
    }
    src.x = src.y = 32; dst.y = 0; dst.x = 36; src.w = dst.w = bg->w; src.h = dst.h = bg->h;
    SDL_BlitSurface(bg, &src, screen, &dst);
    SDL_BlitSurface(cl, &src, screen, &dst);
    if(!curronly) SDL_BlitSurface(fg, &src, screen, &dst);
}

/**
 * Edit Area scrolling event handler
 */
int edit_scroll(SDL_Event *event)
{
    int lu = up, lx = ox, ly = oy;
    if(shift) {
        if(event->wheel.y) {
            up -= event->wheel.y;
            if(up < -128) up = -128;
            if(up > 127) up = 127;
        } else {
            edit_rotate(currlayer, cz, cx, event->wheel.x < 0 ? 1 : 0);
            return 1;
        }
    } else if(ctrl) {
        if(event->wheel.y > 0) return edit_zoomout();
        if(event->wheel.y < 0) return edit_zoomin();
    } else {
        oy += event->wheel.y;
        if(oy < -128) oy = -128;
        if(oy > 127) oy = 127;
        ox += event->wheel.x;
        if(ox < -128) ox = -128;
        if(ox > 127) ox = 127;
    }
    return (lu != up || lx != ox || ly != oy);
}

/**
 * Edit Area mouse over event handler
 */
void edit_mouseover(SDL_Event *event)
{
    int l, k, x, y, X = cx, Z = cz;
    x = (event->type == SDL_MOUSEMOTION ? event->motion.x : event->button.x) + 32;
    y = (event->type == SDL_MOUSEMOTION ? event->motion.y : event->button.y) + 32;

    l = (int)(y - (bg->h / 2));
    if(l < 0) l -= zz;
    l /= zz; l--;
    k = (int)(x - 36 - (bg-> w / 2) - zx/2);
    if(k < 0) k -= zx;
    k -= (l & 1 ? zx/2 : 0);
    k /= zx;
    cz = (l - k) / 2;
    cx = l - cz;
    cz += 127 + oy - ox;
    cx += 127 + oy + ox;
    if((cx != X || cz != Z) && event->type == SDL_MOUSEMOTION && event->motion.state)
        brush_place(event->motion.state == 1 ? palette[activeblock] : 0);
    status = nodes[currlayer][cz][cx].param0 ? blocks[nodes[currlayer][cz][cx].param0].name : NULL;
}

/**
 * Edit Area mouse down event handler
 */
void edit_mousedown(SDL_Event *event)
{
    if(event->button.button != 1)
        edit_rotate(currlayer, cz, cx, 1 - shift);
    else
        brush_place(palette[activeblock]);
}

/**
 * Edit Area key event handler
 */
void edit_key(SDL_Event *event)
{
    switch (event->key.keysym.sym) {
        case SDLK_ESCAPE:
        case SDLK_q: quitting = 1; break;
        case SDLK_l: sdldo(0); break;
        case SDLK_s: if(!shift) sdldo(1); break;
        case SDLK_p: sdldo(2); break;
        case SDLK_r: sdldo(4 - shift); break;
        case SDLK_e: sdldo(12); break;
        case SDLK_g: gndlayer = currlayer; break;
        case SDLK_d:
            if(nodes[currlayer][cz][cx].param0) {
                nodes[currlayer][cz][cx].param1 ^= 0x80;
                status = blocks[nodes[currlayer][cz][cx].param0].name;
            }
        break;
        case SDLK_SEMICOLON: grid ^= 1; break;
        case SDLK_a: curronly ^= 1; break;
        case SDLK_PLUS: case SDLK_EQUALS: mts_layerprob(+1); break;
        case SDLK_MINUS: mts_layerprob(-1); break;
        case SDLK_PAGEUP: if(currlayer < 255) currlayer++; break;
        case SDLK_PAGEDOWN: if(currlayer > 0) currlayer--; break;
        case SDLK_BACKQUOTE: case SDLK_0: activeblock = 0; break;
        case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: case SDLK_5:
        case SDLK_6: case SDLK_7: case SDLK_8: case SDLK_9:
            if(palette[event->key.keysym.sym - SDLK_0])
                activeblock = event->key.keysym.sym - SDLK_0;
        break;
        case SDLK_n: edit_pipette(currlayer, cz, cx); break;
        case SDLK_x: sdldo(6 + ctrl); break;
        case SDLK_c: sdldo(8 + ctrl); break;
        case SDLK_v: sdldo(10 + ctrl); break;
        case SDLK_h: dx--; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
        case SDLK_k: dx++; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
        case SDLK_u: dz--; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
        case SDLK_j: dz++; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
        case SDLK_UP:
            if(ctrl) {
                if(oy < 127) oy++;
            } else if(shift) {
                if(up > -128) up--;
            } else {
                if(cz > 0) cz--;
                status = nodes[currlayer][cz][cx].param0 ?
                    blocks[nodes[currlayer][cz][cx].param0].name : NULL;
            }
        break;
        case SDLK_DOWN:
            if(ctrl) {
                if(oy > -128) oy--;
            } else if(shift) {
                if(up < 127) up++;
            } else {
                if(cz < 255) cz++;
                status = nodes[currlayer][cz][cx].param0 ?
                    blocks[nodes[currlayer][cz][cx].param0].name : NULL;
            }
        break;
        case SDLK_LEFT:
            if(ctrl) {
                if(ox < 127) ox++;
            } else if(shift) {
                edit_rotate(currlayer, cz, cx, 1);
            } else {
                if(cx > 0) cx--;
                status = nodes[currlayer][cz][cx].param0 ?
                    blocks[nodes[currlayer][cz][cx].param0].name : NULL;
            }
        break;
        case SDLK_RIGHT:
            if(ctrl) {
                if(ox > -128) ox--;
            } else if(shift) {
                edit_rotate(currlayer, cz, cx, 0);
            } else {
                if(cx < 255) cx++;
                status = nodes[currlayer][cz][cx].param0 ?
                    blocks[nodes[currlayer][cz][cx].param0].name : NULL;
            }
        break;
        case SDLK_COMMA: edit_zoomout(); break;
        case SDLK_PERIOD: edit_zoomin(); break;
        case SDLK_BACKSPACE:
        case SDLK_DELETE:
        case SDLK_SPACE: brush_place(event->key.keysym.sym == SDLK_SPACE && !shift ? palette[activeblock] : 0); break;
        case SDLK_RETURN: brush_floodfill(!shift ? palette[activeblock] : 0); break;
        case SDLK_z: hist_undo(); break;
        case SDLK_y: hist_redo(); break;
    }
}

/**
 * Rotate a single block
 */
void edit_rotate(int y, int z, int x, int ccw)
{
    unsigned char oldparam2 = nodes[y][z][x].param2;

    if(!nodes[y][z][x].param0) return;
    if(ccw) {
        nodes[y][z][x].param2++;
    } else {
        nodes[y][z][x].param2--;
    }
    if(oldparam2 != nodes[y][z][x].param2) {
        hist_prepare(HIST_NODE, 0);
        hist_add(y, z, x, nodes[y][z][x].param0, oldparam2, nodes[y][z][x].param0, nodes[y][z][x].param2);
        hist_commit();
    }
}
