//
//  grid.cpp
//  oscSenderExample
//
//  Created by Robert Tubb on 03/10/2012.
//
// This is the grid view, i.e. the viewable representation of hilbert surface

#include "grid.h"


#include <sstream>

extern PresetManager presetManager;
extern EventLogger eventLogger;
extern Hilbert hilbert;
//--------------------------------------------------------------
Grid::Grid(): maxValue(pow(32.0,7.0)-1), minValue(30),     paramsPerDim(5), paramBitDepth(7){

    interpLevel = 4;
}
//--------------------------------------------------------------
//--------------------------------------------------------------
Grid::~Grid(){

}
void Grid::init(){

    interpolateMode = NO_INTERPOLATION;
    
    maxZoom = false;
    minZoom = false;

    pixSize.setCoord(ofGetWidth(), ofGetHeight());
 
    //set scale and position to random
    scale = 15500.0;
    
    smallestGridSpacing = 3; //pixels
    
    snapDist = TwoVector(9,9);
    snapped = false;
    
    size.setCoord(pixSize.x*scale, pixSize.y*scale);
    centre.setCoord(maxValue/2 , maxValue/2);
    topLeft.setCoord(centre.x - size.x/2, centre.y - size.y/2);

    hilbert.init(paramBitDepth, paramsPerDim);

    // set a starting param value, find coord that does it.
    vector<int> params(paramsPerDim*2);
    for(int i = 0;i<2*paramsPerDim;i++){
        midiCC[i] = 12;
        params[i] = 12;
    }
    
    
    TwoVector coord = calculateCoordFromParams(params);
    setCoord(coord);
    
    viewWasChanged();
    calculateInterpolateLevel();
}

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}
//--------------------------------------------------------------
double Grid::calculateInterpolateLevel(){
    if(interpolateMode == INTERPOLATE_GRID){

        // log_32 of scale times the pixel size we want to be the "integral" interp
        int N = 1 << paramsPerDim;
        interpLevel = log(scale*N*smallestGridSpacing)/log(N);

        return interpLevel;
    }else{
        interpLevel = 0;
        return 0; // ???
    }
}
//--------------------------------------------------------------
void Grid::setInterpolation(interpolateModeType mode){
    interpolateMode = mode;
    viewWasChanged();
    calculateInterpolateLevel();
    eventLogger.clearTrail();
}
//--------------------------------------------------------------
void Grid::draw(){
    if(interpolateMode == NO_INTERPOLATION){
        drawGridLines();
        drawPresets();
    }else if(interpolateMode == INTERPOLATE_GRID){
        // calculate according to smallest gridlines
        drawGridLines();
        
    }else if(interpolateMode == INTERPOLATE_PRESET){
        drawPresets();
        return; // ???
    }
    
    
    
    // draw centre cross hairs
    drawCrossHairs();
    
    
    // draw the undo history trail, given viewing area
    eventLogger.drawTrail(topLeft, topLeft + size);
}
//--------------------------------------------------------------
void Grid::drawGridLines(){  // draw lines
    // TODO too friggin long
    
    pixSize.setCoord(ofGetWidth(), ofGetHeight()); // incase of rotation?

    int power = 0;
   
    // these get big!
    double  divsr;
    double yl,xl;
    double gridSize = 0;

    double firstLineXPos, lastLineXPos,firstLineYPos, lastLineYPos, remleft, xoffset, yoffset = 0.0;
    double xstart, xfinish, ystart,yfinish;
    // these don't
    int xpos, ypos = 0;
    
    float alpha;
    bool done = false;
    float markpow = 1 << paramsPerDim;  // power that denotes next grid markings e.g. 32
    int lineWidth = 0;
    int colCycle = 0;

 
    // loop thru powers of (base?) smallest to biggest
    // to determine which should be shown as grids
    while (!done){
        gridSize = pow(markpow,power);
 
        colCycle = power % paramBitDepth;

        divsr = size.x / gridSize;

        // if (divisor i.e. number of lines is less than 1000
        if( ofGetWidth()/divsr >= smallestGridSpacing){

            // calculate transparency
            float visCont = log10(divsr*2); // 0 if only 1 line, 3 if 1000 lines
            alpha = 90*(3 - visCont);
            ofSetLineWidth(lineWidth);
            
            // cycle colors for different scales
            if(colCycle == 0){
                ofSetColor(255,255,255,alpha);
            }else if(colCycle == 1){
                ofSetColor(255,0,0,alpha);
            }else if(colCycle == 2){
                ofSetColor(0,0,255,alpha);
            }else if(colCycle == 3){
                ofSetColor(0,255,0,alpha);
            }else if(colCycle == 4){
                ofSetColor(255,0,255,alpha);
            }else if(colCycle == 5){
                ofSetColor(0,255,255,alpha);
            }else if(colCycle == 6){
                ofSetColor(255,255,0,alpha);
            }else if(colCycle == 7){
                ofSetColor(255,255,255,alpha);
            }else{
                cout << "err colour messed up\n";
            }
            
            
            // draw level numbers associated with each color. This would not be true if markpow wasnt 2^d
            ////////-------/////////
            /*
            temp << "THIS LEVEL = " << (int)pow(2.0,power) << "   ";
            string s = temp.str();
            ofDrawBitmapString( s, 10, line );
            line+=30;
            temp.str("");
            *////////-------/////////
            
            // draw presets at this level
            // fill in smallest squares if they contain a preset
            // how to do this efficiently?
            
            
            if(topLeft.x < 0.0){
                firstLineXPos = 0.0;
                xoffset = firstLineXPos - topLeft.x;
                xstart = xoffset/scale;
            }else{
                firstLineXPos = topLeft.x;
                // kinda float version of % operator
                remleft = ceil(firstLineXPos/gridSize);
                xoffset = (remleft * gridSize) - topLeft.x;
                xstart = 0;
            }
            if(topLeft.x + size.x > maxValue){
                lastLineXPos = maxValue+1 - topLeft.x;
                
                
            }else{
                lastLineXPos = size.x;
                
            }
            xfinish = lastLineXPos/scale;
            
            //////////------------------------
            // same for y
            
            if(topLeft.y < 0.0){
                firstLineYPos = 0.0;
                yoffset = firstLineYPos - topLeft.y;
                ystart = yoffset/scale;
            }else{
                firstLineYPos = topLeft.y;
                // kinda float version of % operator
                remleft = ceil(firstLineYPos/gridSize);
                yoffset = (remleft * gridSize) - topLeft.y;
                ystart = 0;
            }
            if(topLeft.y + size.y > maxValue){
                lastLineYPos = maxValue+1 - topLeft.y;
               
            }else{
                lastLineYPos = size.y;
                
            }
            yfinish = lastLineYPos/scale;
            // -------------------------------------------
                // now draw
            for(xl = xoffset; xl <= (lastLineXPos); xl+= gridSize){

                xpos = xl/scale;
                ofLine(xpos, ystart, xpos, yfinish);
            
            }

            for(yl = yoffset; yl <= (lastLineYPos); yl+= gridSize){
                
                ypos = yl/scale;
                ofLine(xstart, ypos, xfinish, ypos);
            }
            
            
        }
        /*else if (divsr < 1){
            // maximum only one line. worth checking so that bigger lines still show up?
            done = true;
        }
*/
        power++;
        if(power > paramBitDepth)
            done = true;

    }
    //cout << "draw done" << "\n";
    //displayInfo();


    
    ////////-------////////
    /*
    ostringstream temp;
    temp << "Centre x = " << centre.x << "\n      y = " << centre.y << "   ";
    string s = temp.str();
    ofDrawBitmapString( s, pixSize.x/2+10, pixSize.y/2+10 );
    */
}
//-----------------------------------------------------------------------
void Grid::drawCrossHairs(){
    // snapping
    TwoVector pos;
    if(snapped){
        pos = coordToPixel(snapCentre);
    }else{
        pos = coordToPixel(centre);
        
    }
    ofSetColor(255, 0, 0);
    ofNoFill();
    ofLine(pos.x-20, pos.y, pos.x+20, pos.y);
    ofLine(pos.x, pos.y-20, pos.x, pos.y+20);
    ofEllipse(pos.x, pos.y, 20, 20);
    ofFill();
}
//-----------------------------------------------------------------------
TwoVector Grid::coordToPixel(TwoVector coord){
    TwoVector pix;
    pix.x = (coord.x - topLeft.x)/scale;
    pix.y = (coord.y - topLeft.y)/scale;
    return pix;
    
}

