/****************************************************************/
/*      Novation Launchpad Mini Mapping                         */
/*      For Mixxx version 1.12                                  */
/*      Author: marczis based on zestoi's work                  */
/****************************************************************/

//Common helpers
colorCode = function()
{
    return {
        black: 4,

        lo_red: 1 + 4,
        mi_red: 2 + 4,
        hi_red: 3 + 4,
        lo_green: 16 + 4,
        mi_green: 32 + 4,
        hi_green: 48 + 4,
        lo_amber: 17 + 4,
        mi_amber: 34 + 4,
        hi_amber: 51 + 4,
        hi_orange: 35 + 4,
        lo_orange: 18 + 4,
        hi_yellow: 50 + 4,
        lo_yellow: 33 + 4,

    }
};
//Define one Key
Key = Object;
Key.prototype.color = colorCode("black");
Key.prototype.x = -1;
Key.prototype.y = -1;
Key.prototype.page = -1;
Key.prototype.pressed = false;

Key.prototype.init = function(page, x, y)
{
    this.x = x;
    this.y = y;
    this.page = page;
    //print("Key created");
}

Key.prototype.setColor = function(color)
{
    //First line is special
    this.color = colorCode()[color];
    this.draw();
};

Key.prototype.draw = function()
{
    if ( this.page != NLM.page ) return;
    if ( this.y == 8 ) {
        midi.sendShortMsg(0xb0, this.x + 0x68, this.color);
        return;
    }
    midi.sendShortMsg(0x90, this.x+this.y*16, this.color);
    //midi.sendShortMsg(0xb0, 0x0, 0x28); //Enable buffer cycling
}

Key.prototype.onPush = function()
{
}

Key.prototype.onRelease = function()
{
}

Key.prototype.callback = function()
{
    if (this.pressed) {
        this.onPush();
    } else {
        this.onRelease();
    }
}

function PushKey(colordef, colorpush) {
    var that = new Key;

    that.setColor(colordef);

    that.colordef = colordef;
    that.colorpush = colorpush;

    that.onPush = function()
    {
        this.setColor(this.colorpush);
    }

    that.onRelease = function()
    {
        this.setColor(this.colordef);
    }

    return that;
}

function PushKeyBin(colordef, colorpush, group, control, pushval) {
    var that = PushKey(colordef, colorpush);

    that.onPushOrig = that.onPush;
    that.onPush = function()
    {
        engine.setValue(group, control, pushval);
        this.onPushOrig();
    }
    return that;
}

function PageSelectKey() {
    var that = new Key;

    that.onPush = function()
    {
        NLM.btns[NLM.page][8][NLM.page].setColor("black");
        NLM.page = this.y;
        NLM.btns[NLM.page][8][NLM.page].setColor("hi_amber");
        NLM.drawPage();
    }
    return that;
}

function ShiftKey() {
    var that = PushKey("lo_green", "hi_yellow");

    that.onPushOrig = that.onPush;
    that.onPush = function()
    {
        NLM.shiftstate = this.pressed;
        this.onPushOrig();
    }

    that.onReleaseOrig = that.onRelease;
    that.onRelease = function()
    {
        NLM.shiftstate = this.pressed;
        this.onReleaseOrig();
    }

    return that;
}

function HotCueKey(ctrl, deck, hotcue) {
    var that = new Key();
    that.deck = deck;
    that.hotcue = hotcue;

    that.group = "[" + ctrl + deck + "]";
    that.ctrl_act = "hotcue_" + hotcue + "_activate";
    that.ctrl_del = "hotcue_" + hotcue + "_clear";
    that.state   = "hotcue_" + hotcue + "_enabled";

    that.setled = function() {
        if (this.pressed) {
            this.setColor("hi_amber");
        } else if (engine.getValue(this.group, this.state) == 1) {
            this.setColor("lo_green");
        } else {
            this.setColor("lo_red");
        }
    }

    that.conEvent = function() {
        engine.connectControl(this.group, this.state, this.setled);
    }

    that.setled();
    that.conEvent();

    that.callback = function() {
        if (NLM.shiftstate) {
            ctrl = this.ctrl_del;
        } else {
            ctrl = this.ctrl_act;
        }

        if (this.pressed) {
            engine.setValue(this.group, ctrl, 1);
        } else {
            engine.setValue(this.group, ctrl, 0);
        }

        this.setled();
    }

    return that;
}

