#ifdef SYSTEM_CPP

Input input;

uint8 Input::port_read(bool portnumber) {
  if(cartridge.has_serial() && portnumber == 1) {
    return serial.latch();
  }

  port_t &p = port[portnumber];

  switch(p.device) {
    case Device::Joypad: {
      if(cpu.joylatch() == 0) {
        if(p.counter0 >= 16) return 1;
        return system.interface->input_poll(portnumber, p.device, 0, p.counter0++);
      } else {
        return system.interface->input_poll(portnumber, p.device, 0, 0);
      }
    } //case Device::Joypad

    case Device::Multitap: {
      if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0

      unsigned deviceidx, deviceindex0, deviceindex1;
      uint8 mask = (portnumber == 0 ? 0x40 : 0x80);

      if(cpu.pio() & mask) {
        deviceidx = p.counter0;
        if(deviceidx >= 16) return 3;
        p.counter0++;

        deviceindex0 = 0;  //controller 1
        deviceindex1 = 1;  //controller 2
      } else {
        deviceidx = p.counter1;
        if(deviceidx >= 16) return 3;
        p.counter1++;

        deviceindex0 = 2;  //controller 3
        deviceindex1 = 3;  //controller 4
      }

      return (system.interface->input_poll(portnumber, p.device, deviceindex0, deviceidx) << 0)
           | (system.interface->input_poll(portnumber, p.device, deviceindex1, deviceidx) << 1);
    } //case Device::Multitap

    case Device::Mouse: {
      if(p.counter0 >= 32) return 1;

      int position_x = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::X);  //-n = left, 0 = center, +n = right
      int position_y = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Y);  //-n = up,   0 = center, +n = right

      bool direction_x = position_x < 0;  //0 = right, 1 = left
      bool direction_y = position_y < 0;  //0 = down,  1 = up

      if(position_x < 0) position_x = -position_x;  //abs(position_x)
      if(position_y < 0) position_y = -position_y;  //abs(position_x)

      position_x = min(127, position_x);  //range = 0 - 127
      position_y = min(127, position_y);  //range = 0 - 127

      switch(p.counter0++) { default:
        case  0: return 0;
        case  1: return 0;
        case  2: return 0;
        case  3: return 0;
        case  4: return 0;
        case  5: return 0;
        case  6: return 0;
        case  7: return 0;

        case  8: return system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Right);
        case  9: return system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Left);
        case 10: return 0;  //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
        case 11: return 0;  // ||

        case 12: return 0;  //signature
        case 13: return 0;  // ||
        case 14: return 0;  // ||
        case 15: return 1;  // ||

        case 16: return (direction_y) & 1;
        case 17: return (position_y >> 6) & 1;
        case 18: return (position_y >> 5) & 1;
        case 19: return (position_y >> 4) & 1;
        case 20: return (position_y >> 3) & 1;
        case 21: return (position_y >> 2) & 1;
        case 22: return (position_y >> 1) & 1;
        case 23: return (position_y >> 0) & 1;

        case 24: return (direction_x) & 1;
        case 25: return (position_x >> 6) & 1;
        case 26: return (position_x >> 5) & 1;
        case 27: return (position_x >> 4) & 1;
        case 28: return (position_x >> 3) & 1;
        case 29: return (position_x >> 2) & 1;
        case 30: return (position_x >> 1) & 1;
        case 31: return (position_x >> 0) & 1;
      }
    } //case Device::Mouse

    case Device::SuperScope: {
      if(portnumber == 0) break;  //Super Scope in port 1 not supported ...
      if(p.counter0 >= 8) return 1;

      if(p.counter0 == 0) {
        //turbo is a switch; toggle is edge sensitive
        bool turbo = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Turbo);
        if(turbo && !p.superscope.turbolock) {
          p.superscope.turbo = !p.superscope.turbo;  //toggle state
          p.superscope.turbolock = true;
        } else if(!turbo) {
          p.superscope.turbolock = false;
        }

        //trigger is a button
        //if turbo is active, trigger is level sensitive; otherwise it is edge sensitive
        p.superscope.trigger = false;
        bool trigger = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Trigger);
        if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) {
          p.superscope.trigger = true;
          p.superscope.triggerlock = true;
        } else if(!trigger) {
          p.superscope.triggerlock = false;
        }

        //cursor is a button; it is always level sensitive
        p.superscope.cursor = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Cursor);

        //pause is a button; it is always edge sensitive
        p.superscope.pause = false;
        bool pause = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Pause);
        if(pause && !p.superscope.pauselock) {
          p.superscope.pause = true;
          p.superscope.pauselock = true;
        } else if(!pause) {
          p.superscope.pauselock = false;
        }

        p.superscope.offscreen =
           p.superscope.x < 0 || p.superscope.x >= 256
        || p.superscope.y < 0 || p.superscope.y >= (ppu.overscan() ? 240 : 225);
      }

      switch(p.counter0++) {
        case 0: return p.superscope.trigger;
        case 1: return p.superscope.cursor;
        case 2: return p.superscope.turbo;
        case 3: return p.superscope.pause;
        case 4: return 0;
        case 5: return 0;
        case 6: return p.superscope.offscreen;
        case 7: return 0;  //noise (1 = yes)
      }
    } //case Device::SuperScope

    case Device::Justifier:
    case Device::Justifiers: {
      if(portnumber == 0) break;  //Justifier in port 1 not supported ...
      if(p.counter0 >= 32) return 1;

      if(p.counter0 == 0) {
        p.justifier.trigger1 = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Trigger);
        p.justifier.start1   = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Start);

        if(p.device == Device::Justifiers) {
          p.justifier.trigger2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Trigger);
          p.justifier.start2   = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Start);
        } else {
          p.justifier.x2 = -1;
          p.justifier.y2 = -1;

          p.justifier.trigger2 = false;
          p.justifier.start2   = false;
        }
      }

      switch(p.counter0++) {
        case  0: return 0;
        case  1: return 0;
        case  2: return 0;
        case  3: return 0;
        case  4: return 0;
        case  5: return 0;
        case  6: return 0;
        case  7: return 0;
        case  8: return 0;
        case  9: return 0;
        case 10: return 0;
        case 11: return 0;

        case 12: return 1;  //signature
        case 13: return 1;  // ||
        case 14: return 1;  // ||
        case 15: return 0;  // ||

        case 16: return 0;
        case 17: return 1;
        case 18: return 0;
        case 19: return 1;
        case 20: return 0;
        case 21: return 1;
        case 22: return 0;
        case 23: return 1;

        case 24: return p.justifier.trigger1;
        case 25: return p.justifier.trigger2;
        case 26: return p.justifier.start1;
        case 27: return p.justifier.start2;
        case 28: return p.justifier.active;

        case 29: return 0;
        case 30: return 0;
        case 31: return 0;
      }
    } //case Device::Justifier(s)
  } //switch(p.device)

  //no device connected
  return 0;
}

