#include "global.h"
#include "m4a.h"
#include "main.h"
#include "pokedex_cry_screen.h"
#include "palette.h"
#include "sprite.h"
#include "ewram.h"
#include "trig.h"
#include "sound.h"

extern struct SoundInfo gSoundInfo;
extern struct MusicPlayerInfo gMPlayInfo_BGM;

struct PokedexCryMeterNeedle {
    s8 rotation;
    s8 targetRotation;
    u8 moveIncrement;
    u16 spriteId;
};

struct PokedexCryScreen
{
    u8 cryWaveformBuffer[0x10];
    u8 cryState;
    u8 playhead;
    u8 waveformPreviousY;
    u16 charBase;
    u8 playStartPos;
    u16 species;
    u8 cryOverrideCountdown;
    u8 cryRepeatDelay;
};

void PlayCryScreenCry(u16 species);
void BufferCryWaveformSegment(void);
void DrawWaveformFlatline(void);
void AdvancePlayhead(u8 bgId);
void DrawWaveformSegment(u8 position, u8 amplitude);
void DrawWaveformWindow(void);
void ShiftWaveformOver(u8 bgId, s16 pos, u8 axis);
void SpriteCB_CryMeterNeedle(struct Sprite *);
void SetCryMeterNeedleTarget(s8);

u8 gDexCryScreenState;

// data/pokedex_cry_screen.o

const u16 CryMeterNeedlePalette[] = INCBIN_U16("graphics/pokedex/cry_meter_needle.gbapal");
const u8 CryMeterNeedleTiles[] = INCBIN_U8("graphics/pokedex/cry_meter_needle.4bpp");

#if ENGLISH
const u16 gCryMeter_Tilemap[] = INCBIN_U16("graphics/pokedex/cry_meter_map.bin");
const u16 gCryMeter_Pal[] = INCBIN_U16("graphics/pokedex/cry_meter.gbapal");
const u8 gCryMeter_Gfx[] = INCBIN_U8("graphics/pokedex/cry_meter.4bpp.lz");
#else
extern const u16 gCryMeter_Tilemap[];
extern const u16 gCryMeter_Pal[];
extern const u8 gCryMeter_Gfx[];
#endif // ENGLISH

