#include <cstdio>
#include <string>
#include <vector>
#include <ctime>

/* S3M handling interface
 * Copyright (C) 1992,2003 Bisqwit (http://iki.fi/bisqwit/)
 */

using std::string;
using std::vector;
using std::FILE;

class S3M
{
public:
    /* S3M instrument: */
    class instrument
    {
    public:
        unsigned oldparaptr;
        
        unsigned char type;
        string filename;
        unsigned char data[32];
        string name;
        vector<unsigned char> audio;
    public:
        instrument() { Clear(); }
        
        // Makes this sample be nothing
        void Clear();
        // Sets default values for all data for empty SMP
        void MakeSMP();
        // Sets default values for all data for an AME
        void MakeAME();
        
        // Set various properties of the sample
        void SetLoop(unsigned begin, unsigned end); //default 0..0
        void SetC4SPD(unsigned c4spd);              //default 8363
        void SetVolume(unsigned char vol);          //default 64
    };

    /* Compressed pattern: */
    class pattern : public vector<unsigned char>
    {
    public:
        pattern() : oldparaptr(0) { }
        pattern(unsigned n) : vector<unsigned char> (n), oldparaptr(0) { }
        unsigned oldparaptr;
    };
    
    /* S3M pattern event: */
    class event
    {
    public:
        // Note: 254=noteoff, 255=no note, other=oct*12+note
        unsigned char note;
        // Instrument (1-255): 0=no instruument
        unsigned char ins;
        // Volume (0-64): 255=no volume change
        unsigned char vol;
        // Command: 0=no command, 1=A, 2=B, so on
        unsigned char cmd;
        // Info: (0-255), 0=default
        unsigned char info;
    public:
        event() { Clear(); }
        void Clear();
        
        bool operator==(const event &b) const
        { return note==b.note && ins==b.ins
              && vol==b.ins && cmd==b.cmd && info==b.info; }
    };
    
    class eventtable
    {
    public:
        unsigned channelcount;
        unsigned rowcount;
        vector<event> events;
    public:
        eventtable();
        eventtable(const eventtable& src, unsigned row1, unsigned numrows);
        void setgeometry(unsigned rows, unsigned chans);
        void loadchannel(unsigned chan, const vector<event>& channel);
        
        bool operator==(const eventtable &b) const { return events==b.events; }
        
        eventtable slice(unsigned row1, unsigned numrows) const;
    };
    
private:
    string name;
    unsigned flags, version, ffi;
    unsigned char globalvolume;
    unsigned char cmdA, cmdT;
    unsigned char mastervolume;
    bool stereo;
    unsigned char ultraclick;
    unsigned char panflags;
    unsigned char chansets[32];
    unsigned char pansets[32];
    vector<unsigned char> orders;
    
    vector<pattern> patterns;
    vector<instrument> instrus;
    
    bool ok;
public:
    S3M(const string& fn) : ok(Load(fn)) { }
    S3M(FILE* fp = NULL);
    
    // Returns true if no errors have been noticed.
    const bool isok() const { return ok; }
    
    // Loads the S3M from a file.
    bool Load(const string& fn);
    
    // Loads the S3M from a file.
    bool Load(FILE* fp);

    // Saves the S3M to a file.
    void Save(const string& fn);
    
    // Saves the S3M to a file.
    void Save(FILE* fp);
    
    // Displays the contents of the S3M.
    void Dump() const;
    
    // Returns the name of the song.
    const string& getname() const { return name; }
    
    // Sets the name of the song.
    void setname(const string& newname);
    
    // Alters the global settings of the S3M
    void setsettings(unsigned char A, unsigned char T,
                     unsigned char globalvol, unsigned char mastervol,
                     bool stereoflag);
    
    // Alters channel settings.
    //  "type" is one of leftchan(n), rightchan(n) and amechan(n)
    //  "pan" is 
    void setupchannel(unsigned chan, unsigned char type, unsigned char pan=0);
    static unsigned char leftchan(unsigned num)  { return 0+num; }
    static unsigned char rightchan(unsigned num) { return 8+num; }
    static unsigned char amechan(unsigned num)   { return 16+num; }
    
    // Alters orders.
    void setorder(unsigned ordernum, unsigned char value);
    
    // Returns a reference to the modifiable instrument.
    instrument& getinstrument(unsigned instrunum);
    
    // Returns a reference to the modifiable pattern.
    pattern& getpattern(unsigned patnum);
    
    // Converts an event table to a pattern.
    // The event table should have 64 rows.
    static pattern compresspattern(const eventtable& events, unsigned padto=64);
};