//scan all input; update cursor positions if needed
void Input::update() {
  system.interface->input_poll();
  port_t &p = port[1];

  switch(p.device) {
    case Device::SuperScope: {
      int x = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::X);
      int y = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::Y);
      x += p.superscope.x;
      y += p.superscope.y;
      p.superscope.x = max(-16, min(256 + 16, x));
      p.superscope.y = max(-16, min(240 + 16, y));

      latchx = p.superscope.x;
      latchy = p.superscope.y;
    } break;

    case Device::Justifier:
    case Device::Justifiers: {
      int x1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::X);
      int y1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::Y);
      x1 += p.justifier.x1;
      y1 += p.justifier.y1;
      p.justifier.x1 = max(-16, min(256 + 16, x1));
      p.justifier.y1 = max(-16, min(240 + 16, y1));

      int x2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::X);
      int y2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::Y);
      x2 += p.justifier.x2;
      y2 += p.justifier.y2;
      p.justifier.x2 = max(-16, min(256 + 16, x2));
      p.justifier.y2 = max(-16, min(240 + 16, y2));

      if(p.justifier.active == 0) {
        latchx = p.justifier.x1;
        latchy = p.justifier.y1;
      } else {
        latchx = (p.device == Device::Justifiers ? p.justifier.x2 : -1);
        latchy = (p.device == Device::Justifiers ? p.justifier.y2 : -1);
      }
    } break;
  }

  if(latchy < 0 || latchy >= (ppu.overscan() ? 240 : 225) || latchx < 0 || latchx >= 256) {
    //cursor is offscreen, set to invalid position so counters are not latched
    latchx = ~0;
    latchy = ~0;
  } else {
    //cursor is onscreen
    latchx += 40;  //offset trigger position to simulate hardware latching delay
    latchx <<= 2;  //dot -> clock conversion
    latchx +=  2;  //align trigger on half-dot ala interrupts (speed optimization for sCPU::add_clocks)
  }
}

void Input::port_set_device(bool portnumber, Device device) {
  port_t &p = port[portnumber];

  p.device = device;
  p.counter0 = 0;
  p.counter1 = 0;

  //set iobit to true if device is capable of latching PPU counters
  iobit = port[1].device == Device::SuperScope
       || port[1].device == Device::Justifier
       || port[1].device == Device::Justifiers;
  latchx = -1;
  latchy = -1;

  if(device == Device::SuperScope) {
    p.superscope.x = 256 / 2;
    p.superscope.y = 240 / 2;

    p.superscope.trigger   = false;
    p.superscope.cursor    = false;
    p.superscope.turbo     = false;
    p.superscope.pause     = false;
    p.superscope.offscreen = false;

    p.superscope.turbolock   = false;
    p.superscope.triggerlock = false;
    p.superscope.pauselock   = false;
  } else if(device == Device::Justifier) {
    p.justifier.active = 0;
    p.justifier.x1 = 256 / 2;
    p.justifier.y1 = 240 / 2;
    p.justifier.x2 = -1;
    p.justifier.y2 = -1;

    p.justifier.trigger1 = false;
    p.justifier.trigger2 = false;
    p.justifier.start1 = false;
    p.justifier.start2 = false;
  } else if(device == Device::Justifiers) {
    p.justifier.active = 0;
    p.justifier.x1 = 256 / 2 - 16;
    p.justifier.y1 = 240 / 2;
    p.justifier.x2 = 256 / 2 + 16;
    p.justifier.y2 = 240 / 2;

    p.justifier.trigger1 = false;
    p.justifier.trigger2 = false;
    p.justifier.start1 = false;
    p.justifier.start2 = false;
  }
}

void Input::poll() {
  port[0].counter0 = 0;
  port[0].counter1 = 0;
  port[1].counter0 = 0;
  port[1].counter1 = 0;

  port[1].justifier.active = !port[1].justifier.active;
}

void Input::init() {
}

#endif
