/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * u6m.h - Ultima 6 Music Player by Marc Winterrowd.
 * This code extends the Adlib Winamp plug-in by Simon Peter <dn.tlp@gmx.net>
 */

#include <stack>

#include "player.h"

#define default_dict_size 4096     // because maximum codeword size == 12 bits
#define max_codeword_length 12     // maximum codeword length in bits

class Cu6mPlayer: public CPlayer
{
 public:
  static CPlayer *factory(Copl *newopl);

  Cu6mPlayer(Copl *newopl) : CPlayer(newopl), song_data(0), song_size(0)
    {
    };


  ~Cu6mPlayer()
    {
      if(song_data) delete[] song_data;
    };

  bool load(const std::string &filename, const CFileProvider &fp) override;
  bool update() override;
  void rewind(int subsong) override;
  float getrefresh() override;

  std::string gettype() override
    {
      return std::string("Ultima 6 Music");
    };

 protected:

  struct byte_pair
  {
    unsigned char lo;
    unsigned char hi;
  };

  struct subsong_info   // information about a subsong
  {
    size_t continue_pos;
    size_t subsong_start;
    int subsong_repetitions;
  };

  struct dict_entry   // dictionary entry
  {
    unsigned char root;
    short int codeword;
  };

  struct data_block   // 
  {
    size_t size;
    unsigned char *data;
  };

  class MyDict
    {
    private:
      // The actual number of dictionary entries allocated
      // is (dictionary_size-256), because there are 256 roots
      // that do not need to be stored.
      int contains; // number of entries currently in the dictionary
      int dict_size; // max number of entries that will fit into the dictionary
      dict_entry* dictionary;

    public:
      MyDict(); // use dictionary size of 4096
      MyDict(int); // let the caller specify a dictionary size
      ~MyDict();
      void reset(); // re-initializes the dictionary
      void add(unsigned char, int);
      unsigned char get_root(int);
      int get_codeword(int);
    };


  // class variables
  unsigned char* song_data;   // the uncompressed .m file (the "song")
  size_t song_size;           // allocated size of song_data
  bool driver_active;         // flag to prevent reentrancy
  bool songend;	              // indicates song end
  size_t song_pos;            // current offset within the song
  size_t loop_position;       // position of the loop point
  int read_delay;             // delay (in timer ticks) before further song data is read
  std::stack<subsong_info> subsong_stack;

  size_t instrument_offsets[9]; // offsets of the adlib instrument data
  // vibrato ("vb")
  unsigned char vb_current_value[9];
  unsigned char vb_double_amplitude[9];
  unsigned char vb_multiplier[9];
  unsigned char vb_direction_flag[9];
  // mute factor ("mf") = not(volume)
  unsigned char carrier_mf[9];
  signed char carrier_mf_signed_delta[9];
  unsigned char carrier_mf_mod_delay_backup[9];
  unsigned char carrier_mf_mod_delay[9];
  // frequency
  byte_pair channel_freq[9];  // adlib freq settings for each channel
  signed char channel_freq_signed_delta[9];

  // protected functions used by update()
  void command_loop();
  int read_song_byte();
  signed char read_signed_song_byte();
  void dec_clip(int&);
  byte_pair expand_freq_byte(unsigned char);
  void set_adlib_freq(int channel,byte_pair freq_word);
  void set_adlib_freq_no_update(int channel,byte_pair freq_word);
  void set_carrier_mf(int channel,unsigned char mute_factor);
  void set_modulator_mf(int channel,unsigned char mute_factor);
  void freq_slide(int channel);
  void vibrato(int channel);
  void mf_slide(int channel);

  void command_0(int channel);
  void command_1(int channel);
  void command_2(int channel);
  void command_3(int channel);
  void command_4(int channel);
  void command_5(int channel);
  void command_6(int channel);
  void command_7(int channel);
  void command_81();
  void command_82();
  void command_83();
  void command_85();
  void command_86();
  void command_E();
  void command_F();

  void out_adlib(unsigned char adlib_register, unsigned char adlib_data);
  void out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte);

  // protected functions used by load()
  bool lzw_decompress(data_block source, data_block dest);
  int get_next_codeword(unsigned long& bits_read, data_block& source, int codeword_size);
  bool output_root(unsigned char root, data_block& destination, size_t& position);
  void get_string(int codeword, MyDict& dictionary, std::stack<unsigned char>& root_stack);
};

