#ifndef __HQN_H__
#define __HQN_H__

#include <Nes_Emu.h>
#include <cstdint>
#include <stdio.h>

#define BLIT_SIZE 65536

namespace hqn
{

typedef const char *error_t;

class HQNState;

class HQNListener
{
public:
    HQNListener() = default;
    virtual ~HQNListener() = default;

    virtual void onLoadROM(HQNState *state, const char *filename) = 0;
    virtual void onAdvanceFrame(HQNState *state) = 0;
    virtual void onLoadState(HQNState *state) = 0;
};

/*
State which is maintained by the emulator driver.

This should normally be obtained using hqn_get_state() if you are in a lua
function.
*/
class HQNState
{
public:

    /* A reference to the emulator instance. */
    Nes_Emu *m_emu;

    static const int32_t *NES_VIDEO_PALETTE;

    /**
     * Constructor.
     */
    HQNState();
    ~HQNState();

    void setEmulatorPointer(void* const emuPtr) { m_emu = (Nes_Emu*)emuPtr; }

    /*
    The joypad data for the two joypads available to an NES.
    This is directly available because I'm lazy.
    */
    uint32_t joypad[2];

    /* Get the emulator this state uses. */
    inline Nes_Emu *emu() const
    { return m_emu; }


    /*
    Advance the emulator by one frame. If sleep is true and there is a frame
    limit set advanceFrame() will sleep in order to limit the framerate.
    Returns NULL or error string.
    */
	error_t advanceFrame(bool sleep=true);

    /*
    Save the game state. This can be restored at any time.
    */
    error_t saveState(void *dest, size_t size, size_t *size_out);

    /*
    Get the size (bytes) of a savestate.
    Use this to allocate enough space for the saveState method.
    */
    error_t saveStateSize(size_t *size) const;

    /*
    Load the emulator state from data.
    This should be data produced by saveState().
    */
    error_t loadState(const char *data, size_t size);


	error_t setSampleRate(int rate);

    /**
     * Set a limit to the framerate.
     * 0 means no limit.
     */
    void setFramerate(int fps);

    /**
     * Get the current framerate limit.
     */
    int getFramerate() const;

    /**
     * Get the calculated frames per second.
     */
    double getFPS() const;

    inline HQNListener *getListener() const
    { return m_listener; }

    inline void setListener(HQNListener *l)
    { m_listener = l; }

    /**
     * Get the state of the keyboard. Use this to update the keyboard state.
     */
    inline uint8_t *getKeyboard()
    { return m_keyboard; }

private:
    /* ROM file stored in memory because reasons */
    uint8_t *m_romData;
    size_t m_romSize;
    /* Minimum milliseconds between each frame. */
    uint32_t m_msPerFrame;
    /* time value of previous frame. */
    uint32_t m_prevFrame;
    /* The listener */
    HQNListener *m_listener;
    /* Keyboard state */
    uint8_t m_keyboard[256];
    /* Number of frames we've processed */
    uint32_t m_frames;
    /* How long it took to process the previous frame */
    uint32_t m_frameTime;
    uint32_t m_initialFrame;
};

/*
Print the usage message.
@param filename used to specify the name of the exe file, may be NULL.
*/
void printUsage(const char *filename);

} // end namespace hqn

// Copied from bizinterface.cpp in BizHawk/quicknes
inline void saveBlit(const void *ePtr, int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom)
{
    // what is the point of the 256 color bitmap and the dynamic color allocation to it?
    // why not just render directly to a 512 color bitmap with static palette positions?
//    Nes_Emu *e = m_emu; // e was a parameter but since this is now part of a class, it's just in here
//    const int srcpitch = e->frame().pitch;
//    const unsigned char *src = e->frame().pixels;
//    const unsigned char *const srcend = src + (e->image_height - cropbottom) * srcpitch;
//
//    const short *lut = e->frame().palette;
//
//    const int rowlen = 256 - cropleft - cropright;
//
//    src += cropleft;
//    src += croptop * srcpitch;
//
//    for (; src < srcend; src += srcpitch)
//    {
//        for (int i = 0; i < rowlen; i++)
//        {
//            *dest++ = colors[lut[src[i]]];
//        }
//    }

 const Nes_Emu *e = (Nes_Emu*) ePtr;
 const unsigned char *in_pixels = e->frame().pixels;
 if (in_pixels == NULL) return;
 int32_t *out_pixels = dest;

 for (unsigned h = 0; h < Nes_Emu::image_height;  h++, in_pixels += e->frame().pitch, out_pixels += Nes_Emu::image_width)
  for (unsigned w = 0; w < Nes_Emu::image_width; w++)
  {
     unsigned col = e->frame().palette[in_pixels[w]];
     const Nes_Emu::rgb_t& rgb = e->nes_colors[col];
     unsigned r = rgb.red;
     unsigned g = rgb.green;
     unsigned b = rgb.blue;
     out_pixels[w] = (r << 16) | (g << 8) | (b << 0);
  }
}

#endif /* __HQN_H__ */
