/*  RetroArch - A frontend for libretro.
 *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
 *  Copyright (C) 2011-2017 - Daniel De Matteis
 *
 *  RetroArch 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 Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  RetroArch 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 RetroArch.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __CHEAT_MANAGER_H
#define __CHEAT_MANAGER_H

#include <boolean.h>
#include <retro_common_api.h>

#include "../setting_list.h"

RETRO_BEGIN_DECLS

enum cheat_handler_type
{
   CHEAT_HANDLER_TYPE_EMU = 0,
   CHEAT_HANDLER_TYPE_RETRO,
   CHEAT_HANDLER_TYPE_END
};

enum cheat_type
{
   CHEAT_TYPE_DISABLED = 0,
   CHEAT_TYPE_SET_TO_VALUE,
   CHEAT_TYPE_INCREASE_VALUE,
   CHEAT_TYPE_DECREASE_VALUE,
   CHEAT_TYPE_RUN_NEXT_IF_EQ,
   CHEAT_TYPE_RUN_NEXT_IF_NEQ,
   CHEAT_TYPE_RUN_NEXT_IF_LT,
   CHEAT_TYPE_RUN_NEXT_IF_GT
};

enum cheat_search_type
{
   CHEAT_SEARCH_TYPE_EXACT = 0,
   CHEAT_SEARCH_TYPE_LT,
   CHEAT_SEARCH_TYPE_LTE,
   CHEAT_SEARCH_TYPE_GT,
   CHEAT_SEARCH_TYPE_GTE,
   CHEAT_SEARCH_TYPE_EQ,
   CHEAT_SEARCH_TYPE_NEQ,
   CHEAT_SEARCH_TYPE_EQPLUS,
   CHEAT_SEARCH_TYPE_EQMINUS
};

enum cheat_match_action_type
{
   CHEAT_MATCH_ACTION_TYPE_VIEW = 0,
   CHEAT_MATCH_ACTION_TYPE_DELETE,
   CHEAT_MATCH_ACTION_TYPE_COPY,
   CHEAT_MATCH_ACTION_TYPE_BROWSE
};

enum cheat_rumble_type
{
   RUMBLE_TYPE_DISABLED = 0,
   RUMBLE_TYPE_CHANGES,
   RUMBLE_TYPE_DOES_NOT_CHANGE,
   RUMBLE_TYPE_INCREASE,
   RUMBLE_TYPE_DECREASE,
   RUMBLE_TYPE_EQ_VALUE,
   RUMBLE_TYPE_NEQ_VALUE,
   RUMBLE_TYPE_LT_VALUE,
   RUMBLE_TYPE_GT_VALUE,
   RUMBLE_TYPE_INCREASE_BY_VALUE,
   RUMBLE_TYPE_DECREASE_BY_VALUE,
   RUMBLE_TYPE_END_LIST
};

/* Some codes are ridiculously large - over 10000 bytes */
#define CHEAT_CODE_SCRATCH_SIZE 16*1024
#define CHEAT_DESC_SCRATCH_SIZE 255

struct item_cheat
{
   /* Clock value for when rumbling should stop */
   retro_time_t rumble_primary_end_time; 
   retro_time_t rumble_secondary_end_time; 

   char *desc;
   char *code;

   unsigned int idx;
   unsigned int handler;
   /* Number of bits = 2^memory_search_size
    * 0=1, 1=2, 2=4, 3=8, 4=16, 5=32
    */
   unsigned int memory_search_size;
   unsigned int cheat_type;
   unsigned int value;
   unsigned int address;
   /*
    * address_mask used when memory_search_size <8 bits
    * if memory_search_size=0, then the number of bits is 1 and this value can be one of the following:
    * 0 : 00000001
    * 1 : 00000010
    * 2 : 00000100
    * 3 : 00001000
    * 4 : 00010000
    * 5 : 00100000
    * 6 : 01000000
    * 7 : 10000000
    * if memory_search_size=1, then the number of bits is 2 and this value can be one of the following:
    * 0 : 00000011
    * 1 : 00001100
    * 2 : 00110000
    * 3 : 11000000
    * if memory_search_size=2, then the number of bits is 4 and this value can be one of the following:
    * 0 : 00001111
    * 1 : 11110000
    */
   unsigned int address_mask;
   unsigned int rumble_type;
   unsigned int rumble_value;
   unsigned int rumble_prev_value;
   unsigned int rumble_initialized;
   /* 0-15 for specific port, anything else means "all ports" */
   unsigned int rumble_port; 
   unsigned int rumble_primary_strength; /* 0-65535 */
   unsigned int rumble_primary_duration; /* in milliseconds */
   unsigned int rumble_secondary_strength; /* 0-65535 */
   unsigned int rumble_secondary_duration; /* in milliseconds */

