#ifndef EZNES_H
#define EZNES_H
/**
  EZnes: a set of helpers for easy NES indie development in pure C
  for CC65 (in text mode)

  Created by Luxferre in 2026, released into public domain
*/

#include <nes.h>
#include <joystick.h>
#include <conio.h>

#define u8 unsigned char
#define u16 unsigned short
#define s8 char
#define s16 short

/* Generic constants */

#define CPU 1789773 /* NTSC */

#define SCREEN_WIDTH  32
#define SCREEN_HEIGHT 28
#define SCREEN_CENTER_X (SCREEN_WIDTH >> 1)
#define SCREEN_CENTER_Y (SCREEN_HEIGHT >> 1)

#define EZJOY_UP 1
#define EZJOY_DN 2
#define EZJOY_LT 3
#define EZJOY_RT 4
#define EZJOY_A  5
#define EZJOY_B  6
#define EZJOY_SL 7
#define EZJOY_ST 8

#define NES_BLACK  0x0F
#define NES_WHITE  0x30
#define NES_RED    0x15
#define NES_GREEN  0x28
#define NES_YELLOW 0x2A

/* Graphics section */

void ppu_off() {
  PPU.mask &= 0xE7;
}

void ppu_on() {
  waitvsync();
  PPU.scroll = 0;
  PPU.mask |= 0x1A;
}

int joy_input_wait() {
  u8 last_joy = 0, joy, pressed;
  /* wait until the previous key is released */
  while(joy_read(JOY_1));
  /* wait for a new press */
  while(1) {
    joy = joy_read(JOY_1);
    pressed = joy & ~last_joy;
    if(pressed & JOY_UP_MASK) return EZJOY_UP;
    if(pressed & JOY_DOWN_MASK) return EZJOY_DN;
    if(pressed & JOY_LEFT_MASK) return EZJOY_LT;
    if(pressed & JOY_RIGHT_MASK) return EZJOY_RT;
    if(pressed & JOY_BTN_1_MASK) return EZJOY_A;
    if(pressed & JOY_BTN_2_MASK) return EZJOY_B;
    if(pressed & JOY_BTN_3_MASK) return EZJOY_SL;
    if(pressed & JOY_BTN_4_MASK) return EZJOY_ST;
    last_joy = joy;
    waitvsync();
  }
}

/* palette loader */
void init_palette(u8 bg, u8 c1, u8 c2, u8 c3, u8 c4) {
  u8 i, pal[16] = {0};
  pal[0] = pal[4] = pal[8] = pal[12] = bg;
  pal[3] = c1;
  pal[7] = c2;
  pal[11] = c3;
  pal[15] = c4;
  PPU.vram.address = 0x3F;
  PPU.vram.address = 0;
  for(i=0; i<16; i++) PPU.vram.data = pal[i];
}

/* 4x4 char block colorization method (palette is 0 to 3) */
void colorxy(u8 x, u8 y, u8 palette) {
  unsigned int addr;
  u8 attr_byte;
  addr = 0x23C0 + ((y >> 2) << 3) + (x >> 2);
  attr_byte = palette | (palette << 2) | (palette << 4) | (palette << 6);
  waitvsync();
  PPU.vram.address = (u8) (addr >> 8);
  PPU.vram.address = (u8) (addr & 255);
  PPU.vram.data = attr_byte;
}

/* Sound section */

unsigned int get_timer_by_freq(int freq) {
  return (unsigned int) ((CPU / 16 / freq) - 1);
}

void play_pulse(u8 idx, unsigned int freq, u8 len) {
  unsigned int tm = get_timer_by_freq(freq);
  if(len > 31) len = 31;
  if(idx > 1) idx = 0;
  APU.pulse[idx].period_low = tm & 255;
  APU.pulse[idx].len_period_high = (tm >> 8) | (len << 3);
}

void play_jingle(unsigned int n1f1,
  unsigned int n1f2,
  unsigned int n2f1,
  unsigned int n2f2,
  unsigned int n3f1,
  unsigned int n3f2) {
  unsigned int i, dl = 2000;
  play_pulse(0, n1f1, 2);
  play_pulse(1, n1f2, 2);
  for(i=0;i<dl;++i);
  play_pulse(0, n2f1, 2);
  play_pulse(1, n2f2, 2);
  for(i=0;i<dl;++i);
  play_pulse(0, n3f1, 2);
  play_pulse(1, n3f2, 2);
}

void init_sound() {
  APU.status = 3;
  APU.pulse[0].ramp = 0x8;
  APU.pulse[1].ramp = 0x8;
  APU.pulse[0].control = 0x9F;
  APU.pulse[1].control = 0x9F;
  waitvsync();
}

/* Game initialization section */

void init_game(u8 bg, u8 c1, u8 c2, u8 c3, u8 c4) {
#ifdef _randomize
  _randomize();
#endif
  init_sound();
  bgcolor(COLOR_BLACK);
  textcolor(0);
  init_palette(bg, c1, c2, c3, c4);
  joy_install(joy_static_stddrv);
  clrscr();
}

/* wait until Start is pressed */
void wait_for_start() {
  u8 joy;
  while(1) {
    joy = joy_read(JOY_1);
    if(JOY_START(joy)) break;
    waitvsync();
  }
}

/* randomize until Start is pressed */
void wait_for_start_rand() {
  u8 joy;
  unsigned int entropy = rand();
  while(1) {
    ++entropy;
    joy = joy_read(JOY_1);
    if(JOY_START(joy)) break;
    waitvsync();
  }
  srand(entropy);
}

#endif