//-----------------------------------------------------------------------
void Grid::drawPresets(){
    presetManager.drawPresetsInRange(topLeft, topLeft + size);
    // draw snapped preset info
    if(snapped && closestPreset != NULL){
        ofDrawBitmapString( closestPreset->displayTextDescription(), pixSize.x/2+10, pixSize.y/2+10 );
    }
    
}

//-----------------------------------------------------------------------
void Grid::displayInfo(){
    
    // display some "useful information"
    
    ofSetColor(255,255,255,255);
    
    ostringstream temp;
    int line = 10; // text output pos
    
    
    ////////-------/////////
    temp << "scale = " << scale << "   ";
    string s = temp.str();
	ofDrawBitmapString( s, 10, line );
    line+=30;
    temp.str("");
    ////////-------/////////
    temp << "Top Left = " << topLeft.x << "," << topLeft.y << "   ";
    s = temp.str();
    ofDrawBitmapString( s, 10, line );
    line+=60;
    temp.str("");
    
    ////////-------/////////
    temp << "View Size = " << size.x << "," << size.y << "   ";
    s = temp.str();
    ofDrawBitmapString( s, 10, line );
    line+=60;
    temp.str("");
    
    ////////-------/////////
    
    for(int i=0;i<10;i++){ // TODO 5bit specific
        temp << midiCC[i] << " ";
        s = temp.str();
        ofDrawBitmapString( s, 10, line );
        line+=20;
        temp.str("");
    }
}
 //--------------------------------------------------------------