   /*
    * The repeat_ variables allow for a single cheat code to affect multiple memory addresses.
    * repeat_count - the number of times the cheat code should be applied
    * repeat_add_to_value - every iteration of repeat_count will have this amount added to item_cheat.value
    * repeat_add_to_address - every iteration of repeat_count will have this amount added to item_cheat.address
    *
    * Note that repeat_add_to_address represents the number of "memory_search_size" blocks to add to
    * item_cheat.address.  If memory_seach_size is 16-bits and repeat_add_to_address is 2, then item_cheat.address
    * will be increased by 4 bytes 2*(16-bits) for every iteration.
    *
    * This is a cheating structure used for codes like unlocking all levels, giving yourself 1 of every item,etc.
    */
   unsigned int repeat_count;
   unsigned int repeat_add_to_value;
   unsigned int repeat_add_to_address;

   bool state;
   /* Whether to apply the cheat based on big-endian console memory or not */
   bool big_endian;
};

struct cheat_manager
{
   struct item_cheat working_cheat; /* retro_time_t alignment */
   struct item_cheat *cheats;
   uint8_t *curr_memory_buf;
   uint8_t *prev_memory_buf;
   uint8_t *matches;
   uint8_t **memory_buf_list;
   unsigned *memory_size_list;
   unsigned int delete_state;
   unsigned int loading_cheat_size;
   unsigned int loading_cheat_offset;
   unsigned ptr;
   unsigned size;
   unsigned buf_size;
   unsigned total_memory_size;
   unsigned num_memory_buffers;
   unsigned match_idx;
   unsigned match_action;
   unsigned search_bit_size;
   unsigned dummy;
   unsigned search_exact_value;
   unsigned search_eqplus_value;
   unsigned search_eqminus_value;
   unsigned num_matches;
   unsigned browse_address;
   char working_desc[CHEAT_DESC_SCRATCH_SIZE];
   char working_code[CHEAT_CODE_SCRATCH_SIZE];
   bool  big_endian;
   bool  memory_initialized;
   bool  memory_search_initialized;
};

typedef struct cheat_manager cheat_manager_t;

extern cheat_manager_t cheat_manager_state;

unsigned cheat_manager_get_size(void);

bool cheat_manager_load(const char *path, bool append);

/**
 * cheat_manager_save:
 * @path                      : Path to cheats file (absolute path).
 *
 * Saves cheats to file on disk.
 *
 * Returns: true (1) if successful, otherwise false (0).
 **/
bool cheat_manager_save(const char *path,
      const char *cheat_database, bool overwrite);

bool cheat_manager_realloc(unsigned new_size, unsigned default_handler);

void cheat_manager_set_code(unsigned index, const char *str);

void cheat_manager_index_next(void);

void cheat_manager_index_prev(void);

void cheat_manager_toggle(void);

void cheat_manager_apply_cheats(void);

void cheat_manager_update(cheat_manager_t *handle, unsigned handle_idx);

void cheat_manager_toggle_index(bool apply_cheats_after_toggle,
      unsigned i);

unsigned cheat_manager_get_buf_size(void);

const char *cheat_manager_get_desc(unsigned i);

const char *cheat_manager_get_code(unsigned i);

bool cheat_manager_get_code_state(unsigned i);

void cheat_manager_state_free(void);

bool cheat_manager_alloc_if_empty(void);

bool cheat_manager_copy_idx_to_working(unsigned idx);

bool cheat_manager_copy_working_to_idx(unsigned idx);

void cheat_manager_load_game_specific_cheats(const char *path_cheat_database);

void cheat_manager_save_game_specific_cheats(const char *path_cheat_database);

int cheat_manager_initialize_memory(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_exact(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_lt(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_gt(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_lte(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_gte(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_eq(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_neq(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_eqplus(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_search_eqminus(rarch_setting_t *setting, size_t idx, bool wraparound);

unsigned cheat_manager_get_state_search_size(unsigned search_size);

int cheat_manager_add_matches(const char *path,
      const char *label, unsigned type, size_t idx, size_t entry_idx);

void cheat_manager_apply_retro_cheats(void);

void cheat_manager_match_action(
      enum cheat_match_action_type match_action,
      unsigned int target_match_idx,
      unsigned int *address, unsigned int *address_mask,
      unsigned int *prev_value, unsigned int *curr_value);

int cheat_manager_copy_match(rarch_setting_t *setting, size_t idx, bool wraparound);

int cheat_manager_delete_match(rarch_setting_t *setting, size_t idx, bool wraparound);

RETRO_END_DECLS

#endif