const u16 sWaveformOffsets[][72] = {
    {
        0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C,
        0x0400, 0x0404, 0x0408, 0x040C, 0x0410, 0x0414, 0x0418, 0x041C,
        0x0800, 0x0804, 0x0808, 0x080C, 0x0810, 0x0814, 0x0818, 0x081C,
        0x0C00, 0x0C04, 0x0C08, 0x0C0C, 0x0C10, 0x0C14, 0x0C18, 0x0C1C,
        0x1000, 0x1004, 0x1008, 0x100C, 0x1010, 0x1014, 0x1018, 0x101C,
        0x1400, 0x1404, 0x1408, 0x140C, 0x1410, 0x1414, 0x1418, 0x141C,
        0x1800, 0x1804, 0x1808, 0x180C, 0x1810, 0x1814, 0x1818, 0x181C,
        0x1C00, 0x1C04, 0x1C08, 0x1C0C, 0x1C10, 0x1C14, 0x1C18, 0x1C1C,
        0x2000, 0x2004, 0x2008, 0x200C, 0x2010, 0x2014, 0x2018, 0x201C
    }, {
        0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C,
        0x0400, 0x0404, 0x0408, 0x040C, 0x0410, 0x0414, 0x0418, 0x041C,
        0x0800, 0x0804, 0x0808, 0x080C, 0x0810, 0x0814, 0x0818, 0x081C,
        0x0C00, 0x0C04, 0x0C08, 0x0C0C, 0x0C10, 0x0C14, 0x0C18, 0x0C1C,
        0x1000, 0x1004, 0x1008, 0x100C, 0x1010, 0x1014, 0x1018, 0x101C,
        0x1400, 0x1404, 0x1408, 0x140C, 0x1410, 0x1414, 0x1418, 0x141C,
        0x1800, 0x1804, 0x1808, 0x180C, 0x1810, 0x1814, 0x1818, 0x181C,
        0x1C00, 0x1C04, 0x1C08, 0x1C0C, 0x1C10, 0x1C14, 0x1C18, 0x1C1C,
        0x2000, 0x2004, 0x2008, 0x200C, 0x2010, 0x2014, 0x2018, 0x201C
    }, {
        0x0001, 0x0005, 0x0009, 0x000D, 0x0011, 0x0015, 0x0019, 0x001D,
        0x0401, 0x0405, 0x0409, 0x040D, 0x0411, 0x0415, 0x0419, 0x041D,
        0x0801, 0x0805, 0x0809, 0x080D, 0x0811, 0x0815, 0x0819, 0x081D,
        0x0C01, 0x0C05, 0x0C09, 0x0C0D, 0x0C11, 0x0C15, 0x0C19, 0x0C1D,
        0x1001, 0x1005, 0x1009, 0x100D, 0x1011, 0x1015, 0x1019, 0x101D,
        0x1401, 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D,
        0x1801, 0x1805, 0x1809, 0x180D, 0x1811, 0x1815, 0x1819, 0x181D,
        0x1C01, 0x1C05, 0x1C09, 0x1C0D, 0x1C11, 0x1C15, 0x1C19, 0x1C1D,
        0x2001, 0x2005, 0x2009, 0x200D, 0x2011, 0x2015, 0x2019, 0x201D
    }, {
        0x0001, 0x0005, 0x0009, 0x000D, 0x0011, 0x0015, 0x0019, 0x001D,
        0x0401, 0x0405, 0x0409, 0x040D, 0x0411, 0x0415, 0x0419, 0x041D,
        0x0801, 0x0805, 0x0809, 0x080D, 0x0811, 0x0815, 0x0819, 0x081D,
        0x0C01, 0x0C05, 0x0C09, 0x0C0D, 0x0C11, 0x0C15, 0x0C19, 0x0C1D,
        0x1001, 0x1005, 0x1009, 0x100D, 0x1011, 0x1015, 0x1019, 0x101D,
        0x1401, 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D,
        0x1801, 0x1805, 0x1809, 0x180D, 0x1811, 0x1815, 0x1819, 0x181D,
        0x1C01, 0x1C05, 0x1C09, 0x1C0D, 0x1C11, 0x1C15, 0x1C19, 0x1C1D,
        0x2001, 0x2005, 0x2009, 0x200D, 0x2011, 0x2015, 0x2019, 0x201D
    }, {
        0x0002, 0x0006, 0x000A, 0x000E, 0x0012, 0x0016, 0x001A, 0x001E,
        0x0402, 0x0406, 0x040A, 0x040E, 0x0412, 0x0416, 0x041A, 0x041E,
        0x0802, 0x0806, 0x080A, 0x080E, 0x0812, 0x0816, 0x081A, 0x081E,
        0x0C02, 0x0C06, 0x0C0A, 0x0C0E, 0x0C12, 0x0C16, 0x0C1A, 0x0C1E,
        0x1002, 0x1006, 0x100A, 0x100E, 0x1012, 0x1016, 0x101A, 0x101E,
        0x1402, 0x1406, 0x140A, 0x140E, 0x1412, 0x1416, 0x141A, 0x141E,
        0x1802, 0x1806, 0x180A, 0x180E, 0x1812, 0x1816, 0x181A, 0x181E,
        0x1C02, 0x1C06, 0x1C0A, 0x1C0E, 0x1C12, 0x1C16, 0x1C1A, 0x1C1E,
        0x2002, 0x2006, 0x200A, 0x200E, 0x2012, 0x2016, 0x201A, 0x201E
    }, {
        0x0002, 0x0006, 0x000A, 0x000E, 0x0012, 0x0016, 0x001A, 0x001E,
        0x0402, 0x0406, 0x040A, 0x040E, 0x0412, 0x0416, 0x041A, 0x041E,
        0x0802, 0x0806, 0x080A, 0x080E, 0x0812, 0x0816, 0x081A, 0x081E,
        0x0C02, 0x0C06, 0x0C0A, 0x0C0E, 0x0C12, 0x0C16, 0x0C1A, 0x0C1E,
        0x1002, 0x1006, 0x100A, 0x100E, 0x1012, 0x1016, 0x101A, 0x101E,
        0x1402, 0x1406, 0x140A, 0x140E, 0x1412, 0x1416, 0x141A, 0x141E,
        0x1802, 0x1806, 0x180A, 0x180E, 0x1812, 0x1816, 0x181A, 0x181E,
        0x1C02, 0x1C06, 0x1C0A, 0x1C0E, 0x1C12, 0x1C16, 0x1C1A, 0x1C1E,
        0x2002, 0x2006, 0x200A, 0x200E, 0x2012, 0x2016, 0x201A, 0x201E
    }, {
        0x0003, 0x0007, 0x000B, 0x000F, 0x0013, 0x0017, 0x001B, 0x001F,
        0x0403, 0x0407, 0x040B, 0x040F, 0x0413, 0x0417, 0x041B, 0x041F,
        0x0803, 0x0807, 0x080B, 0x080F, 0x0813, 0x0817, 0x081B, 0x081F,
        0x0C03, 0x0C07, 0x0C0B, 0x0C0F, 0x0C13, 0x0C17, 0x0C1B, 0x0C1F,
        0x1003, 0x1007, 0x100B, 0x100F, 0x1013, 0x1017, 0x101B, 0x101F,
        0x1403, 0x1407, 0x140B, 0x140F, 0x1413, 0x1417, 0x141B, 0x141F,
        0x1803, 0x1807, 0x180B, 0x180F, 0x1813, 0x1817, 0x181B, 0x181F,
        0x1C03, 0x1C07, 0x1C0B, 0x1C0F, 0x1C13, 0x1C17, 0x1C1B, 0x1C1F,
        0x2003, 0x2007, 0x200B, 0x200F, 0x2013, 0x2017, 0x201B, 0x201F
    }, {
        0x0003, 0x0007, 0x000B, 0x000F, 0x0013, 0x0017, 0x001B, 0x001F,
        0x0403, 0x0407, 0x040B, 0x040F, 0x0413, 0x0417, 0x041B, 0x041F,
        0x0803, 0x0807, 0x080B, 0x080F, 0x0813, 0x0817, 0x081B, 0x081F,
        0x0C03, 0x0C07, 0x0C0B, 0x0C0F, 0x0C13, 0x0C17, 0x0C1B, 0x0C1F,
        0x1003, 0x1007, 0x100B, 0x100F, 0x1013, 0x1017, 0x101B, 0x101F,
        0x1403, 0x1407, 0x140B, 0x140F, 0x1413, 0x1417, 0x141B, 0x141F,
        0x1803, 0x1807, 0x180B, 0x180F, 0x1813, 0x1817, 0x181B, 0x181F,
        0x1C03, 0x1C07, 0x1C0B, 0x1C0F, 0x1C13, 0x1C17, 0x1C1B, 0x1C1F,
        0x2003, 0x2007, 0x200B, 0x200F, 0x2013, 0x2017, 0x201B, 0x201F
    }
};