void Grid::update(){  // ?


}

//--------------------------------------------------------------

void Grid::move(TwoVector moveP){
    // numspacing, pixelspacing stay the same
    
    // convert pixels to surf units
    TwoVector moveS;
    moveS = moveP * scale;
    
    topLeft = topLeft - moveS;  // - because moving to the right means taking away from offset
    centre = centre - moveS;
    
    viewWasChanged();
    eventLogger.logEvent(SCROLL, centre, scale);
    
}
//--------------------------------------------------------------
void Grid::zoom(float factor){
    if(snapped)centre = (centre + snapCentre)*0.5; // clunky
    
    if(maxZoom && factor > 1.0){
        return;
        
    }
    if(factor < 1.0){
        maxZoom = false;
    }
    if(minZoom && factor < 1.0){
        return;
        
    }
    if(factor > 1.0){
        minZoom = false;
    }
    scale = scale*factor; // simple eh?

    // update view size using centre
    // and scale...
    size.x = size.x*factor; // zooming in, size gets SMALLER (view less)
    size.y = size.y*factor;

    
    
    viewWasChanged();
    calculateInterpolateLevel();
    eventLogger.logEvent(ZOOM, centre, scale);

}
//--------------------------------------------------------------
void Grid::shiftCentreToSnapped(){
    // TODO just in case we're freezing something
    // snapping actually change centre
    centre = snapCentre;
    
}
//--------------------------------------------------------------
void Grid::snapCheck(){
    
    if(interpolateMode == INTERPOLATE_GRID){
        snapped = false;
        closestPreset = NULL;
        snapCentre = centre;
        return;
    } // no presets visible
    
    // check environs for presets.
    vector<Preset *> closePresets = presetManager.getPresetsInRange(centre - snapDist*scale, centre + snapDist*scale);
    if(closePresets.size() > 0){
        snapped = true;
        // find closest
        double dist, mindist = maxValue;
        closestPreset = closePresets[0];
        
        for(vector<Preset *>::iterator piter = closePresets.begin(); piter < closePresets.end(); piter++){
            dist = (*piter)->coordinates.distanceTo(centre);
            
            if(dist < mindist){
                mindist = dist;
                closestPreset = *piter;
            }
        }
        snapCentre = closestPreset->coordinates;
        eventLogger.logEvent(SNAPPED_TO_PRESET, getCoord(),closestPreset->creationTime );
        //cout << "SNAPPED CHECK\n";
    }else{
        snapped = false;
        closestPreset = NULL;
        snapCentre = centre;
    }

    
}
//--------------------------------------------------------------
void Grid::setMaxZoom(){ // got to smallest (white)
    if(snapped)centre = snapCentre;
    size.x = maxValue*2.0;
    scale = size.x/pixSize.x;
    size.y = scale * pixSize.y;
    maxZoom = true;
    minZoom = false;
    viewWasChanged();
}
//--------------------------------------------------------------
void Grid::setMinZoom(){ // go to entire space (orange)
    if(snapped)centre = snapCentre;
    size.x = minValue*2.0;
    scale = size.x/pixSize.x;
    size.y = scale * pixSize.y;
    minZoom = true;
    maxZoom = false;
    viewWasChanged();
}
//--------------------------------------------------------------
void Grid::viewWasChanged(){
    snapCheck();
    checkLimits();
    // calculate new params?
    vector<int> params;
    if(snapped){
        params = calculateParamsFromCoord(snapCentre);
    }else{
        if(interpolateMode == NO_INTERPOLATION){
            params = calculateParamsFromCoord(centre);
        }else if(interpolateMode == INTERPOLATE_GRID){
            params = calculateInterpolatedParamsFromCoord(centre);
        }
    }
    for(int i = 0;i<2*paramsPerDim;i++){
        midiCC[i] = params[i];
    }

}

