
// this.position will be an array of fret numbers corresponding to the tuning

ChordPosition = function(chord,instrument,position_num,config) {
    if (typeof chord == 'undefined') {
        this.chord = new Chord(); // defaults to C major
    }
    else {
        this.chord = chord;
    }
    if (typeof instrument == 'undefined') {
        this.instrument = new Instrument(); // defaults to standard guitar
    }
    else {
        this.instrument = instrument;
    }
    if (typeof position_num == 'undefined') {
        this.position_num = 1; // default to first position
    }
    else {
        this.position_num = position_num;
    }
    this.config = { position_separation : 3,
                    show_inversions : false};
    for (var i in config) {
    	this.config[i] = config[i];
    }

    // constants
    this.INDEX_FINGER = 1;
    this.MIDDLE_FINGER = 2;
    this.RING_FINGER = 3;
    this.PINKY_FINGER = 4;
    
    this._setPositions();
};

ChordPosition.prototype = {
        
    // set all the positions
    _setPositions : function()
    {
        var root_positions = this._getRootPositions();
        this.positions = [ ];
        for (var i=0; i<root_positions.length; i++) {
            var backward_pos = this._findPosition(root_positions[i],'backward');
            var forward_pos = this._findPosition(root_positions[i],'forward');
            
            // only prefer backward pos if it looks playable
            if (i==0 && backward_pos && this._looksPlayable(backward_pos,'backward')) {                
                var preferred_pos = backward_pos;
                var non_preferred_pos = forward_pos;
            }
            else if (this._looksPlayable(forward_pos)) {
                var preferred_pos = forward_pos;
                var non_preferred_pos = backward_pos;
            }
            else {
                preferred_pos = non_preferred_pos = false;
            }
            
            if (preferred_pos && 
                this._numStringsInPos(preferred_pos) >= this._numStringsInPos(non_preferred_pos)
            ) {
                var found_pos = preferred_pos;
            }
            else {
                var found_pos = non_preferred_pos;
            }
            
            if (found_pos) {
                this.positions.push(found_pos);
            }
        }
    },
    
    _findPosition : function(pos,direction)
    {
        if (direction=='forward') {
            var fret_check_from = pos.fret;
            var fret_check_to = pos.fret + this.config.position_separation;
            if (fret_check_from > this.instrument.config.num_frets) {
                fret_check_from = this.instrument.config.num_frets;
            }
        }
        else {
            var fret_check_from = pos.fret - this.config.position_separation;
            if (fret_check_from < 0) {
                fret_check_from = 0;
            }
            var fret_check_to = pos.fret;            
        }
        // for each string
        var string_check_from = (this.config.show_inversions) ? 0
                                                              : pos.string;
        var try_pos = {}; // array of strings
        for (var j=string_check_from; j<this.instrument.config.tuning.length; j++) {
            for (var k=fret_check_from; k<=fret_check_to; k++) {
                var note = this.instrument.noteAddition(this.instrument.config.tuning[j],
                                                        k);
                if (this.instrument.array_search(note,
                                                 this.chord.chord_notes)!=-1
                ) {
                    if (typeof try_pos[j] == 'undefined') {
                        try_pos[j] = [];
                    }
                    try_pos[j].push({fret:k,note:note});
                }
            }
        }
        // set the root one
        try_pos[pos.string] = 
            [{fret:pos.fret,
              note: this.instrument.noteAddition(
                          this.instrument.config.tuning[pos.string],
                          pos.fret
                    )
            }];
        
        var valid_pos = this._findBestPos(try_pos);
        if (valid_pos) {
            return valid_pos;
        }
        else {
            return false;
        }
    },
    
    _getRootPositions : function()
    {
        var root_positions = [];
        // looking on the top 3 strings
        for (var i=0; i<=this.instrument.config.num_frets; i++) { // fret
            for (var j=0; j<3; j++) { // strings
                var note = this.instrument.noteAddition(this.instrument.config.tuning[j],
                                                        i);
                if (note==this.chord.tonic) {
                    root_positions.push({string:j,fret:i});
                }
            }
        }
        return root_positions;
    },
    
    _findBestPos : function(try_pos)
    {
        var return_pos = [];
        var pos_notes = [];
        if (this._hasAllNotes(try_pos)) {
            for (var i in try_pos) { // i = string
                // work backwards through these pos - woo!
                // for (var j=try_pos[i].length-1; j>=0; j--) {
                for (var j=0; j<try_pos[i].length; j++) {
                    var note = try_pos[i][j].note;
                    if (j==0 || this.instrument.array_search(note,pos_notes)==-1) {
                        pos_notes.push(try_pos[i][j].note);
                        return_pos[i] = try_pos[i][j].fret;
                    }
                }
            }
            if (this._hasEnoughStrings(return_pos)) {
                return return_pos;
            }
            else {
                return false;
            }
        }
        else {
            return false
        }
    },
    
    _hasAllNotes : function(try_pos)
    {
        var pos_notes = [];
        for (var i in try_pos) {
            for (var j=0; j<try_pos[i].length; j++) { 
                if (this.instrument.array_search(try_pos[i][j].note,
                                                    pos_notes)==-1) {
                    pos_notes.push(try_pos[i][j].note);
                }
            }
        }

        return pos_notes.length == this.chord.chord_notes.length;
    },
    
    _hasEnoughStrings : function(try_pos)
    {
        var s_count = this._numStringsInPos(try_pos);
        return s_count > (this.instrument.config.tuning.length/2);
    },
    
    _numStringsInPos : function(pos)
    {
    	return this._objectCount(pos);
    },
    
    _objectCount : function(obj)
    {
        var prop_count = 0;
        for (var i in obj) {
        	prop_count ++;
        }
        return prop_count;
    },
    
    _looksPlayable : function(pos,direction)
    {
        // check the number of different frets in the chord
        var different_frets  = [];
        for (var i in pos) {
            if (pos[i] > 0 && 
                this.instrument.array_search(pos[i],
                                             different_frets)==-1
            ) {
                different_frets.push(pos[i]);
            }
        }
        if (different_frets.length > 3) {
            return false;
        }

        
        // look for the WRONG minor shapes
        if (pos[5]) {
            if (pos[4]==pos[5]-2 &&
                pos[3]==pos[5]-3 &&
                pos[2]==pos[5]-2 &&
                pos[1]==pos[5]
            ) {
                return false;
            }
        }
        if (pos[5]) {
            if (pos[4]==pos[5] &&
                pos[3]==pos[5]-3 &&
                pos[2]==pos[5]-3 &&
                pos[1]==pos[5]-2 &&
                pos[0]==pos[5]
            ) {
                return false;
            }
        }
        if (pos[5]) {
            if (pos[4]==pos[5]-2 &&
                pos[3]==pos[5] &&
                pos[2]==pos[5]-2 &&
                pos[1]==pos[5]
            ) {
                return false;
            }
        }
        
        // try and deduce some fingerings...
        var fingering = this.getFingering(pos);
        if (fingering) { return true; } else { return false; }
        
        return true;
    },
    
    getFingering : function(pos) 
    {
        var fingering = {}; // array of [string][fret] = finger (0-4)(open-pinky)
        var fingers_used = [];
        // find open strings
        for (var i in pos) {
            if (pos[i]==0) {
                fingering[i] = {};
                fingering[i][pos[i]] = 0; // no finger
            }
        }
        // any more to go?
        if (fingering.length != pos.length) {
            // now find the back fret(s) - they are index finger
            back_fret = back_fret_string = false;
            for (var i in pos) {
                if (!back_fret || back_fret > pos[i] && pos[i]!=0) {
                    back_fret = pos[i];
                    back_fret_string = i;
                }
            }
            // set the index finger - barred accross back fret
            for (var i in pos) {
                if (pos[i]==back_fret) {
                    fingering[i] = {};
                    fingering[i][pos[i]] = this.INDEX_FINGER; // index finger
                }
            }
            fingers_used.push(this.INDEX_FINGER); // index finger is now used up            
            if (fingering.length != pos.length) {
                
                // find next frets across - starting with middle finger
                for (var j=1; j < this.config.position_separation; j++) {
                    for (var i in pos) {
                        if (pos[i]==back_fret+j) {
                        	// note, middle finger can only stretch 2 frets
                            if (j <= 2 && this.instrument.array_search(this.MIDDLE_FINGER,
                                                                       fingers_used) == -1
                            ) {
                                fingering[i] = {};
                                fingering[i][pos[i]] = this.MIDDLE_FINGER;
                            	fingers_used.push(this.MIDDLE_FINGER);
                            }
                            // ring can only stretch 2 frets
                            else if (j <= 2 && this.instrument.array_search(this.RING_FINGER,
                            											    fingers_used) == -1
                            ) {
                                fingering[i] = {};
                                fingering[i][pos[i]] = this.RING_FINGER;
                                fingers_used.push(this.RING_FINGER);
                            }
                            else if (this.instrument.array_search(this.PINKY_FINGER,
                                                                  fingers_used) == -1
                            ) {
                                fingering[i] = {};
                                fingering[i][pos[i]] = this.PINKY_FINGER;
                                fingers_used.push(this.PINKY_FINGER);
                            }
                        }
                    } // end, for each string
                } // end, for a couple of frets
            } // end if, any more positions to account for
        } // end if, any more positions to account for
        // let's see if we have overstretched here...   
        if (this._objectCount(fingering)==this._objectCount(pos)) {
            return fingering;
        }
        else {
            return false;
        }
    },
    
    getPosition : function(pos_num)
    {
        return this.positions[pos_num];
    }
    
};