struct PokedexCryScreen * const gPokedexCryScreenPtr = &ePokedexCryScreen;

const u16 sCryScreenBg_Pal[] = INCBIN_U16("graphics/pokedex/83FB6F8.gbapal");
const u16 sCryScreenBg_Gfx[] = INCBIN_U16("graphics/pokedex/83FB718.4bpp");

const u8 sWaveformTileDataNybbleMasks[] = {0xF0, 0x0F};

// Waveform is blue in the middle (8) grading to white at peaks (15)
// Split into two arrays for the two vertical slice halves
const u8 sWaveformColor[][16] = {
    {
        0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
    }, {
        0xF0, 0xE0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80,
        0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0
    }
};

struct PokedexCryMeterNeedle * const gPokedexCryScreenPtr2 = &ePokedexCryScreen2;

union AnimCmd sAnim_CryMeterNeedle_0[] = {
    ANIMCMD_FRAME(0, 30),
    ANIMCMD_END
};

const union AnimCmd *sAnimTable_CryMeterNeedle[] = { sAnim_CryMeterNeedle_0 };

struct OamData sOamData_CryMeterNeedle = {
    .y = 160,
    .affineMode = ST_OAM_AFFINE_NORMAL,
    .size = 3,
    .priority = 1
};

struct SpriteTemplate sSpriteTemplate_CryMeterNeedle = {
    0x2000,
    0x2000,
    &sOamData_CryMeterNeedle,
    sAnimTable_CryMeterNeedle,
    NULL,
    gDummySpriteAffineAnimTable,
    SpriteCB_CryMeterNeedle
};