//--------------------------------------------------------------
void Grid::checkLimits(){
    // check for maximum zoom level
    

    if(size.x > maxValue*2.0){
        cout << "maxZoom\n";
        maxZoom = true;
        size.x = maxValue*2.0;
        // need to also set y size back to
    }
    if(size.y > maxValue*2.0){
        cout << "maxZoom\n";
        maxZoom = true;
        size.y = maxValue*2.0;
    }
    
    if(size.x < minValue){
        cout << "min Zoom\n";
        minZoom = true;
        size.x = minValue;
        // need to also set y size back to
    }
    if(size.y < minValue){
        minZoom = true;
        cout << "min Zoom\n";
        size.y = minValue;
    }
    
    scale = size.x/pixSize.x;
    size.y = scale * pixSize.y;
    
    topLeft.x = centre.x - size.x/2;
    topLeft.y = centre.y - size.y/2;
    // check for negatives
    
    // allow centre to be at limits
    if((topLeft.x + size.x*0.5) < 0.0){
        cout << "left Wall\n";
        topLeft.x = 0.0 - size.x*0.5;
        centre.x = 0.0;
    }

    if(topLeft.y + size.y*0.5 < 0.0) {
        cout << "top Wall\n";
        topLeft.y = 0.0 - size.y*0.5;
        centre.y = 0.0;
    }
    
   // does topleft refer to lines or view?
    
        // check max 
    if(topLeft.x + size.x/2 > maxValue){
        cout << "right Wall\n";
        topLeft.x = maxValue - size.x/2;
        centre.x = maxValue;
    }
    if(topLeft.y + size.y/2 > maxValue) {
        cout << "bottom Wall\n";
        topLeft.y = maxValue - size.y/2;
        centre.y = maxValue;
    }

}
//--------------------------------------------------------------
void Grid::checkConsistencies(){
    // debug function to check all the parameters are consistent maybe

}
//--------------------------------------------------------------
void Grid::setCoord(TwoVector coord){
    
    centre = coord;
    
    viewWasChanged();
}
//--------------------------------------------------------------
TwoVector Grid::getCoord(){
    // return read point crosshairs
    if(snapped){
        return snapCentre;
    }
    return centre;
}
//--------------------------------------------------------------
vector<int> Grid::getParams(){
    // return a vector with midiCCs in
    // should we store params somewhere and use this as a low computation get ?
    vector<int> params(2*paramsPerDim,0);
    //
    for(int i = 0;i<2*paramsPerDim;i++){
        params[i] = midiCC[i];
    }
    return params;
}
//--------------------------------------------------------------
void Grid::setParams(vector<int> params){
    // input midiCCs, and go to the appropriate coordinate

    for(int i = 0;i<2*paramsPerDim;i++){
        midiCC[i] = 64;
    }
    TwoVector coord = calculateCoordFromParams(params);
    setCoord(coord);
    
    // snapping?
    viewWasChanged();

}
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
#pragma mark const utils

