/*
 *  Copyright (C) 2002-2010  The DOSBox Team
 *
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

//#include "adlib.h"
//#include "dosbox.h"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
typedef size_t Bitu;
typedef ptrdiff_t Bits;
typedef uint8_t Bit8u;
typedef uint16_t Bit16u;
typedef uint32_t Bit32u;
typedef int8_t Bit8s;
typedef int16_t Bit16s;
typedef int32_t Bit32s;

// Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
#define WAVE_HANDLER 10
// Use a logarithmic wavetable with an exponential table for volume
#define WAVE_TABLELOG 11
// Use a linear wavetable with a multiply table for volume
#define WAVE_TABLEMUL 12

// Select the type of wave generator routine
#define DBOPL_WAVE WAVE_TABLEMUL

namespace DBOPL {

struct Chip;
struct Operator;
struct Channel;

#if (DBOPL_WAVE == WAVE_HANDLER)
typedef Bits(DB_FASTCALL* WaveHandler)(Bitu i, Bitu volume);
#endif

typedef Bits (DBOPL::Operator::*VolumeHandler)();
typedef Channel* (DBOPL::Channel::*SynthHandler)(Chip* chip, Bit32u samples, Bit32s* output);

// Different synth modes that can generate blocks of data
typedef enum {
  sm2AM,
  sm2FM,
  sm3AM,
  sm3FM,
  sm4Start,
  sm3FMFM,
  sm3AMFM,
  sm3FMAM,
  sm3AMAM,
  sm6Start,
  sm2Percussion,
  sm3Percussion,
} SynthMode;

// Shifts for the values contained in chandata variable
enum
{
  SHIFT_KSLBASE = 16,
  SHIFT_KEYCODE = 24,
};

struct Operator
{
public:
  // Masks for operator 20 values
  enum
  {
    MASK_KSR = 0x10,
    MASK_SUSTAIN = 0x20,
    MASK_VIBRATO = 0x40,
    MASK_TREMOLO = 0x80,
  };

  typedef enum {
    OFF,
    RELEASE,
    SUSTAIN,
    DECAY,
    ATTACK,
  } State;

  VolumeHandler volHandler;

#if (DBOPL_WAVE == WAVE_HANDLER)
  WaveHandler waveHandler; // Routine that generate a wave
#else
  Bit16s* waveBase;
  Bit32u waveMask;
  Bit32u waveStart;
#endif
  Bit32u waveIndex;   // WAVE_BITS shifted counter of the frequency index
  Bit32u waveAdd;     // The base frequency without vibrato
  Bit32u waveCurrent; // waveAdd + vibratao

  Bit32u chanData;     // Frequency/octave and derived data coming from whatever channel controls this
  Bit32u freqMul;      // Scale channel frequency with this, TODO maybe remove?
  Bit32u vibrato;      // Scaled up vibrato strength
  Bit32s sustainLevel; // When stopping at sustain level stop here
  Bit32s totalLevel;   // totalLevel is added to every generated volume
  Bit32u currentLevel; // totalLevel + tremolo
  Bit32s volume;       // The currently active volume

  Bit32u attackAdd; // Timers for the different states of the envelope
  Bit32u decayAdd;
  Bit32u releaseAdd;
  Bit32u rateIndex; // Current position of the evenlope

  Bit8u rateZero; // Bits for the different states of the envelope having no changes
  Bit8u keyOn;    // Bitmask of different values that can generate keyon
  // Registers, also used to check for changes
  Bit8u reg20, reg40, reg60, reg80, regE0;
  // Active part of the envelope we're in
  Bit8u state;
  // 0xff when tremolo is enabled
  Bit8u tremoloMask;
  // Strength of the vibrato
  Bit8u vibStrength;
  // Keep track of the calculated KSR so we can check for changes
  Bit8u ksr;
  void SetState(Bit8u s);

private:
  void UpdateAttack(const Chip* chip);
  void UpdateRelease(const Chip* chip);
  void UpdateDecay(const Chip* chip);

public:
  void UpdateAttenuation();
  void UpdateRates(const Chip* chip);
  void UpdateFrequency();

  void Write20(const Chip* chip, Bit8u val);
  void Write40(const Chip* chip, Bit8u val);
  void Write60(const Chip* chip, Bit8u val);
  void Write80(const Chip* chip, Bit8u val);
  void WriteE0(const Chip* chip, Bit8u val);

  bool Silent() const;
  void Prepare(const Chip* chip);

  void KeyOn(Bit8u mask);
  void KeyOff(Bit8u mask);

  template<State state>
  Bits TemplateVolume();

  Bit32s RateForward(Bit32u add);
  Bitu ForwardWave();
  Bitu ForwardVolume();

  Bits GetSample(Bits modulation);
  Bits GetWave(Bitu index, Bitu vol);

public:
  Operator();
};

struct Channel
{
  Operator op[2];
  inline Operator* Op(Bitu index) { return &((this + (index >> 1))->op[index & 1]); }
  SynthHandler synthHandler;
  Bit32u chanData; // Frequency/octave and derived values
  Bit32s old[2];   // Old data for feedback

  Bit8u feedback; // Feedback shift
  Bit8u regB0;    // Register values to check for changes
  Bit8u regC0;
  // This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
  Bit8u fourMask;
  Bit8s maskLeft; // Sign extended values for both channel's panning
  Bit8s maskRight;

  // Forward the channel data to the operators of the channel
  void SetChanData(const Chip* chip, Bit32u data);
  // Change in the chandata, check for new values and if we have to forward to operators
  void UpdateFrequency(const Chip* chip, Bit8u fourOp);
  void WriteA0(const Chip* chip, Bit8u val);
  void WriteB0(const Chip* chip, Bit8u val);
  void WriteC0(const Chip* chip, Bit8u val);
  void ResetC0(const Chip* chip);

  // call this for the first channel
  template<bool opl3Mode>
  void GeneratePercussion(Chip* chip, Bit32s* output);

  // Generate blocks of data in specific modes
  template<SynthMode mode>
  Channel* BlockTemplate(Chip* chip, Bit32u samples, Bit32s* output);
  Channel();
};

struct Chip
{
  // This is used as the base counter for vibrato and tremolo
  Bit32u lfoCounter;
  Bit32u lfoAdd;

  Bit32u noiseCounter;
  Bit32u noiseAdd;
  Bit32u noiseValue;

  // Frequency scales for the different multiplications
  Bit32u freqMul[16];
  // Rates for decay and release for rate of this chip
  Bit32u linearRates[76];
  // Best match attack rates for the rate of this chip
  Bit32u attackRates[76];

  // 18 channels with 2 operators each
  Channel chan[18];

  Bit8u reg104;
  Bit8u reg08;
  Bit8u reg04;
  Bit8u regBD;
  Bit8u vibratoIndex;
  Bit8u tremoloIndex;
  Bit8s vibratoSign;
  Bit8u vibratoShift;
  Bit8u tremoloValue;
  Bit8u vibratoStrength;
  Bit8u tremoloStrength;
  // Mask for allowed wave forms
  Bit8u waveFormMask;
  // 0 or -1 when enabled
  Bit8s opl3Active;

  // Return the maximum amount of samples before and LFO change
  Bit32u ForwardLFO(Bit32u samples);
  Bit32u ForwardNoise();

  void WriteBD(Bit8u val);
  void WriteReg(Bit32u reg, Bit8u val);

  Bit32u WriteAddr(Bit32u port, Bit8u val);

  void GenerateBlock2(Bitu samples, Bit32s* output);
  void GenerateBlock3(Bitu samples, Bit32s* output);

  void Generate(Bit32u samples);
  void Setup(Bit32u r);

  Chip();
};

// struct Handler : public Adlib::Handler {
// 	DBOPL::Chip chip;
// 	virtual Bit32u WriteAddr( Bit32u port, Bit8u val );
// 	virtual void WriteReg( Bit32u addr, Bit8u val );
// 	virtual void Generate( MixerChannel* chan, Bitu samples );
// 	virtual void Init( Bitu rate );
// };

void InitTables();

}; // namespace DBOPL
