/*
 * Schism Tracker - a cross-platform Impulse Tracker clone
 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
 * copyright (c) 2009 Storlek & Mrs. Brisby
 * copyright (c) 2010-2012 Storlek
 * URL: http://schismtracker.org/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "headers.h"
#include "fmt.h"

/* --------------------------------------------------------------------------------------------------------- */

static int _mod_period_to_note(int period)
{
        int n;

        if (period)
                for (n = 0; n <= NOTE_LAST; n++)
                        if (period >= (32 * period_table[n % 12] >> (n / 12 + 2)))
                                return n + 1;
        return NOTE_NONE;
}

void mod_import_note(const uint8_t p[4], song_note_t *note)
{
        note->note = _mod_period_to_note(((p[0] & 0xf) << 8) + p[1]);
        note->instrument = (p[0] & 0xf0) + (p[2] >> 4);
        note->voleffect = VOLFX_NONE;
        note->volparam = 0;
        note->effect = p[2] & 0xf;
        note->param = p[3];
}

/* --------------------------------------------------------------------------------------------------------- */

const uint8_t effect_weight[FX_MAX] = {
        [FX_PATTERNBREAK]       = 248,
        [FX_POSITIONJUMP]       = 240,
        [FX_SPEED]              = 232,
        [FX_TEMPO]              = 224,
        [FX_GLOBALVOLUME]       = 216,
        [FX_GLOBALVOLSLIDE]     = 208,
        [FX_CHANNELVOLUME]      = 200,
        [FX_CHANNELVOLSLIDE]    = 192,
        [FX_TONEPORTAVOL]       = 184,
        [FX_TONEPORTAMENTO]     = 176,
        [FX_ARPEGGIO]           = 168,
        [FX_RETRIG]             = 160,
        [FX_TREMOR]             = 152,
        [FX_OFFSET]             = 144,
        [FX_VOLUME]             = 136,
        [FX_VIBRATOVOL]         = 128,
        [FX_VOLUMESLIDE]        = 120,
        [FX_PORTAMENTODOWN]     = 112,
        [FX_PORTAMENTOUP]       = 104,
        [FX_NOTESLIDEDOWN]      =  96, // IMF Hxy
        [FX_NOTESLIDEUP]        =  88, // IMF Gxy
        [FX_PANNING]            =  80,
        [FX_PANNINGSLIDE]       =  72,
        [FX_MIDI]               =  64,
        [FX_SPECIAL]            =  56,
        [FX_PANBRELLO]          =  48,
        [FX_VIBRATO]            =  40,
        [FX_FINEVIBRATO]        =  32,
        [FX_TREMOLO]            =  24,
        [FX_KEYOFF]             =  16,
        [FX_SETENVPOSITION]     =   8,
        [FX_NONE]               =   0,
};

void swap_effects(song_note_t *note)
{
        song_note_t tmp = {
                .note = note->note,
                .instrument = note->instrument,
                .voleffect = note->effect,
                .volparam = note->param,
                .effect = note->voleffect,
                .param = note->volparam,
        };
        *note = tmp;
}