TwoVector Grid::calculateCoordFromParams(vector<int> params){

    vector<int> ccValueX(paramsPerDim);
    vector<int> ccValueY(paramsPerDim);
    for(int i=0;i<paramsPerDim;i++){
        ccValueX[i] = params[i];
        ccValueY[i] = params[i+paramsPerDim];
    }
    
    TwoVector result;
    result.x = hilbert.calculateIndexFromParams(ccValueX);
    result.y = hilbert.calculateIndexFromParams(ccValueY);
    return result;
}

//--------------------------------------------------------------
vector<int> Grid::calculateParamsFromCoord(TwoVector coord){

    if (coord.x > maxValue || coord.y > maxValue){
        cout << "calculateParams Error: centre double value is too large" << "\n";
        vector<int> empty;
        return empty;
    }
    if (coord.x < 0 || coord.y < 0){
        cout << "calculateParams Error: centre double value is negative" << "\n";
        vector<int> empty;
        return empty;
    }

    // X
    vector<int> resultX;
    resultX = hilbert.calculateParamsFromIndex(coord.x);
    // Y
    vector<int> resultY;
    resultY = hilbert.calculateParamsFromIndex(coord.y);
    
    // concatenate
    vector<int> result = resultX;
    
    result.insert( result.end(), resultY.begin(), resultY.end() );
    return result;
}
//--------------------------------------------------------------
vector<int> Grid::calculateInterpolatedParamsFromCoord(TwoVector coord){

    vector<int> result;
    
    int interpLevelBigGrid = ceil(interpLevel);
    int interpLevelSmallGrid = floor(interpLevel);
    
    if(interpLevelBigGrid >= paramBitDepth){
        // maximum is bit-1 so don't do interp at all
        vector<int> pSmallGrid = interpHilbert(interpLevelSmallGrid, coord);
        return pSmallGrid;
    }
    double interpFrac = interpLevel - interpLevelSmallGrid;
    
    vector<int> pBigGrid = interpHilbert(interpLevelBigGrid, coord);
    vector<int> pSmallGrid = interpHilbert(interpLevelSmallGrid, coord);
// now we need to interpolate twixt these two aswell!
    
    result = interpVector(pBigGrid, pSmallGrid, interpFrac);
    
    return result;
}
//--------------------------------------------------------------
vector<int> Grid::interpHilbert(int bitlevel, TwoVector coord){
    vector<int> result;
    long long x = coord.x;
    long long y = coord.y;
    
    int roundDigits = (paramsPerDim*bitlevel);
    // knock off last digits
    x = x >> roundDigits;
    x = x << roundDigits;
    float frac = (coord.x - x)/(1 << roundDigits);
    long long xlower = x;
    long long xupper = x + (1 << roundDigits);
    
    vector<int> pupper = hilbert.calculateParamsFromIndex(xupper);
    vector<int> plower = hilbert.calculateParamsFromIndex(xlower);
    
    result = interpVector(pupper,plower,frac);
    // now Y
    y = y >> roundDigits;
    y = y << roundDigits;
    frac = (coord.y - y)/(1 << roundDigits);
    long long ylower = y;
    long long yupper = y + (1 << roundDigits);
    
    pupper = hilbert.calculateParamsFromIndex(yupper);
    plower = hilbert.calculateParamsFromIndex(ylower);
    
    vector<int> resultY = interpVector(pupper,plower,frac);
    // stickem together
    result.insert( result.end(), resultY.begin(), resultY.end() );
    
    return result;
}
//--------------------------------------------------------------
vector<int> Grid::interpVector(vector<int> upper, vector<int> lower, float frac){
    vector<int> result;
    if(upper.size() != lower.size()){
        cout << "Error: interpVector takes vectors of same length" << "\n";
        return result;
    }
    if(frac > 1){
        cout << "Error: bad fraction for vector interpolation" << "\n";
        return result;
    }
    int N = upper.size();
    for(int i=0; i<N; i++){
        result.push_back(frac*upper[i] + (1-frac)*lower[i]);
    }
    return result;
}
//--------------------------------------------------------------
vector<int> Grid::walkDiff(vector<bool> left, vector<bool> right){
    // horrible
    vector<int> result(2,0);

    int size = left.size();
    for(int i = 0; i < size; i++){
        if(left[i] && !right[i]){  // 1 - 0
            // dim
            result[0] = i;
            result[1] = 1;
        }else if(!left[i] && right[i]){ // 0 - 1
            result[0] = i;
            result[1] = -1;
        }else{ // equal do nothing
            
            
        }
    }
    
    return result;
}
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------


