// Part of readsb, a Mode-S/ADSB/TIS message decoder.
//
// mode_s.h: Mode S message decoding (header)
//
// Copyright (c) 2019 Michael Wolf <michael@mictronics.de>
//
// This code is based on a detached fork of dump1090-fa.
//
// Copyright (c) 2017 FlightAware, LLC
// Copyright (c) 2017 Oliver Jowett <oliver@mutability.co.uk>
//
// This file 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 Foundation, either version 3 of the License, or
// any later version.
//
// This file 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 this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef MODE_S_H
#define MODE_S_H

#include <assert.h>

//
// Functions exported from mode_s.c
//
int scoreModesMessage (unsigned char *msg, int validbits);
int decodeModesMessage (struct modesMessage *mm);
void displayModesMessage (struct modesMessage *mm);

// datafield extraction helpers

// The first bit (MSB of the first byte) is numbered 1, for consistency
// with how the specs number them.

// Extract one bit from a message.

static inline __attribute__ ((always_inline)) unsigned
getbit (unsigned char *data, unsigned bitnum)
{
  unsigned bi = bitnum - 1;
  unsigned by = bi >> 3;
  unsigned mask = 1 << (7 - (bi & 7));

  return (data[by] & mask) != 0;
}

// Extract some bits (firstbit .. lastbit inclusive) from a message.

static inline __attribute__ ((always_inline)) unsigned
getbits (unsigned char *data, unsigned firstbit, unsigned lastbit)
{
  unsigned fbi = firstbit - 1;
  unsigned lbi = lastbit - 1;
  unsigned nbi = (lastbit - firstbit + 1);

  unsigned fby = fbi >> 3;
  unsigned lby = lbi >> 3;
  unsigned nby = (lby - fby) + 1;

  unsigned shift = 7 - (lbi & 7);
  unsigned topmask = 0xFF >> (fbi & 7);

  assert (fbi <= lbi);
  assert (nbi <= 32);
  assert (nby <= 5);

  if (nby == 5)
    {
      return
      ((data[fby] & topmask) << (32 - shift)) |
              (data[fby + 1] << (24 - shift)) |
              (data[fby + 2] << (16 - shift)) |
              (data[fby + 3] << (8 - shift)) |
              (data[fby + 4] >> shift);
    }
  else if (nby == 4)
    {
      return
      ((data[fby] & topmask) << (24 - shift)) |
              (data[fby + 1] << (16 - shift)) |
              (data[fby + 2] << (8 - shift)) |
              (data[fby + 3] >> shift);
    }
  else if (nby == 3)
    {
      return
      ((data[fby] & topmask) << (16 - shift)) |
              (data[fby + 1] << (8 - shift)) |
              (data[fby + 2] >> shift);
    }
  else if (nby == 2)
    {
      return
      ((data[fby] & topmask) << (8 - shift)) |
              (data[fby + 1] >> shift);
    }
  else if (nby == 1)
    {
      return
      (data[fby] & topmask) >> shift;
    }
  else
    {
      return 0;
    }
}

//=========================================================================
//
// Given the Downlink Format (DF) of the message, return the message length in bits.
//
// All known DF's 16 or greater are long. All known DF's 15 or less are short.
// There are lots of unused codes in both category, so we can assume ICAO will stick to
// these rules, meaning that the most significant bit of the DF indicates the length.
//

static inline int modesMessageLenByType(int type) {
    return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS;
}

#endif