struct SpriteSheet gCryMeterNeedleSpriteSheets[] = {
    {CryMeterNeedleTiles, 0x800, 0x2000},
    {}
};

struct SpritePalette gCryMeterNeedleSpritePalettes[] = {
    {CryMeterNeedlePalette, 0x2000},
    {}
};

#if ENGLISH
#define CRY_METER_MAP_WIDTH 10
#elif GERMAN
#define CRY_METER_MAP_WIDTH 32
#endif

bool8 LoadCryWaveformWindow(struct CryScreenWindow *cry, u8 bgId)
{
    u8 i;
    u8 j;
    u8 position;
    u16 tileNum;
    u16 screenBase;
    bool8 finished = FALSE;

    switch (gDexCryScreenState)
    {
    case 0:
        gPokedexCryScreenPtr->charBase = cry->charBase;
        gPokedexCryScreenPtr->playStartPos = cry->yPos;
        gPokedexCryScreenPtr->cryOverrideCountdown = 0;
        gPokedexCryScreenPtr->cryRepeatDelay = 0;
        gPokedexCryScreenPtr->cryState = 0;
        gPokedexCryScreenPtr->waveformPreviousY = 28;
        gPokedexCryScreenPtr->playhead = 0;
        ShiftWaveformOver(bgId, -8 * cry->xPos, 1);
        for (i = 0; i < 7; i++)
        {
            for (j = 0; j < 32; j++)
            {
                DmaCopy16(3, sCryScreenBg_Gfx, &sCryWaveformWindowTiledata[((32 * i) + j) * 32], 32);
            }
        }
        gDexCryScreenState++;
        break;
    case 1:
        screenBase = cry->screenBase * 0x800;
        tileNum = (cry->paletteNo << 12) + ((cry->charBase >> 5) & 0x1FF);
        for (i = 0; i < 7; i++)
        {
            for (j = 0; j < 32; j++)
            {
                *((u16 *)(VRAM + (2 * j) + (64 * i) + screenBase)) = tileNum++;
            }
        }
        for (position = 0; position < gPokedexCryScreenPtr->playStartPos * 8; position++)
        {
            DrawWaveformSegment(position, 0);
        }
        gDexCryScreenState++;
        break;
    case 2:
        DrawWaveformWindow();
        LoadPalette(sCryScreenBg_Pal, cry->paletteNo * 16, 32);
        finished = TRUE;
        break;
    }
    return finished;
}