//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
#pragma mark The old algorithm

TwoVector Grid::calculateCoordFromParamsOld(vector<int> params) const{
    
    vector<int> ccValueX(paramsPerDim);
    vector<int> ccValueY(paramsPerDim);
    for(int i=0;i<paramsPerDim;i++){
        ccValueX[i] = params[i];
        ccValueY[i] = params[i+paramsPerDim];
    }
    vector<vector <bool> > codesX = midiToGray(ccValueX);
    vector<vector <bool> > codesY = midiToGray(ccValueY);
    
    vector<int> base32X = codesToBase32(codesX);
    vector<int> base32Y = codesToBase32(codesY);
    
    TwoVector result;
    result.x = base32toCoord(base32X);
    result.y = base32toCoord(base32Y);
    return result;
}

//--------------------------------------------------------------
vector<int> Grid::calculateParamsFromCoordOld(TwoVector coord) const{
    // some arrays in reverse order of power from normal numbers!   1,2,4,
    
    // centre to base 32
    if (coord.x > maxValue || coord.y > maxValue){
        cout << "calculateParams Error: centre double value is too large" << "\n";
        vector<int> empty;
        return empty;
    }
    
    //--------------------------
    // X
    vector<int> base32x = coordTobase32(coord.x); // 7 numbers from 0 to 31
    vector<vector <bool> > grayCodesX;
    
    int size = base32x.size();
    for(int i=0;i<size;i++){
        grayCodesX.push_back(intToGray(base32x[i]));
    }
    
    vector<int> result;
    result = grayToMidi(grayCodesX);
    //--------------------------
    // AND FOR Y
    vector<int> base32y = coordTobase32(coord.y);
    vector<vector <bool> > grayCodesY;
    
    size = base32y.size();
    for(int i=0;i<size;i++){
        grayCodesY.push_back(intToGray(base32y[i]));
    }
    
    vector<int> resultY;
    resultY = grayToMidi(grayCodesY);
    
    // concatenate
    result.insert( result.end(), resultY.begin(), resultY.end() );
    return result;
}
//-------------------------------------------------------------------

// for 1 dimension!!! i.e. call this twice
vector<int> Grid::grayToMidi(vector<vector <bool> > grayCodes) const{
    
    // gray to midi CC values
    // loop thru the scales to build up a CC number per dimension
    int midiCCresult[paramsPerDim]; // TODO dims specific
    for(int i=0;i<paramsPerDim;i++){
        midiCCresult[i] = 0;
    }
    
    int bin = 1;
    bin = bin << grayCodes.size()-1;
    
    int midP = 0;
    
    vector<vector <bool> >::iterator piter = grayCodes.begin();
    for(;piter < grayCodes.end();piter++){ // each lesser power of 2
        midP = 0; // reset
        vector<bool>::iterator diter = (*piter).begin();
        for(; diter <= (*piter).end();diter++){ // each one is different dimension
            int ig = int(*diter); // convert binary to int
            //cout << "ig: " << ig;
            midiCCresult[midP] += ig*bin; // mult by power of two
            midP++;
        }
        bin = bin >> 1; // next power of 2 down
        
    }
    
    // put in vector
    vector<int> result;
    for(int i=0;i<paramsPerDim;i++){
        result.push_back(midiCCresult[i]);
    }
    
    return result;
    
    
}