function PlayKey(ctrl, deck) {
    var that = new Key();
    that.group = "[" + ctrl + deck + "]";
    that.ctrl  = "play";
    that.state = "play_indicator";

    that.setled = function() {
        if (this.pressed) {
            this.setColor("hi_amber");
        } else if (engine.getValue(this.group, this.state) == 1) {
            this.setColor("hi_green");
        } else {
            this.setColor("hi_yellow");
        }
    }

    that.conEvent = function() {
        engine.connectControl(this.group, this.state, this.setled);
    }

    that.setled();
    that.conEvent();

    that.onPush = function()
    {
        engine.setValue(this.group, this.ctrl, engine.getValue(this.group, this.ctrl) == 1 ? 0 : 1);
        this.setled();
    }

    that.onRelease = function()
    {
        this.setled();
    }

    return that;
}

function LoopKey(deck, loop) {
    var that = new Key();

    that.group = "[Channel" + deck + "]";
    that.ctrl0 = "beatloop_" + loop + "_toggle";
    that.ctrl1 = "beatlooproll_" + loop + "_activate";
    that.state = "beatloop_" + loop + "_enabled";
    that.setColor("hi_yellow");

    if (LoopKey.keys == undefined) {
        LoopKey.keys = new Array;
        LoopKey.mode = 0;
    }

    LoopKey.setMode = function(mode)
    {
        LoopKey.mode = mode;
        if (mode == 1) {
            LoopKey.keys.forEach(function(e) { e.setColor("hi_orange");} );
        }
        if (mode == 0) {
            LoopKey.keys.forEach(function(e) { e.setColor("hi_yellow");} );
        }
    }

    that.callback = function()
    {
        if (LoopKey.mode == 0) {
             if (this.pressed) {
                engine.setValue(this.group, this.ctrl0, 1);
                this.setColor("hi_green");
            } else {
                if ( engine.getValue(this.group, this.state) == 1) {
                    engine.setValue(this.group, this.ctrl0, 1);
                }
                this.setColor("hi_yellow");
            }
        } else {
            if (this.pressed) {
                engine.setValue(this.group, this.ctrl1, 1);
                this.setColor("hi_green");
            } else {
                engine.setValue(this.group, this.ctrl1, 0);
                this.setColor("hi_orange");
            }
        }
    }

    LoopKey.keys.push(that);
    return that;
}

function LoopModeKey() {
    var that = new Key();
    that.setColor("lo_yellow");

    that.callback = function()
    {
        if (this.pressed) {
            if (LoopKey.mode == 0) {
                LoopKey.setMode(1);
                this.setColor("lo_orange");
            } else {
                LoopKey.setMode(0);
                this.setColor("lo_yellow");
            }
        }
    }

    return that;
}

function LoadKey(ctrl, channel) {
    var that = PushKey("hi_green","hi_amber");

    that.group   = "[" + ctrl + channel + "]";
    that.control = "LoadSelectedTrack";

    that.onPushOrig = that.onPush;

    that.onPush = function()
    {
        engine.setValue(this.group, this.control, 1);
        this.onPushOrig();
    }

    that.event = function() {
        if (engine.getValue(this.group, "play")) {
            this.colordef = "lo_red";
        } else {
            this.colordef = "hi_green";
        }
        this.setColor(this.colordef);
    }

    that.conEvent = function() {
        engine.connectControl(this.group, "play", this.event);
    }

    that.conEvent();
    return that;
}

function ZoomKey(dir) {
    var that = PushKey("lo_green", "hi_amber");

    that.dir  = dir;

    that.onPushOrig = that.onPush;
    that.onPush = function()
    {
        // range is 1..10
        if (ZoomKey.zoom < 10 && this.dir === "+") {
            ZoomKey.zoom++;
        }
        if (ZoomKey.zoom > 1 && this.dir === "-") {
            ZoomKey.zoom--;
        }

        for ( ch = 1 ; ch <= NLM.numofdecks ; ch++ ) {
            //print("Zoom:" + ZoomKey.zoom);
            var group = "[Channel" + ch + "]";
            engine.setValue(group, "waveform_zoom", ZoomKey.zoom);
        }

        this.onPushOrig();
    }

    return that;
}
ZoomKey.zoom = 3;