int convert_voleffect(uint8_t *e, uint8_t *p, int force)
{
        switch (*e) {
        case FX_NONE:
                return 1;
        case FX_VOLUME:
                *e = VOLFX_VOLUME;
                *p = MIN(*p, 64);
                break;
        case FX_PORTAMENTOUP:
                /* if not force, reject when dividing causes loss of data in LSB, or if the final value is too
                large to fit. (volume column Ex/Fx are four times stronger than effect column) */
                if (!force && ((*p & 3) || *p > 9 * 4 + 3))
                        return 0;
                *p = MIN(*p / 4, 9);
                *e = VOLFX_PORTAUP;
                break;
        case FX_PORTAMENTODOWN:
                if (!force && ((*p & 3) || *p > 9 * 4 + 3))
                        return 0;
                *p = MIN(*p / 4, 9);
                *e = VOLFX_PORTADOWN;
                break;
        case FX_TONEPORTAMENTO:
                if (*p >= 0xf0) {
                        // hack for people who can't type F twice :)
                        *e = VOLFX_TONEPORTAMENTO;
                        *p = 0x9;
                        return 1;
                }
                for (int n = 0; n < 10; n++) {
                        if (force
                            ? (*p <= vc_portamento_table[n])
                            : (*p == vc_portamento_table[n])) {
                                *e = VOLFX_TONEPORTAMENTO;
                                *p = n;
                                return 1;
                        }
                }
                return 0;
        case FX_VIBRATO:
                if (force)
                        *p = MIN(*p, 9);
                else if (*p > 9)
                        return 0;
                *e = VOLFX_VIBRATODEPTH;
                break;
        case FX_FINEVIBRATO:
                if (force)
                        *p = 0;
                else if (*p)
                        return 0;
                *e = VOLFX_VIBRATODEPTH;
                break;
        case FX_PANNING:
                *p = MIN(64, *p * 64 / 255);
                *e = VOLFX_PANNING;
                break;
        case FX_VOLUMESLIDE:
                // ugh
                // (IT doesn't even attempt to do this, presumably since it'd screw up the effect memory)
                if (*p == 0)
                        return 0;
                if ((*p & 0xf) == 0) { // Dx0 / Cx
                        if (force)
                                *p = MIN(*p >> 4, 9);
                        else if ((*p >> 4) > 9)
                                return 0;
                        else
                                *p >>= 4;
                        *e = VOLFX_VOLSLIDEUP;
                } else if ((*p & 0xf0) == 0) { // D0x / Dx
                        if (force)
                                *p = MIN(*p, 9);
                        else if (*p > 9)
                                return 0;
                        *e = VOLFX_VOLSLIDEDOWN;
                } else if ((*p & 0xf) == 0xf) { // DxF / Ax
                        if (force)
                                *p = MIN(*p >> 4, 9);
                        else if ((*p >> 4) > 9)
                                return 0;
                        else
                                *p >>= 4;
                        *e = VOLFX_FINEVOLUP;
                } else if ((*p & 0xf0) == 0xf0) { // DFx / Bx
                        if (force)
                                *p = MIN(*p, 9);
                        else if ((*p & 0xf) > 9)
                                return 0;
                        else
                                *p &= 0xf;
                        *e = VOLFX_FINEVOLDOWN;
                } else { // ???
                        return 0;
                }
                break;
        case FX_SPECIAL:
                switch (*p >> 4) {
                case 8:
                        /* Impulse Tracker imports XM volume-column panning very weirdly:
                                XM = P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 PA PB PC PD PE PF
                                IT = 00 05 10 15 20 21 30 31 40 45 42 47 60 61 62 63
                        I'll be um, not duplicating that behavior. :) */
                        *e = VOLFX_PANNING;
                        *p = short_panning_table[*p & 0xf];
                        return 1;
                case 0: case 1: case 2: case 0xf:
                        if (force) {
                                *e = *p = 0;
                                return 1;
                        }
                        break;
                default:
                        break;
                }
                return 0;
        default:
                return 0;
        }
        return 1;
}


void read_lined_message(char *msg, slurp_t *fp, int len, int linelen)
{
        int msgsize = 0, linesize;

        while (len) {
                linesize = MIN(len, linelen);
                if (msgsize + linesize + 1 >= MAX_MESSAGE) {
                        /* Skip the rest */
                        slurp_seek(fp, len, SEEK_CUR);
                        break;
                }

                slurp_read(fp, msg, linesize);
                len -= linesize;

                msg[linesize] = '\0';
                linesize = rtrim_string(msg);
                msgsize += linesize + 1;
                msg += linesize;
                *msg++ = '\n';
        }
        *msg = '\0';
}