//--------------------------------------------------------------

vector<bool> Grid::intToGray(int num, int dimToTravel) const{
    
    // dimToTravel - this is the dimension that we want the walk to traverse, i.e. the last non zero digit of the code
    // so swap it for 3 to produce correct orientation
    
    // just use look up table... until it gets huuuge.
    vector<bool> grayCode = vcode[num];
    grayCode[3] = vcode[num][dimToTravel];
    grayCode[dimToTravel] = vcode[num][3];
    return grayCode;
}
//--------------------------------------------------------------
/*
 breaks down float into a base 32 number (2^D)
 each of these is converted to gray code
 this is then converted to 10 parameters, where each 32-digit becomes separate power of 2
 so "zoom" grid 32-patches are converted to 2-scales. 1,2,4,8,16,32,64,(128?) biggest number is 32^7
 */
vector<int> Grid::coordTobase32(double value) const{
    //double base = 32.0;
    if(value < 0.0){
        cout << "coordTobase32 error: input value is negative\n";
        value = 0.0;
    }else if(value > maxValue){
        cout << "coordTobase32 error: input value too big!\n";
    }
    double rem, divdr = 0.0;
    int digit32;
    
    // what power of 32 to start at?
    int maxpow = 7; // midi cc specific
    vector<int> result;
    
    rem = value;
    for(;maxpow >=0;maxpow--){
        // repeatedly get digit and remainder. This is exactly what we're doing in draw !?... could put all this in one place?
        divdr = pow((double)codeLength,(double)maxpow);
        digit32 = floor(rem/divdr);
        rem = rem - digit32*divdr;
        result.push_back(digit32);  // array, biggest index is smallest power
    }
    // at this point rem should be fractional... use for interp?
    
    return result;
}
//--------------------------------------------------------------

vector<int> Grid::codesToBase32(vector<vector<bool> > inCodes) const{
    vector<int> result;
    for(int i=0;i<paramBitDepth;i++){
        result.push_back(grayToInt(inCodes[i]));
    }
    return result;
}
//--------------------------------------------------------------

int Grid::grayToInt(vector<bool> incode) const{
    // look for match in table
    
    int s = vcode.size();
    
    for(int i=0; i<s;i++){ // fill vector
        if(vcode[i] == incode){
            return i;
        }
        
    }
    cout << "grayToInt error: no matching code found!";
    return -1;
}
//--------------------------------------------------------------
double Grid::base32toCoord(vector<int> base32Digs) const{
    // build up the big float from a base 32 number
    double result = 0.0;
    
    // what power of 32 to start at?
    int mpow = base32Digs.size() - 1; // should be 7...
    
    for(int p=0;p<=mpow;p++){ // biggest index is smallest power
        result += ((double)base32Digs[p]) * pow(32.0, (double)mpow-p);
        
    }
    return result;
    
}
//--------------------------------------------------------------
// for 1 dimension - takes in 5 cc params and outputs  7 codes
vector<vector <bool> > Grid::midiToGray(vector<int> ccValue) const{
    int pow2 = 1 << (paramBitDepth-1);
    
    vector<int> aCode(paramsPerDim);
    vector<vector<int> > theXCodes(paramBitDepth,aCode);
    
    // build up binary gray code representations from the bits of the midi ccs
    
    vector<vector<bool> > theCodes(paramBitDepth, vector<bool>(paramsPerDim));
    
    // x
    for(int p=0;p<paramBitDepth;p++){
        
        for(int i=0;i<paramsPerDim;i++){
            
            bool bit = (pow2 == (ccValue[i] & pow2));
            theCodes[p][i] = bit;
        }
        pow2 = pow2 >> 1;
        
    }
    return theCodes;
}