void UpdateCryWaveformWindow(u8 bgId)
{
    u8 r4;
    DrawWaveformWindow();
    AdvancePlayhead(bgId);
    if (gPokedexCryScreenPtr->cryRepeatDelay)
        gPokedexCryScreenPtr->cryRepeatDelay--;
    if (gPokedexCryScreenPtr->cryOverrideCountdown && !--gPokedexCryScreenPtr->cryOverrideCountdown)
    {
        PlayCryScreenCry(gPokedexCryScreenPtr->species);
        DrawWaveformFlatline();
        return;
    }
    if (gPokedexCryScreenPtr->cryState == 0)
    {
        DrawWaveformFlatline();
        return;
    }
    if (gPokedexCryScreenPtr->cryState == 1)
    {
        BufferCryWaveformSegment();
    }
    else if (gPokedexCryScreenPtr->cryState > 8)
    {
        if (!IsCryPlaying())
        {
            DrawWaveformFlatline();
            gPokedexCryScreenPtr->cryState = 0;
            return;
        }
        BufferCryWaveformSegment();
        gPokedexCryScreenPtr->cryState = 1;
    }
    r4 = 2 * (gPokedexCryScreenPtr->cryState - 1);
    DrawWaveformSegment(gPokedexCryScreenPtr->playStartPos * 8 + gPokedexCryScreenPtr->playhead - 2,
        gPokedexCryScreenPtr->cryWaveformBuffer[r4]);
    DrawWaveformSegment(gPokedexCryScreenPtr->playStartPos * 8 + gPokedexCryScreenPtr->playhead - 1,
        gPokedexCryScreenPtr->cryWaveformBuffer[r4 + 1]);
    gPokedexCryScreenPtr->cryState++;
}

void CryScreenPlayButton(u16 species)
{
    if (gMPlayInfo_BGM.status & MUSICPLAYER_STATUS_PAUSE && !gPokedexCryScreenPtr->cryOverrideCountdown)
    {
        if (!gPokedexCryScreenPtr->cryRepeatDelay)
        {
            gPokedexCryScreenPtr->cryRepeatDelay = 4;
            if (IsCryPlaying() == TRUE)
            {
                StopCry();
                gPokedexCryScreenPtr->species = species;
                gPokedexCryScreenPtr->cryOverrideCountdown = 2;
            }
            else
                PlayCryScreenCry(species);
        }
    }
}

void PlayCryScreenCry(u16 species)
{
    PlayCry2(species, 0, 125, 10);
    gPokedexCryScreenPtr->cryState = 1;
}

#define GetSrcBufferForCryWaveformSegment(counter) ({ \
    const s8 * p;                     \
    if ((counter) < 2)                \
        p = gSoundInfo.pcmBuffer;     \
    else                              \
        p = gSoundInfo.pcmBuffer + (gSoundInfo.pcmDmaPeriod + 1 - gPcmDmaCounter) * gSoundInfo.pcmSamplesPerVBlank; \
    p + PCM_DMA_BUF_SIZE;             \
})

void BufferCryWaveformSegment(void)
{
    const s8 * src;
    u8 i;

    src = GetSrcBufferForCryWaveformSegment(gPcmDmaCounter);
    for (i = 0; i < 16; i++)
        gPokedexCryScreenPtr->cryWaveformBuffer[i] = src[i * 2] * 2;

}

void DrawWaveformFlatline(void)
{
    DrawWaveformSegment(gPokedexCryScreenPtr->playStartPos * 8 + gPokedexCryScreenPtr->playhead - 2, 0);
    DrawWaveformSegment(gPokedexCryScreenPtr->playStartPos * 8 + gPokedexCryScreenPtr->playhead - 1, 0);
}

void AdvancePlayhead(u8 bgId)
{
    u8 i;
    u16 offset;

    ShiftWaveformOver(bgId, gPokedexCryScreenPtr->playhead, 0);
    gPokedexCryScreenPtr->playhead += 2;
    offset = (gPokedexCryScreenPtr->playhead / 8 + gPokedexCryScreenPtr->playStartPos + 1) % 32;
    for (i = 0; i < 7; i++)
    {
        DmaCopy16(3, sCryScreenBg_Gfx, &sCryWaveformWindowTiledata[32 * (32 * i + offset)], 32);
    }
}

