#include "includes.h"
#include "custkeys.h"
#include "dsfont.h"
#include "graphics.h"
#include "main.h"
#include "menu.h"
#include "mscreen.h"

SDL_Surface *screen;
SDL_Surface *font, *font2;
SDL_Surface *arrows, *ships, *docks, *filldial, *mscreenb, *menub;

int screenx = 320, screeny = 200, fullscreen = 0;

SDLKey key[512];
Uint32 whichkey = 0;

Uint32 col[256];

SDL_MouseMotionEvent mousepos;
Sint8 mouse[3];

FILE *sav;
char **name, **map;
int names, maps, selname, selmap;

char *world;
size_t worldlen;

struct savegame_t game;

Uint16 custkey[6];

#ifdef WIN32
int STDCALL WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR lpcmd, int nshow) {
#else
int main(int argc, char *argv[]) {
    if(argc > 1) {
        if(is_usage(argv[1])) {
            print_usage(argc ? argv[0] : "*unknown*");
            return 0;
        }
    }
#endif
    print_welcome();

    init_sdl();
    init_bmps();
    
    main_screen();

    write_prefs();

    del_bmps();
    SDL_Quit();

    return 0;
}

void print_welcome(void) {
    printf("Sail the Seas v%i.%02i by DWK\nCompiled for "
#ifdef WIN32
        "Win32"
#else
        "Linux"
#endif
        " on " __DATE__ ", " __TIME__ "\n",
        VERSION/100, VERSION%100);
}

int main_loop(int newgame) {
    int x, quit = 0;
    double a, b;
    Uint32 lastframe = 0;
    Uint32 framecount = 0;
    int pressed[5] = {0};

    if(newgame) new_game();
    else load_game();

    if(load_fg_bg()) {
        fprintf(stderr, "Error in map \"%s\": can't open .fg/.bg file\n",
            map[selmap]);
        return 0;
    }

    init_keyboard();
    init_graphics(screenx, screeny);

    while(!quit) {
        init_mouse();
        get_events(&quit);

        if(key[SDLK_ESCAPE]) quit = 1;
        if(key[SDLK_F2]) {
            save_screen(0);
        }
        if(key[SDLK_F10]) {
            quit = main_loop_menu(&newgame);
        }
        if(key[custkey[CKEY_RIGHT]]) {
            if(!pressed[1]) {
                if(game.ship[0].rot < 39) game.ship[0].rot ++;
                else game.ship[0].rot = 0;

                pressed[1] = 5;
            }
        }
        if(key[custkey[CKEY_LEFT]]) {
            if(!pressed[3]) {
                if(game.ship[0].rot > 0) game.ship[0].rot --;
                else game.ship[0].rot = 39;

                pressed[3] = 5;
            }
        }
        if(key[custkey[CKEY_ACCELERATE]]) {
            if(!pressed[0]) {
                rotate(0, -100, &a, &b, (PI*2/360) * game.ship[0].rot * 9);
                if(hypotenuse(fabs(game.ship[0].xs+a), fabs(game.ship[0].ys+b))
                    < 1000) {

                    game.ship[0].xs += a, game.ship[0].ys += b;
                }

                pressed[0] = 5;
            }
        }
        if(key[custkey[CKEY_REVERSE]]) {
            if(!pressed[2]) {
                rotate(0, 100, &a, &b, (PI*2/360) * game.ship[0].rot * 9);
                if(hypotenuse(fabs(game.ship[0].xs+a), fabs(game.ship[0].ys+b))
                    <= 1000) {

                    game.ship[0].xs += a, game.ship[0].ys += b;
                }
                pressed[2] = 5;
            }
        }
        if(key[custkey[CKEY_BRAKE]]) {
            if(!pressed[4]) {
                rotate(0, (game.ship[0].ys > 0 ? 100 : -100), &a, &b,
                    atan(game.ship[0].xs/game.ship[0].ys));  /* !!! */
                
                if(fabs(game.ship[0].xs) > fabs(a)) {
                    game.ship[0].xs += a;
                }
                else game.ship[0].xs = 0;

                if(fabs(game.ship[0].ys) > fabs(b)) {
                    game.ship[0].ys -= b;
                }
                else game.ship[0].ys = 0;

                pressed[4] = 5;
            }
        }

        if(mouse[0] < 0) {
            if(in_button(0, 0, 24, 24)) {
                quit = main_loop_menu(&newgame);
            }
            else if(in_button(30, 0, 200, 24)) {
                save_screen(0);
            }
            else if(in_button(screenx-24, 0, 24, 24)) {  /* close ("X") */
                quit = 1;
            }
        }

        if(!quit && SDL_GetTicks() >= lastframe+30) {
            lastframe = SDL_GetTicks();

            for(x = 0; x < 5; x ++) {
                if(pressed[x]) pressed[x] --;
                /*pressed[x] = 0;*/
            }

            game.ship[0].xp += game.ship[0].xs;
            game.ship[0].yp += game.ship[0].ys;

            paint_screen();
            framecount ++;
        }
    }

    init_keyboard();
    init_graphics(0, 0);

    get_paused_games();

    mouse[0] = 0;

    return --quit;
}

void paint_screen(void) {
    int x, y, z;
    /*double a, b, d;*/
    SDL_Rect rect, rect2;

    if(SDL_LockSurface(screen) < 0) {
        fatal_error("Can't lock screen");
    }

    /*rect.x = screenx/2-32, rect.y = screeny/2-32;
    rect.w = 64, rect.h = 64;*/
    SDL_FillRect(screen, NULL, gp(docks, 0, 0));

    SDL_UnlockSurface(screen);

    /*rect.x = 64 * game.ship[0].type, rect.y = 64 * game.ship[0].rot, rect.w = 64, rect.h = 64;
    rect2.x = screenx/2-32, rect2.y = screeny/2-32;
    SDL_BlitSurface(ships, &rect, screen, &rect2);*/

    rect.x = 1, rect.y = 0, rect.w = 128, rect.h = 128;
    rect2.x = screenx/2-(Sint16)(game.ship[0].xp/1000);
    rect2.y = screeny/2-(Sint16)(game.ship[0].yp/1000);
    SDL_BlitSurface(docks, &rect, screen, &rect2);

    for(x = 0; x < 64; x ++) {
        for(y = 0; y < 64; y ++) {
            if(game.ship[0].rot < 10) {
                if((z = gp(ships,
                    64*game.ship[0].type + x, 64*(game.ship[0].rot%10) + y)))
                    wp(screenx/2-32 + x, screeny/2-32 + y, z);
            }
            else if(game.ship[0].rot < 20) {
                if((z = gp(ships,
                    64*game.ship[0].type + x, 64*(game.ship[0].rot%10) + y)))
                    wp(screenx/2-32 + 63-y, screeny/2-32 + x, z);
            }
            else if(game.ship[0].rot < 30) {
                if((z = gp(ships,
                    64*game.ship[0].type + x, 64*(game.ship[0].rot%10) + y)))
                    wp(screenx/2-32 + 63-x, screeny/2-32 + 63-y, z);
            }
            else {
                if((z = gp(ships,
                    64*game.ship[0].type + x, 64*(game.ship[0].rot%10) + y)))
                    wp(screenx/2-32 + y, screeny/2-32 + 63-x, z);
            }
        }
    }

    paint_button(font, "M", 24, 0, 0);
    paint_button(font, game.ship[0].name, 200, 30, 0);
    paint_button(font, "X", 24, screenx-24, 0);

#if 0
    {  /* display the FPS */
        static time_t ti, tt;
        static Uint32 lfc;
        unsigned fc;
        char s[100];  /* !!! */ 

        if(!ti) ti = time(0);

        if((tt = time(0)) != ti) {
            ti = tt;
            fc = (unsigned)(framecount-lfc);
            lfc = framecount;
            printf(/*s,*/ "\rFPS: %u                ", fc);
            fflush(stdout);
        }

        /*dsfont(font, s, 1, screeny-10, col[COL_WHITE]);*/
    }
#endif

    /*if(key[SDLK_m]) {
        circle(screenx-21, screeny-21, 20, col[COL_WHITE]);
    }
    else {*/
        rect.x = screenx-41;
        rect.y = screeny-41;
        SDL_BlitSurface(filldial, NULL, screen, &rect);
    /*}*/

    if(game.ship[0].xs || game.ship[0].ys) {
        line(screenx-21, screeny-21, game.ship[0].xs/(1000.0/20)+screenx-21,
            game.ship[0].ys/(1000.0/20)+screeny-21, 210);
#if 0
        d = hypotenuse(game.ship[0].xs, game.ship[0].ys) / (1000.0/20);

        rotate(0, (game.ship[0].ys > 0 ? d : -d), &a, &b,
            atan(game.ship[0].xs/game.ship[0].ys));  /* !!! */
        line(screenx-21, screeny-21, -a+screenx-21, b+screeny-21,
            210);
#endif
    }

    paint_money();

    /* update the whole screen */
    SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
}

void paint_money(void) {
    char s[BUFSIZ] = "$";
    size_t len = 1;

    if(game.ship[0].money >= 1000000000) {
        sprintf(s+len, "%lu", game.ship[0].money / 1000000000);
        len = strlen(s);
    }
    if(game.ship[0].money >= 1000000) {
        if(len > 1) strcpy(s+(len++), ",");
        sprintf(s+len, "%0*lu", s[len-1]==',' ? 3 : 0,
            game.ship[0].money / 1000000 % 1000);
        len = strlen(s);
    }
    if(game.ship[0].money >= 1000) {
        if(len > 1) strcpy(s+(len++), ",");
        sprintf(s+len, "%0*lu", s[len-1]==',' ? 3 : 0,
            game.ship[0].money / 1000 % 1000);
        len = strlen(s);
    }

    if(len > 1) strcpy(s+(len++), ",");
    sprintf(s+len, "%0*lu", s[len-1]==',' ? 3 : 0, game.ship[0].money % 1000);
    len = strlen(s);

    dsfont(font, s, 1, screeny-13, col[COL_WHITE]);
}

int save_screen(int i) {
    int quit = 0;
    Uint32 lastframe = 0;
    char un[21] = {0};
    size_t len = strlen(game.ship[0].name);

    strcpy(un, game.ship[0].name);

    init_keyboard();

    while(!quit) {
        init_mouse();
        init_keyboard();
        get_events(&quit);

        if(key[SDLK_ESCAPE]) quit = 2;
        else if(key[SDLK_RETURN]) quit = 1;
        else if(key[SDLK_BACKSPACE]) {
            if(len) un[--len] = 0;
        }
        else if(len < 20 && whichkey < 256 && isprint(whichkey)) {
            if(key[SDLK_LSHIFT] || key[SDLK_RSHIFT]) {
                whichkey = shift_key(whichkey);
            }

            un[len++] = whichkey;
        }

        if(mouse[0] < 0) {
            if(in_button((screenx-200)/2, (screeny-30)/2+30,
                90, 24)) {  /* save */
                quit = 1;
            }
            else if(in_button((screenx-200)/2+100, (screeny-30)/2+30,
                90, 24)) {  /* cancel */
                quit = 2;
            }
        }

        if(!quit && SDL_GetTicks() >= lastframe+10) {
            lastframe = SDL_GetTicks();
            paint_save(un, len, i);
        }
    }

    init_keyboard();

    if(!--quit) {
        strcpy(game.ship[0].name, un);
        if(i) delete_saved_game(DELETE_REN);
        else save_game();
    }

    mouse[0] = 0;
    
    return quit;
}

void paint_save(const char *un, size_t len, int i) {
    int x, y;

    start_frame();

    SDL_UnlockSurface(screen);

    dsfont(font2, i ? "Renaming game" : "Saving game", (screenx-200)/2,
        (screeny-80)/2, col[COL_WHITE]);
    dsfont(font, game.ship[0].name, (screenx-150)/2, (screeny-30)/2, col[71]);
    dsfont(font, un, (screenx-150)/2, screeny/2,
        col[COL_WHITE]);

    paint_button(font, "Save", 90, (screenx-200)/2, (screeny-30)/2+30);
    paint_button(font, "Cancel", 90, (screenx-200)/2+100, (screeny-30)/2+30);

    for(x = 0; x < 8; x ++) {
        for(y = 0; y < 12; y ++) {
            wp((screenx-150)/2+len*8+x, screeny/2+y, col[COL_WHITE]);
        }
    }

    /* update the whole screen */
    SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
}

int read_game(struct savegame_t *game, FILE *fp) {
    int x, y, c;

    for(x = 0; x < 16; x ++) {
        for(y = 0; y < 21; y ++) {
            if((c = getc(fp)) == EOF) return 1;
            game->ship[x].name[y] = c;
        }

        if(!fscanf(fp, "%lu%u%u", (unsigned long *)&game->ship[x].money,
            (unsigned *)&game->ship[x].type, (unsigned *)&game->ship[x].rot))
            return 1;

        for(y = 0; y < 128; y ++) {
            game->ship[x].hold[y] = (getc(fp) * 256) + getc(fp);
        }

        if(!fscanf(fp, "%le%le%le%le", &game->ship[x].xp, &game->ship[x].yp,
            &game->ship[x].xs, &game->ship[x].ys)) return 1;
    }

    return 0;
}

void write_game(const struct savegame_t *game, FILE *fp) {
    int x, y;

    for(x = 0; x < 16; x ++) {
        for(y = 0; y < 21; y ++) putc(game->ship[x].name[y], fp);

        fprintf(fp, "%10lu %5u %5u", (unsigned long)game->ship[x].money,
            game->ship[x].type, game->ship[x].rot);

        for(y = 0; y < 128; y ++) {
            putc(game->ship[x].hold[y] / 256, fp);
            putc(game->ship[x].hold[y] % 256, fp);
        }

        fprintf(fp, "%20e %20e %20e %20e", game->ship[x].xp, game->ship[x].yp,
            game->ship[x].xs, game->ship[x].ys);
    }
}

int load_fg_bg(void) {
    char *fbg = malloc(worldlen+9);
    if(!fbg) fatal_error("Out of memory");

    strcpy(fbg, "maps/");
    strcat(fbg, world);
    strcat(fbg, ".fg");

    if((ships = SDL_LoadBMP(fbg)) == NULL) {
        free(fbg);
        return 1;
    }
    SDL_SetColorKey(ships, SDL_SRCCOLORKEY, col[COL_BLACK]);

    *(fbg+strlen(fbg)-2) = 'b';

    if((docks = SDL_LoadBMP(fbg)) == NULL) {
        SDL_FreeSurface(ships);
        free(fbg);
        return 1;
    }
    SDL_SetColorKey(docks, SDL_SRCCOLORKEY, col[COL_BLACK]);

    free(fbg);

    return 0;
}

void switch_screen_mode(int x, int y, int f) {
    if(x != screenx || y != screeny || f != fullscreen) {
        screenx = x, screeny = y, fullscreen = f;
        init_graphics(screenx, screeny);
    }
}

void new_world(const char *nworld) {
    char *p;

    if(worldlen) free(world);
    worldlen = strlen(nworld);
    world = malloc(worldlen+1);
    if(!world) fatal_error("Out of memory");
    strcpy(world, nworld);
    if((p = strrchr(world, '.'))) *p = 0;
    get_paused_games();
}

void new_game(void) {
    int x;

    game.ship[0].money = 1000;
    game.ship[0].xp = game.ship[0].yp = 0;
    game.ship[0].type = game.ship[0].rot = 0;
    game.ship[0].xs = game.ship[0].ys = 0;
    strcpy(game.ship[0].name, "Anonymous");

    for(x = 1; x < 16; x ++) {
        game.ship[x].type = -1;
    }
}

void load_game(void) {
    fseek(sav, selname * SAVEDGAME_SIZE, SEEK_SET);

    if(read_game(&game, sav)) fatal_error("Can't load saved game");
}

int game_exists(const char *sgame) {
    int x;

    for(x = 0; x < names; x ++) {
        if(strcmp(name[x], sgame) == 0) return x;
    }

    return -1;
}

void save_game(void) {
    int x;

    if((x = game_exists(game.ship[0].name)) >= 0) {
        fseek(sav, x * SAVEDGAME_SIZE, SEEK_SET);
        write_game(&game, sav);
        selname = x;
    }
    else {
        fseek(sav, 0, SEEK_END);
        write_game(&game, sav);
        selname = names;
    }

    fflush(sav);
}

void delete_saved_game(int i) {
    char *save, dup[21];
    struct savegame_t sg, other, prev;
    int x, count = 0;
    size_t len;
    FILE *tmp;

    if((tmp = fopen(TEMPFILE, "wt+")) == NULL) {
        fprintf(stderr, "Can't open temporary file; saved game not d%sted\n",
            i ? "uplica" : "ele");
        return;
    }

    rewind(sav);

    if(i == DELETE_MUP) {
        if(read_game(&prev, sav))
            fprintf(stderr, "Can't read first saved game\n");
        else {
            while(!read_game(&sg, sav)) {
                if(++count < selname) {
                    write_game(&prev, tmp);
                }
                else if(count == selname) {
                    write_game(&sg, tmp);
                    write_game(&prev, tmp);
                }
                else {
                    write_game(&sg, tmp);
                }

                prev = sg;
            }
        }
    }
    else while(!read_game(&sg, sav)) {
        if(count++ != selname) {
            write_game(&sg, tmp);
        }
        else if(i != DELETE_DEL) {
            if(i == DELETE_REN) {
                strcpy(sg.ship[0].name, game.ship[0].name);
                write_game(&sg, tmp);
            }
            else if(i == DELETE_DUP) {
                write_game(&sg, tmp);

                strcpy(dup, sg.ship[0].name);
                len = strlen(dup);
                if(len > 16) len = 16;
                strcpy(dup+len, "_");
                len ++;

                for(x = 0; x < 1000; x ++) {
                    sprintf(dup+len, "%03i", x);

                    if(game_exists(dup) < 0) {
                        strcpy(sg.ship[0].name, dup);
                        write_game(&sg, tmp);
                        break;
                    }
                }

                if(x == 1000) {
                    fprintf(stderr, "Too many duplicates -- "
                        "not duplicating\n");
                }
            }
            else if(i == DELETE_MDN) {
                if(read_game(&other, sav)) {
                    fprintf(stderr, "Can't read next saved game\n");
                }
                else {
                    write_game(&other, tmp);
                    write_game(&sg, tmp);
                }
            }
        }
    }

    save = malloc(worldlen+10);
    if(!save) fatal_error("Out of memory");
    strcpy(save, "sav/");
    strcat(save, world);
    strcat(save, ".sav");

    fclose(tmp);
    fclose(sav);
    sav = 0;

    if(copy_file(TEMPFILE, save)) {
        fprintf(stderr, "Can't copy file; saved game not d%sted\n",
            i ? "uplica" : "ele");
    }

    free(save);

    remove(TEMPFILE);

    if(i == DELETE_DEL && selname == names-1) selname --;

    get_paused_games();
}

void get_paused_games(void) {
    int x;
    char **p, *saved = malloc(worldlen+10);
    struct savegame_t sg;

    if(!saved) fatal_error("Out of memory");
    strcpy(saved, "sav/");
    strcat(saved, world);
    strcat(saved, ".sav");

    if(sav) fclose(sav);

    if(name) {
        for(x = 0; x < names; x ++) {
            free(name[x]);
        }

        free(name);
        name = 0;
    }
    names = 0;

    if((sav = fopen(saved, "rt+")) == NULL) {
        fprintf(stderr, "No saved games file for %s, creating it\n", world);

        if((sav = fopen(saved, "wt")) == NULL) {
            fprintf(stderr, "Can't make saved games file\n");
        }
        else fclose(sav);

        if((sav = fopen(saved, "rt+")) == NULL) {
            fatal_error("Can't re-open saved games file");
        }

        free(saved);
    }
    else {
        free(saved);
        while(!read_game(&sg, sav)) {
            p = realloc(name, (names+1) * sizeof(char *));
            if(!p) fatal_error("Out of memory");
            name = p;

            name[names] = malloc(strlen(sg.ship[0].name)+1);
            if(!name[names]) fatal_error("Out of memory");
            strcpy(name[names], sg.ship[0].name);

            names ++;
        }
    }
}

void get_maps(void) {
    int x;
    char s[FILENAME_MAX], **p;
    FILE *fp;

    if(map) {
        for(x = 0; x < maps; x ++) {
            free(map[x]);
        }

        free(map);
        map = 0;
    }
    maps = 0;

    if((fp = fopen("./maps.dat", "rt")) == NULL) {
        fprintf(stderr, "No maps dat file, creating it\n");

        if((fp = fopen("./maps.dat", "wt")) == NULL) {
            fprintf(stderr, "Can't make maps dat file\n");
        }
        else fclose(fp);
    }
    else {
        while(fgets(s, sizeof(s), fp)) {
            chomp(s);

            if(!*s) continue;

            p = realloc(map, (maps+1) * sizeof(char *));
            if(!p) fatal_error("Out of memory");
            map = p;

            map[maps] = malloc(strlen(s)+1);
            if(!map[maps]) fatal_error("Out of memory");
            strcpy(map[maps], s);

            maps ++;
        }

        fclose(fp);
    }

    if(!map) fprintf(stderr, "There are no maps installed\n");
}

void get_prefs(void) {
    int c;
    FILE *fp;

    if((fp = fopen("./prefs.dat", "rt")) == NULL) {
        fprintf(stderr, "No prefs file, creating it\n");

        write_prefs();

        set_default_keys();
    }
    else {
        if((c = getc(fp)) == EOF) {
            fprintf(stderr, "Can't read prefs file\n");
        }

        if(c == '1') fullscreen = 1;
        else if(c != '0') {
            fprintf(stderr, "Error in prefs file\n");
            fclose(fp);
            write_prefs();
            return;
        }

        c = getc(fp);
        
        if(c == '1') screenx = 640, screeny = 480;
        else if(c == '2') screenx = 800, screeny = 600;
        else if(c == '3') screenx = 1024, screeny = 768;
        else if(c != '0') {
            fprintf(stderr, "Error in prefs file\n");
            fclose(fp);
            write_prefs();
            return;
        }

        get_custkeys(fp);
        
        fclose(fp);
    }
}

void get_custkeys(FILE *fp) {
    size_t x;
    int y;
    
    for(x = 0; x < sizeof(custkey)/sizeof(*custkey); x ++) {
        y = (getc(fp) * 256) + getc(fp);
        if(y) custkey[x] = y;
    }
}

void set_default_keys(void) {
    custkey[CKEY_ACCELERATE] = SDLK_UP;
    custkey[CKEY_LEFT] = SDLK_LEFT;
    custkey[CKEY_RIGHT] = SDLK_RIGHT;
    custkey[CKEY_REVERSE] = SDLK_DOWN;
    custkey[CKEY_BRAKE] = SDLK_BACKSPACE;
}

void write_prefs(void) {
    size_t x;
    FILE *fp;

    if((fp = fopen("./prefs.dat", "wt")) == NULL) {
        fprintf(stderr, "Can't make prefs file\n");
    }
    else {
        if(fullscreen) putc('1', fp);
        else putc('0', fp);

        if(screenx == 320 && screeny == 200) putc('0', fp);
        else if(screenx == 640 && screeny == 480) putc('1', fp);
        else if(screenx == 800 && screeny == 600) putc('2', fp);
        else putc('3', fp);

        for(x = 0; x < sizeof(custkey)/sizeof(*custkey); x ++) {
            putc(custkey[x] / 256, fp);
            putc(custkey[x] % 256, fp);
        }
        
        fclose(fp);
    }
}

int copy_file(const char *onefn, const char *twofn) {
    int x;
    FILE *one, *two;

    if((one = fopen(onefn, "rt")) == NULL) return 1;
    if((two = fopen(twofn, "wt")) == NULL) {
        fclose(one);
        return 1;
    }

    while((x = getc(one)) != EOF) putc(x, two);

    fclose(one);
    fclose(two);

    return 0;
}

void open_help(void) {
    system(VIEW_README);

    init_keyboard();
}

char shift_key(char key) {
    const char *from = "`-=[]\\;',./1234567890";
    const char *to = "~_+{}|:\"<>?!@#$%^&*()";
    const char *p;
    
    if(isalpha(key)) key = toupper(key);
    else {
        if((p = strchr(from, key))) key = *(to+(p-from));
    }

    return key;
}

const char *key_name(Uint32 key) {
    static char s[2] = {0};

    if(key < 256 && isgraph(key)) {
        s[0] = toupper(key);
        return s;
    }
    else switch(key) {
    case SDLK_TAB: return "tab";
    case SDLK_CLEAR: return "clear";
    case SDLK_RETURN: return "enter";
    case SDLK_PAUSE: return "pause";
    case SDLK_DELETE: return "delete";
    case SDLK_KP0: return "kp 0";
    case SDLK_KP1: return "kp 1";
    case SDLK_KP2: return "kp 2";
    case SDLK_KP3: return "kp 3";
    case SDLK_KP4: return "kp 4";
    case SDLK_KP5: return "kp 5";
    case SDLK_KP6: return "kp 6";
    case SDLK_KP7: return "kp 7";
    case SDLK_KP8: return "kp 8";
    case SDLK_KP9: return "kp 9";
    case SDLK_KP_PERIOD: return "kp .";
    case SDLK_KP_DIVIDE: return "kp /";
    case SDLK_KP_MULTIPLY: return "kp *";
    case SDLK_KP_MINUS: return "kp -";
    case SDLK_KP_PLUS: return "kp +";
    case SDLK_KP_ENTER: return "kp enter";
    case SDLK_KP_EQUALS: return "kp =";
    case SDLK_UP: return "up";
    case SDLK_DOWN: return "down";
    case SDLK_RIGHT: return "right";
    case SDLK_LEFT: return "left";
    case SDLK_INSERT: return "insert";
    case SDLK_HOME: return "home";
    case SDLK_END: return "end";
    case SDLK_PAGEUP: return "page up";
    case SDLK_PAGEDOWN: return "page down";
    case SDLK_F1: return "F1";
    case SDLK_F2: return "F2";
    case SDLK_F3: return "F3";
    case SDLK_F4: return "F4";
    case SDLK_F5: return "F5";
    case SDLK_F6: return "F6";
    case SDLK_F7: return "F7";
    case SDLK_F8: return "F8";
    case SDLK_F9: return "F9";
    case SDLK_F10: return "F10";
    case SDLK_F11: return "F11";
    case SDLK_F12: return "F12";
    case SDLK_F13: return "F13";
    case SDLK_F14: return "F14";
    case SDLK_F15: return "F15";
    case SDLK_NUMLOCK: return "numlock";
    case SDLK_CAPSLOCK: return "capslock";
    case SDLK_SCROLLOCK: return "scrollock";
    case SDLK_RSHIFT: return "R shift";
    case SDLK_LSHIFT: return "L shift";
    case SDLK_RCTRL: return "R ctrl";
    case SDLK_LCTRL: return "L ctrl";
    case SDLK_RALT: return "R alt";
    case SDLK_LALT: return "L alt";
    case SDLK_RMETA: return "R meta";
    case SDLK_LMETA: return "L meta";
    case SDLK_LSUPER: return "L win";
    case SDLK_RSUPER: return "R win";
    case SDLK_MODE: return "mode";
    case SDLK_HELP: return "help";
    case SDLK_PRINT: return "PrtScr";
    case SDLK_SYSREQ: return "SysRq";
    case SDLK_BREAK: return "break";
    case SDLK_MENU: return "menu";
    case SDLK_POWER: return "power";
    case SDLK_EURO: return "euro";
    case SDLK_BACKSPACE: return "bkspace";
    case SDLK_SPACE: return "space";
    }
    
    return "";
}

void save_print_screen(void) {
    int n;
    char s[12] = "ps/";
    FILE *t;

    for(n = 0; n < 10000; n ++) {
        sprintf(s+3, "%04i.bmp", n);
        if((t = fopen(s, "r")) == NULL) break;
        else fclose(t);
    }

    if(n < 10000) {
        SDL_SaveBMP(screen, s);
        fprintf(stderr, "Printscreen saved as %s\n", s);
    }
    else fprintf(stderr, "Too many printscreens, not making a new one\n");
}

void init_bmps(void) {
    if((font = SDL_LoadBMP("data/font.bmp")) == NULL)
        fatal_error("Can't open data/font.bmp");
    if((font2 = SDL_LoadBMP("data/font2.bmp")) == NULL)
        fatal_error("Can't open data/font2.bmp");

    if((arrows = SDL_LoadBMP("data/arrows.bmp")) == NULL)
        fatal_error("Can't open data/arrows.bmp");
    SDL_SetColorKey(arrows, SDL_SRCCOLORKEY, col[COL_BLACK]);
    if((filldial = SDL_LoadBMP("data/filldial.bmp")) == NULL)
        fatal_error("Can't open data/filldial.bmp");
    SDL_SetColorKey(filldial, SDL_SRCCOLORKEY, col[COL_BLACK]);
    
    if((mscreenb = SDL_LoadBMP("data/mscreenb.bmp")) == NULL)
        fatal_error("Can't open data/mscreenb.bmp");
    if((menub = SDL_LoadBMP("data/menub.bmp")) == NULL)
        fatal_error("Can't open data/menub.bmp");
}

void del_bmps(void) {
    SDL_FreeSurface(font);
    SDL_FreeSurface(font2);
    SDL_FreeSurface(arrows);
    SDL_FreeSurface(filldial);
    SDL_FreeSurface(mscreenb);
    SDL_FreeSurface(menub);
}

void init_keyboard(void) {
    int x;

    for(x = 0; x < 512; x ++) {
        if(x != SDLK_LSHIFT && x != SDLK_RSHIFT) key[x] = 0;
    }
}

void init_mouse(void) {
    int x;

    for(x = 0; x < 3; x ++) {
        if(mouse[x] < 0) mouse[x] = 0;
    }
}

void set_mouse_buttons(Uint8 button, int i) {
    switch(button) {
    case SDL_BUTTON_LEFT: mouse[0] = i; break;
    case SDL_BUTTON_RIGHT: mouse[2] = i; break;
    case SDL_BUTTON_MIDDLE: mouse[1] = i; break;
    }
}

void get_events(int *quit) {
    SDL_Event event;

    whichkey = 0;

    while(SDL_PollEvent(&event)) {
        switch(event.type) {
        case SDL_KEYDOWN:
            if(event.key.keysym.sym == SDLK_F9) save_print_screen();
            else {
                key[event.key.keysym.sym] = 1;
                whichkey = event.key.keysym.sym;
            }
            break;
        case SDL_KEYUP:
            key[event.key.keysym.sym] = 0;
            break;
        case SDL_MOUSEMOTION:
            mousepos = event.motion;
            break;
        case SDL_MOUSEBUTTONUP:
            set_mouse_buttons(event.button.button, -1);
            break;
        case SDL_MOUSEBUTTONDOWN:
            set_mouse_buttons(event.button.button, 1);
            break;
        case SDL_QUIT:
            *quit = 1;
            break;
        }
    }
}

double hypotenuse(double x, double y) {
    return sqrt(x*x + y*y);
}

void rotate(double xp, double yp, double *nxp, double *nyp, double angle) {
    *nxp = (xp*cos(angle) - yp*sin(angle));
    *nyp = (yp*cos(angle) + xp*sin(angle));
}

void fatal_error(const char *s) {
    char *sdlerror = SDL_GetError();
    FILE *log = fopen(LOGFILE, "at");

    fprintf(stderr, "Fatal error: %s [SDL: %s]\n", s, sdlerror);
    if(log) {
        fprintf(log, "Fatal error: %s [SDL: %s]\n", s, sdlerror);
        fclose(log);
    }

    SDL_Quit();
    exit(1);
}

int is_usage(const char *s) {
    return (strchr(s, '?')
        || strcmp(s, "-h") == 0
        || strcmp(s, "-v") == 0
        || strcmp(s, "--help") == 0
        || strcmp(s, "--version") == 0);
}

int print_usage(const char *progname) {
    print_welcome();
    printf("Executable path: %s\n"
        "\nusage: sailseas [--version]\n"
        "\nSail the Seas is a cross-platform ocean-based game."
        "\nSee the readme.txt file that should have come with this "
        "distribution, or visit\n"
        "http://dwks.theprogrammingsite.com/myprogs/sailseas.htm to get the "
        "distri-\nbution.\n",
        progname);

    return 0;
}

void chomp(char *s) {
    while(*s && *s != '\n' && *s != '\r') s++;

    *s = 0;
}