//--------------------------------------------------------------
void Grid::makeCode(){
    
    ////////////////////////////////~~~,,,,,,,,.........
    ////////// gray code generation
    //////
    ///
    // TODO 5bit specific
    // transition sequence.... what a palaver! only need to do once though.
    
    // max MRL
    int trans[] = {0,1,2,3,0,4,2,1,0,3,2,1,0,4,2,3,0,1,2,3,0,4,2,1,0,3,2,1,0,4,2,3};
    // balanced
    int transB[] = {1,2,3,4,5,1,5,2,3,5,2,4,2,3,4,1,4,3,2,3,1,5,3,4,1,5,2,5,3,4,1,3};
    for(int i = 0; i<codeLength; i++){
        transB[i] = transB[i] - 1;
    }
    
    int code[codeLength][paramsPerDim]; // start with normal array
    
    
    
    for(int j=0; j<paramsPerDim; j++){
        code[0][j] = 0;
    }
    
    for(int i=0; i < codeLength-1; i++){  // don't need last 3
        transSeq.push_back(trans[i]); // member vector
        for(int j=0; j<paramsPerDim; j++){
            
            if (j == abs(trans[i])){
                code[i+1][j] = !code[i][j];
            }else{
                code[i+1][j] = code[i][j];
            }
        }
        
    }
    
    for(int i=0; i < codeLength; i++){ // fill vector
        
        vcode.push_back(vector<bool>());
        
        for(int j=0; j<paramsPerDim; j++){
            vcode[i].push_back(code[i][j]);
        }
    }
    /*
     // now try with iterators to print...
     vector< vector<bool> >::iterator cit;
     //vector<bool>::iterator bit;
     vector<bool>::iterator bit;
     int i = 0;
     for(cit=vcode.begin(); cit!=vcode.end() ; cit++){ // fill vector
     i++;
     cout << i << " = ";
     bit = (*cit).begin();
     for(bit=(*cit).begin(); bit!=(*cit).end() ; bit++){
     
     cout  << *bit;
     }
     cout << "\n";
     
     }
     
     // codeToInt unit test
     vector<bool> aCode = vcode[12];
     int result = grayToInt(aCode);
     cout << "GRAY TO INT : " << result << "\n";
     
     cout << "-------------------------------\n";
     
     // base32toFloat unit test
     
     vector<int> test32 = coordTobase32(869437.0);
     double cresult = base32toCoord(test32);
     cout << "base32toCoord : " << cresult << "\n";
     
     cout << "-------------------------------\n";
     
     
     
     // midiToGray unit test
     
     vector<vector<bool> > incodes(paramBitDepth, vector<bool>(paramsPerDim));
     for(int i=0;i<7;i++){
     incodes[i] = vcode[i+5];
     }
     
     vector<int> midiTest;
     midiTest = grayToMidi(incodes);
     vector<vector<bool> > outcodes = midiToGray(midiTest);
     
     vector< vector<bool> >::iterator cit1;
     //vector<bool>::iterator bit;
     vector<bool>::iterator bit1;
     cout << "midiToGray Unit test\n";
     for(int i=0;i<paramBitDepth;i++){ // fill vector
     
     for(int j=0;j<paramsPerDim;j++){
     
     cout  << (incodes[i][j] == outcodes[i][j]) << ",";
     }
     cout << "\n";
     
     }
     cout << "-------------------------------\n";
     
     
     // whole process unit test(?)
     
     TwoVector inCoord(888999777,777666555);
     vector<int> checkParam;
     checkParam = calculateParamsFromCoord(inCoord);
     for(int i=0; i<2*paramsPerDim;i++){
     cout << checkParam[i] << "_";
     }
     TwoVector outCoord = calculateCoordFromParams(checkParam);
     cout << "Unit TEst coord = " << outCoord.x << "," << outCoord.y << "\n";
     */
    
}
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