function SeekKey(ch, pos) {
    var that = new Key();

    that.pos  = 0.125 * pos;
    that.grp = "[Channel"+ ch + "]";

    that.setled = function()
    {
        if (engine.getValue(this.grp, "playposition") >= this.pos) {
            this.setColor("hi_red");
        } else {
            this.setColor("lo_green");
        }
    }

    that.conEvent = function()
    {
        engine.connectControl(this.grp, "beat_active", this.setled);
    }

    that.conEvent();

    that.onPush = function()
    {
        engine.setValue(this.grp, "playposition", this.pos);
        SeekKey.keys[ch].forEach(function(e) { e.setled(); });
    }

    that.setled();

    if ( SeekKey.keys[ch] == undefined ) SeekKey.keys[ch] = new Array();
    SeekKey.keys[ch][pos] = that;
    return that;
}
SeekKey.keys = new Array();

//Define the controller

NLM = new Controller();
NLM.init = function()
{
        NLM.page = 0;
        NLM.shiftstate = false;
        NLM.numofdecks = engine.getValue("[App]", "num_decks");
    if (engine.getValue("[App]", "num_samplers") < 16) {
        engine.setValue("[App]", "num_samplers", 16);
    }
        // For testing NLM.numofdecks = 4;

        //Init hw
        midi.sendShortMsg(0xb0, 0x0, 0x0);
        //midi.sendShortMsg(0xb0, 0x0, 0x28); //Enable buffer cycling <-- Figure out what's wrong with this

        // select buffer 0
        midi.sendShortMsg(0xb0, 0x68, 3);
        //midi.sendShortMsg(0xb0, 0x0, 0x31);
        //print("=============================");
        //Setup btnstate which is for phy. state
        NLM.btns = new Array();
        for ( page = 0; page < 8 ; page++ ) {
            NLM.btns[page] = new Array();
            for ( x = 0 ; x < 9 ; x++ ) {
                NLM.btns[page][x] = new Array();
                for ( y = 0 ; y < 9 ; y++ ) {
                    var tmp = new Key;
                    if (x == 8) {
                        tmp = PageSelectKey();
                    }

                    if (y == 8) {
                        if (x == 0) {
                            tmp = ZoomKey("-");
                        }

                        if (x == 1) {
                            tmp = ZoomKey("+");
                        }

                        if (x == 7) {
                            tmp = ShiftKey();
                        }
                    }

                    if (y == 8 && x == 1) {

                    }

                    NLM.setupBtn(page,x,y, tmp);
                }
            }
        }
        //Set default page led
        NLM.btns[NLM.page][8][0].setColor("hi_amber");

        // ============== PAGE A ===============
        //Set ChX CueButtons
        for ( deck = 1; deck <= NLM.numofdecks; deck++ ) {
            for ( hc = 1 ; hc < 9 ; hc++ ) {
                x = hc-1;
                y = (deck-1)*2+1;
                NLM.setupBtn(0,x,y, HotCueKey("Channel", deck, hc));
            }
        }

        for ( deck = 1; deck <= NLM.numofdecks; deck++ ) {
            y = (deck-1)*2;
            //Set Chx PlayButton
            NLM.setupBtn(0,0,y, PlayKey("Channel", deck));
            //Set Chx LoopButtons
            NLM.setupBtn(0,2,y, LoopKey(deck, "0.0625"));
            NLM.setupBtn(0,3,y, LoopKey(deck, "0.125"));
            NLM.setupBtn(0,4,y, LoopKey(deck, "0.25"));
            NLM.setupBtn(0,5,y, LoopKey(deck, "0.5"));
            NLM.setupBtn(0,6,y, LoopKey(deck, "1"));
            NLM.setupBtn(0,7,y, LoopKey(deck, "2"));
        }

        NLM.setupBtn(0,2,8, LoopModeKey());

        // ============== PAGE H ===============

        // Right side, playlist scroll
        NLM.setupBtn(7,6,0, PushKeyBin("lo_amber", "hi_amber", "[Playlist]", "SelectTrackKnob", -50));
        NLM.setupBtn(7,6,1, PushKeyBin("mi_amber", "hi_amber", "[Playlist]", "SelectTrackKnob", -10));
        NLM.setupBtn(7,6,2, PushKeyBin("hi_amber", "hi_amber", "[Playlist]", "SelectPrevTrack", 1));
        NLM.setupBtn(7,6,3, PushKeyBin("hi_green", "hi_amber", "[Playlist]", "LoadSelectedIntoFirstStopped", 1));
        NLM.setupBtn(7,6,4, PushKeyBin("hi_amber", "hi_amber", "[Playlist]", "SelectNextTrack", 1));
        NLM.setupBtn(7,6,5, PushKeyBin("mi_amber", "hi_amber", "[Playlist]", "SelectTrackKnob", 10));
        NLM.setupBtn(7,6,6, PushKeyBin("lo_amber", "hi_amber", "[Playlist]", "SelectTrackKnob", 50));

        NLM.setupBtn(7,5,2, LoadKey("Channel",1));
        NLM.setupBtn(7,7,2, LoadKey("Channel",2));
        NLM.setupBtn(7,5,4, LoadKey("Channel",3));
        NLM.setupBtn(7,7,4, LoadKey("Channel",4));

        NLM.setupBtn(7,0,6, LoadKey("Sampler",1));
        NLM.setupBtn(7,1,6, LoadKey("Sampler",2));
        NLM.setupBtn(7,2,6, LoadKey("Sampler",3));
        NLM.setupBtn(7,3,6, LoadKey("Sampler",4));

        NLM.setupBtn(7,0,7, LoadKey("Sampler",5));
        NLM.setupBtn(7,1,7, LoadKey("Sampler",6));
        NLM.setupBtn(7,2,7, LoadKey("Sampler",7));
        NLM.setupBtn(7,3,7, LoadKey("Sampler",8));

        // Left side, playlists

        NLM.setupBtn(7,1,2, PushKeyBin("hi_green", "hi_amber", "[Playlist]", "SelectPrevPlaylist", 1));
        NLM.setupBtn(7,1,3, PushKeyBin("hi_yellow", "hi_amber", "[Playlist]", "ToggleSelectedSidebarItem", 1));
        NLM.setupBtn(7,1,4, PushKeyBin("hi_green", "hi_amber", "[Playlist]", "SelectNextPlaylist", 1));

        // ============== PAGE B ===============

        //SeekButtons
        for(i = 0 ; i < 8 ; i++) {
            for ( ch = 1 ; ch <= NLM.numofdecks; ch++ ) {
                NLM.setupBtn(1,i,ch*2-1, SeekKey(ch, i));
            }
        }

        // ============== PAGE C ===============

        // Add Sampler playbuttons
        for(var channel = 1 ; channel < 9 ; channel++) {
            NLM.setupBtn(2, 0, channel-1, PlayKey("Sampler", channel));
            NLM.setupBtn(2, 1, channel-1, PushKeyBin("hi_orange", "hi_red", "[Sampler" + channel + "]", "beatsync", 1));
            for(i = 1 ; i < 5 ; i++) {
                NLM.setupBtn(2, 3+i, channel-1, HotCueKey("Sampler", channel, i));
            }
        }




        this.drawPage();
};

NLM.setupBtn = function(page, x, y, btn)
{
    NLM.btns[page][x][y] = btn;
    NLM.btns[page][x][y].init(page, x, y);
}

NLM.shutdown = function()
{

};

NLM.incomingData = function(channel, control, value, status, group)
{
        //print("Incoming data");
        //print("cha: " + channel);
        //print("con: " + control);
        //print("val: " + value);
        //print("sta: " + status);
        //print("grp: " + group);

        //Just to make life easier

        var pressed = (value == 127);
        //Translate midi btn into index
        var y = Math.floor(control / 16);
        var x = control - y * 16;
        if ( y == 6 && x > 8 ) {
            y = 8;
            x -= 8;
        }
        if ( y == 6 && x == 8 && status == 176 ) {
            y = 8; x = 0;
        }

        NLM.btns[NLM.page][x][y].pressed = pressed;
        NLM.btns[NLM.page][x][y].callback();
};

NLM.drawPage = function() {
    for ( x = 0 ; x < 9 ; x++ ) {
        for ( y = 0 ; y < 9 ; y++ ) {
            NLM.btns[NLM.page][x][y].draw();
        }
    }
}