void DrawWaveformSegment(u8 position, u8 amplitude)
{
#define PLAY_START_POS            (position >> 3)
#define PLAYHEAD_POS              (position & ((1 << 3) - 1))
#define VERT_SLICE                (position & 1)
    u8 currentPointY;
    u8 nybble;
    u16 offset;
    u16 temp;
    u8 y;

    temp = (amplitude + 127) * 256;
    y = temp / 1152.0;
    if (y > 71 - 16)
        y = 71 - 16;
    currentPointY = y;
    nybble = VERT_SLICE;
    if (y > gPokedexCryScreenPtr->waveformPreviousY)
    {
        do
        {
            offset = sWaveformOffsets[PLAYHEAD_POS][y] + PLAY_START_POS * TILE_SIZE_4BPP;
            sCryWaveformWindowTiledata[offset] &= sWaveformTileDataNybbleMasks[nybble];
            sCryWaveformWindowTiledata[offset] |= sWaveformColor[nybble][((y / 3) - 1) & 0x0F];
            y--;
        } while (y > gPokedexCryScreenPtr->waveformPreviousY);
    }
    else
    {
        do
        {
            offset = sWaveformOffsets[PLAYHEAD_POS][y] + PLAY_START_POS * TILE_SIZE_4BPP;
            sCryWaveformWindowTiledata[offset] &= sWaveformTileDataNybbleMasks[nybble];
            sCryWaveformWindowTiledata[offset] |= sWaveformColor[nybble][((y / 3) - 1) & 0x0F];
            y++;
        } while (y < gPokedexCryScreenPtr->waveformPreviousY);
    }
    gPokedexCryScreenPtr->waveformPreviousY = currentPointY;
    #undef PLAYHEAD_POS
    #undef PLAY_START_POS
    #undef VERT_SLICE
}

void DrawWaveformWindow(void)
{
    DmaCopy16(3, sCryWaveformWindowTiledata, VRAM + gPokedexCryScreenPtr->charBase, 0x1c00);
}

void ShiftWaveformOver(u8 bgId, s16 pos, u8 axis)
{
    switch (bgId)
    {
        case 0:
            if (axis)
                REG_BG0VOFS = pos;
            else
                REG_BG0HOFS = pos;
            break;
        case 1:
            if (axis)
                REG_BG1VOFS = pos;
            else
                REG_BG1HOFS = pos;
            break;
        case 2:
            if (axis)
                REG_BG2VOFS = pos;
            else
                REG_BG2HOFS = pos;
            break;
        case 3:
            if (axis)
                REG_BG3VOFS = pos;
            else
                REG_BG3HOFS = pos;
            break;
    }
}

bool8 ShowPokedexCryScreen(struct CryScreenWindow *cry, UNUSED u8 arg1) {
    int returnVal = FALSE;

    switch (gDexCryScreenState)
    {
    case 0:
        LZ77UnCompVram(gCryMeter_Gfx, (void *) (VRAM + cry->charBase));
        LoadPalette(gCryMeter_Pal, cry->paletteNo * 16, 0x20);
        gDexCryScreenState++;
        break;

    case 1:
    {
        void *vram;
        u8 row, col;
        u32 r12;
        int x, y;

        vram = BG_SCREEN_ADDR(cry->screenBase);

        r12 = (cry->charBase >> 5) & 0x1FF;

        for (row = 0; row < 8; row++)
        {
            for (col = 0; col < 10; col++)
            {
                y = row + cry->yPos;
                x = col + cry->xPos;
                *(u16 *) (vram + (y * 64 + x * 2)) = (gCryMeter_Tilemap[row * CRY_METER_MAP_WIDTH + col] | (cry->paletteNo << 12)) + r12;
            }
        }

        gDexCryScreenState += 1;
        break;
    }

    case 2:
    {
        LoadSpriteSheets(gCryMeterNeedleSpriteSheets);
        LoadSpritePalettes(gCryMeterNeedleSpritePalettes);
        gPokedexCryScreenPtr2->spriteId = CreateSprite(&sSpriteTemplate_CryMeterNeedle, 40 + cry->xPos * 8, 56 + cry->yPos * 8, 1);
        gPokedexCryScreenPtr2->rotation = 0x20;
        gPokedexCryScreenPtr2->targetRotation = 0x20;
        gPokedexCryScreenPtr2->moveIncrement = 0;

        returnVal = TRUE;
        break;
    }
    }

    return returnVal;
}

