#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#ifdef PI3B
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
extern void _enable_interrupts(void);
#include <sched.h>
#endif

#include "pitrex.h"

extern int vectrexinit (char viaconfig); // not in header file yet
extern int optimizationON; // also not exported...

//static int XL,YB, XR,YT, x1,y1, x2,y2, CX,CY, Intensity, Max=512, Scale=88, CalibX=-150, CalibY=-900;
//static int WX, HY, XL,YB, XR,YT, x1,y1, x2,y2, CX,CY, Intensity, ScaleNumer=75*512, ScaleDenom=512, CalibX=-450, CalibY=-1400, Shear=64;

const int Aspect_W = 9, Aspect_H = 11;
//                                                                WX=900 HY=1100
#define ScaleNumer (42*SC)
#define ScaleDenom (DE)
static int SC, DE,        // Scale as a fraction
           WX, HY,        // Window width and height in user coords, adjusted for aspect ratio
           XL,YB, XR,YT,  // Actual corners of user window in user coordinates
           x1,y1, x2,y2,  // ends of current line in user coordinates
           CX,CY,         // center of user window (pre adjustment) in user coordinates
           Intensity,     // Line intensity in range 0..255???
           CalibX=-450, CalibY=-1400; // offset adjustment for screen in Vectrex World coordinates

static int scalex(int p, int y)
{
  return (((((p-CX)*ScaleNumer)/ScaleDenom)+CalibX));
}

static int scaley(int p, int x)
{
  return (((((p-CY)*ScaleNumer)/ScaleDenom)+CalibY));
}

void PiTrex_StartFrame(void) {
  int off = 0;
  v_WaitRecal();
  v_setBrightness(Intensity = 64);        /* reset intensity of vector beam... */
  v_readButtons();
  v_readJoystick1Analog();
  PiTrex_MoveTo(XL+off,YB+off);
  PiTrex_LineTo(XL+off,YT-off);
  PiTrex_LineTo(XR-off,YT-off);
  PiTrex_LineTo(XR-off,YB+off);
  PiTrex_LineTo(XL+off,YB+off);
}

int PiTrex_InitHZ(int freq) {
#ifdef PI3B
  static volatile uint32_t *regs = NULL;
  int   fd;
  cpu_set_t cpus;

  CPU_ZERO(&cpus);

  CPU_SET(3, &cpus);

  if (sched_setaffinity(0, sizeof(cpus), &cpus)) {
    perror("sched_setaffinity");
    return 0;
  }

  if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) return 0;
  regs = (uint32_t *)mmap(0, 0x10000 /* BLOCK_SIZE */, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x40000000);
  if ((int32_t)regs == -1) return 0;

  regs[0x4C/sizeof(uint32_t)] = 0x00000000; // TURN OFF IRQs
#endif
  
  vectrexinit(1);
  v_init();
  optimizationON = 0; // don't optimise, ie reset to 0 for every line...
  return 1;
}

void PiTrex_MoveTo(int x, int y) {
  x1 = scalex(x,y); y1 = scaley(y,x);
}

void PiTrex_LineTo(int x, int y) {
  x2 = scalex(x,y); y2 = scaley(y,x);
  v_directDraw32(x1,y1, x2,y2, Intensity);
  x1 = x2; y1 = y2;
}

void PiTrex_SetBoundingBox(int xl, int yb, int xr, int yt) {
  XL=xl;YB=yb; XR=xr;YT=yt;
  WX=xr-xl; HY=yt-yb;
  CX = (xr+xl)/2; CY = (yt+yb)/2;

  if (WX/Aspect_W < HY/Aspect_H) {
    WX = HY * Aspect_W / Aspect_H;    // taller than expected so black bars at left and right
  } else {
    HY = WX * Aspect_H / Aspect_W;    // wider than expected so black bars at top and bottom
  }
  // Width and height are now corrected.
  SC = 900; DE = WX;
}