void DestroyCryMeterNeedleSprite(void)
{
    FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(gSprites[gPokedexCryScreenPtr2->spriteId].oam.paletteNum));
    DestroySprite(gSprites + gPokedexCryScreenPtr2->spriteId);
}

void SpriteCB_CryMeterNeedle(struct Sprite * sprite)
{
    u16 i;
    s8 peakAmplitude;
    s16 x;
    s16 y;
    struct ObjAffineSrcData affine;
    struct OamMatrix matrix;
    u8 * r0;

    gSprites[gPokedexCryScreenPtr2->spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
    gSprites[gPokedexCryScreenPtr2->spriteId].oam.affineParam = 0;
    switch (gPokedexCryScreenPtr->cryState)
    {
        case 0:
            gPokedexCryScreenPtr2->targetRotation = 0x20;
            if (gPokedexCryScreenPtr2->rotation > 0)
            {
                if (gPokedexCryScreenPtr2->moveIncrement != 1)
                    gPokedexCryScreenPtr2->moveIncrement--;
            }
            else
                gPokedexCryScreenPtr2->moveIncrement = 5;
            break;
        case 2:
            peakAmplitude = 0;
            for (i = 0; i < 16; i++)
            {
                if (peakAmplitude < gPokedexCryScreenPtr->cryWaveformBuffer[i])
                    peakAmplitude = gPokedexCryScreenPtr->cryWaveformBuffer[i];
            }
            SetCryMeterNeedleTarget(peakAmplitude * 0xd0 / 0x100);
            break;
        case 6:
            r0 = &gPokedexCryScreenPtr->cryWaveformBuffer[10];
            SetCryMeterNeedleTarget(*r0 * 0xd0 / 0x100);
            break;
    }
    if (gPokedexCryScreenPtr2->rotation == gPokedexCryScreenPtr2->targetRotation)
        ; // Empty, needle has reached target
    else if (gPokedexCryScreenPtr2->rotation < gPokedexCryScreenPtr2->targetRotation)
    {
        // Rotate needle left
        if ((gPokedexCryScreenPtr2->rotation += gPokedexCryScreenPtr2->moveIncrement) > gPokedexCryScreenPtr2->targetRotation)
        {
            gPokedexCryScreenPtr2->rotation = gPokedexCryScreenPtr2->targetRotation;
            gPokedexCryScreenPtr2->targetRotation = 0;
        }
    }
    else
    {
        // Rotate needle right
        if ((gPokedexCryScreenPtr2->rotation -= gPokedexCryScreenPtr2->moveIncrement) < gPokedexCryScreenPtr2->targetRotation)
        {
            gPokedexCryScreenPtr2->rotation = gPokedexCryScreenPtr2->targetRotation;
            gPokedexCryScreenPtr2->targetRotation = 0;
        }
    }
    affine.xScale = 0x100;
    affine.yScale = 0x100;
    affine.rotation = gPokedexCryScreenPtr2->rotation * 256;
    ObjAffineSet(&affine, &matrix, 1, 2);
    SetOamMatrix(0, matrix.a, matrix.b, matrix.c, matrix.d);
    x = gSineTable[((gPokedexCryScreenPtr2->rotation + 0x7F) & 0xFF)];
    y = gSineTable[((gPokedexCryScreenPtr2->rotation + 0x7F) & 0xFF) + 0x40];
    sprite->x2 = x * 24 / 256;
    sprite->y2 = y * 24 / 256;
}

void SetCryMeterNeedleTarget(s8 offset)
{
    u16 rotation = (0x20 - offset) & 0xff;
    if (rotation > 0x20 && rotation < 0xe0)
        rotation = 0xe0;
    gPokedexCryScreenPtr2->targetRotation = rotation;
    gPokedexCryScreenPtr2->moveIncrement = 5;
}
