#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

    // This example is designed to show all of the different aligning
    // and scaling functions available in ofRectangle.
    //
    // Please run the example and use the keyboard to see the visual
    // results of each of the alignment and scaling operations.
    //
    // The basic premise is to define a subject and target rectangle.
    // The "subject" (on the left) rectangle will be aligned-to
    // and scaled-to the "target" (on the right) rectangle based
    // on the user's input.
    //
    // The resulting rectangle (workingSubjectRect in this case),
    // displayed in the center, is the result of the operations.
    //
    // An image is used to further emphasize the effect of various
    // aspect-ratio preservation modes.
    
    ofSetFrameRate(30);
    ofEnableAlphaBlending();

    isScaling = false;
    isAligning = true;
    isRectScaling = false;
    
    scaleMode       = OF_SCALEMODE_FIT;
    aspectRatioMode = OF_ASPECT_RATIO_KEEP;
    
    target_hAlign   = OF_ALIGN_HORZ_CENTER;
    target_vAlign   = OF_ALIGN_VERT_CENTER;
    
    subject_hAlign  = OF_ALIGN_HORZ_CENTER;
    subject_vAlign  = OF_ALIGN_VERT_CENTER;
    
    bUseImage = true;
    
    img.load("resolution_test_1080_mini.png");

    targetColor  = ofColor(255,0,0);
    subjectColor = ofColor(255,255,0);

    makeNewTarget();
    makeNewSubject();

}

//--------------------------------------------------------------
void ofApp::update(){
    
    workingSubjectRect = subjectRect;

    // read the results of our keyboard input to determine
    // the correct scaling and / or alignment operation.
    
    if(!isRectScaling) {
        if(isScaling) {
            if(isAligning) {
                workingSubjectRect.scaleTo(targetRect,
                                           aspectRatioMode,
                                           target_hAlign,
                                           target_vAlign,
                                           subject_hAlign,
                                           subject_vAlign);
            } else {
                workingSubjectRect.scaleTo(targetRect,
                                           aspectRatioMode);
            }
        } else {
            if(isAligning) {
                workingSubjectRect.alignTo(targetRect,
                                           target_hAlign,
                                           target_vAlign,
                                           subject_hAlign,
                                           subject_vAlign);
            } else {
                workingSubjectRect.alignTo(targetRect);
            }
        }
    } else {
        workingSubjectRect.scaleTo(targetRect,
                                   scaleMode);
    }
    
    
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackground(0);

    // draw original subject in lower left-hand corner
    if(!bUseImage) {
        ofFill(); ofSetColor(subjectColor, 80);
        ofDrawRectangle(10, ofGetHeight() - subjectRect.height - 10, subjectRect.width, subjectRect.height);
    } else {
        ofFill(); ofSetColor(255);
        img.draw(10, ofGetHeight() - subjectRect.height - 10, subjectRect.width, subjectRect.height);
    }
    // draw original subject frame in lower left-hand corner
    ofNoFill(); ofSetColor(subjectColor, 120);
    ofDrawRectangle(10, ofGetHeight() - subjectRect.height - 10, subjectRect.width, subjectRect.height);
    // draw original subject label
    ofSetColor(255);
    ofDrawBitmapStringHighlight("SUBJECT", 16, ofGetHeight() - 20);
    
    // draw original target in lower right-hand corner
    ofFill(); ofSetColor(targetColor, 80);
    ofDrawRectangle(ofGetWidth() - targetRect.width - 10, ofGetHeight() - targetRect.height - 10, targetRect.width, targetRect.height);
    
    // draw original target frame in lower right-hand corner
    ofNoFill(); ofSetColor(targetColor, 120);
    ofDrawRectangle(ofGetWidth() - targetRect.width - 10, ofGetHeight() - targetRect.height - 10, targetRect.width, targetRect.height);
    ofSetColor(255);
    ofDrawBitmapStringHighlight("TARGET", ofGetWidth() - 65, ofGetHeight() - 20);    
    
    // draw target rectangle in center
    drawAlignRect(targetRect,  targetColor,  target_hAlign,  target_vAlign, false);
    
    // draw aligned / scaled subject with respect to the target
    drawAlignRect(workingSubjectRect, subjectColor, subject_hAlign, subject_vAlign, bUseImage);

    // make the menu
    stringstream ss;
    
    ss << "Keys:" << endl;
    ss << "----------------------------------------------------------" << endl;
    ss << " New Subject / Target (space) " << endl;
    ss << " Use An Image Subject (i) = " << (bUseImage ? "YES" : "NO") << endl;
    ss << "----------------------------------------------------------" << endl;
    ss << "  Enable Custom Align (A) = " << (isAligning && !isRectScaling ? "YES" : "NO") << endl;
    ss << "  Subject ofAlignHorz (h) = " << (isAligning && !isRectScaling ? getHorzAlignString(subject_hAlign) : "-") << endl;
    ss << "    Model ofAlignHorz (H) = " << (isAligning && !isRectScaling ? getHorzAlignString(target_hAlign)  : "-") << endl;
    ss << "  Subject ofAlignVert (v) = " << (isAligning && !isRectScaling ? getVertAlignString(subject_vAlign) : "-") << endl;
    ss << "    Model ofAlignVert (V) = " << (isAligning && !isRectScaling ? getVertAlignString(target_vAlign)  : "-") << endl;
    ss << "----------------------------------------------------------" << endl;
    ss << "Enable Custom Scaling (S) = " << (isScaling && !isRectScaling ? "YES" : "NO") << endl;
    ss << "    ofAspectRatioMode (a) = " << (isScaling && !isRectScaling ? getAspectRatioModeString(aspectRatioMode) : "-") << endl;
    ss << "----------------------------------------------------------" << endl;
    ss << " Override Scale/Align (r) = " << (isRectScaling ? "YES" : "NO") << endl;
    ss << "          ofScaleMode (s) = " << (isRectScaling ? getScaleModeString(scaleMode) : "-") << endl;

    // draw the menu
    ofSetColor(255);
    ofDrawBitmapString(ss.str(), 10, 14);

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    
    // define keyboard interactions
    if(key == ' ') {
        makeNewTarget();
        makeNewSubject();
    } else if(key == 'h' && !isRectScaling && isAligning) {
        subject_hAlign = getNextHorzAlign(subject_hAlign);
    } else if(key == 'H' && !isRectScaling && isAligning) {
        target_hAlign = getNextHorzAlign(target_hAlign);
    } else if(key == 'v' && !isRectScaling && isAligning) {
        subject_vAlign = getNextVertAlign(subject_vAlign);
    } else if(key == 'V' && !isRectScaling && isAligning) {
        target_vAlign = getNextVertAlign(target_vAlign);
    } else if(key == 'a' && !isRectScaling && isScaling) {
        aspectRatioMode = getNextAspectRatioMode(aspectRatioMode);
    } else if(key == 's' && isRectScaling) {
        scaleMode = getNextScaleMode(scaleMode);
    } else if(key == 'S') {
        if(isRectScaling) {
            isScaling = true;
            isRectScaling = false;
        } else {
            isScaling = !isScaling;
        }
    } else if(key == 'r') {
        isRectScaling = !isRectScaling;
        if(isRectScaling) {
            isScaling = false;
            isAligning = false;
        }
    } else if(key == 'A') {
        if(isRectScaling) {
            isRectScaling = false;
            isAligning = true;
        } else {
            isAligning = !isAligning;
        }
    } else if(key == 'i') {
        bUseImage = !bUseImage;
        if(bUseImage) {
            makeNewSubject();
        }
    }
}

//--------------------------------------------------------------
void ofApp::makeNewSubject() {
    // if we are not using an image, make a random subject rectangle
    if(!bUseImage) {
        subjectRect.setFromCenter(ofGetWidth()  / 2.0f,
                                  ofGetHeight() / 2.0f,
                                  ofRandom(30.0f,300.0f),
                                  ofRandom(30.0f,300.0f));
    } else {
        // if we are using the image, then match the image size
        subjectRect.setFromCenter(ofGetWidth()  / 2.0f,
                                  ofGetHeight() / 2.0f,
                                  img.getWidth(),
                                  img.getHeight());
    }
    
    // copy the subject to the working subject so it can be modified.
    workingSubjectRect = subjectRect;
    
}

//--------------------------------------------------------------
void ofApp::makeNewTarget() {
    
    // create a random target rectangle aligned to the center of the screen
    targetRect.setFromCenter(ofGetWidth()  / 2.0f,
                             ofGetHeight() / 2.0f,
                             ofRandom(30.0f,300.0f),
                             ofRandom(30.0f,300.0f));
}

//--------------------------------------------------------------
void ofApp::drawAlignRect(const ofRectangle& rect,
                            const ofColor& color,
                            ofAlignHorz hAlign,
                            ofAlignVert vAlign,
                            bool drawImage) {
    
    // draw the rect -- draw the image if using an image
    ofFill();
    if(drawImage) {
        ofSetColor(255,127);
        img.draw(rect);
    } else {
        ofSetColor(color, 80);
        ofDrawRectangle(rect);
    }
    
    ofNoFill();
    ofSetColor(color, 120);
    ofDrawRectangle(rect);
    
    // draw the alignment marks if applicable
    if(isAligning && !isRectScaling) {
        drawHorzAlignMark(rect, color, hAlign);
        drawVertAlignMark(rect, color, vAlign);
    }
}

//--------------------------------------------------------------
void ofApp::drawHorzAlignMark(const ofRectangle& rect, const ofColor& color, ofAlignHorz hAlign) {
    if(hAlign != OF_ALIGN_HORZ_IGNORE) {
        float hAnchor = rect.getHorzAnchor(hAlign);
        ofSetColor(color,120);
        ofDrawLine(hAnchor, rect.getTop()    - 13, hAnchor, rect.getTop()    - 3);
        ofDrawLine(hAnchor, rect.getBottom() + 13, hAnchor, rect.getBottom() + 3);
    }
}

//--------------------------------------------------------------
void ofApp::drawVertAlignMark(const ofRectangle& rect, const ofColor& color, ofAlignVert vAlign) {
    if(vAlign != OF_ALIGN_VERT_IGNORE) {
        float vAnchor = rect.getVertAnchor(vAlign);
        ofSetColor(color,120);
        ofDrawLine(rect.getLeft()  - 13, vAnchor, rect.getLeft()  - 3, vAnchor);
        ofDrawLine(rect.getRight() + 13, vAnchor, rect.getRight() + 3, vAnchor);
    }
}

//--------------------------------------------------------------
ofScaleMode ofApp::getNextScaleMode(ofScaleMode mode) {
    if(mode == OF_SCALEMODE_FIT) {
        mode = OF_SCALEMODE_FILL;
    } else if(mode == OF_SCALEMODE_FILL) {
        mode = OF_SCALEMODE_CENTER;
    } else if(mode == OF_SCALEMODE_CENTER) {
        mode = OF_SCALEMODE_STRETCH_TO_FILL;
    } else if(mode == OF_SCALEMODE_STRETCH_TO_FILL) {
        mode = OF_SCALEMODE_FIT;
    }
    return mode;
}

//--------------------------------------------------------------
ofAspectRatioMode ofApp::getNextAspectRatioMode(ofAspectRatioMode mode) {
    if(mode == OF_ASPECT_RATIO_IGNORE) {
        mode = OF_ASPECT_RATIO_KEEP;
    } else if(mode == OF_ASPECT_RATIO_KEEP) {
        mode = OF_ASPECT_RATIO_KEEP_BY_EXPANDING;
    } else if(mode == OF_ASPECT_RATIO_KEEP_BY_EXPANDING) {
        mode = OF_ASPECT_RATIO_IGNORE;
    }
    return mode;
}


//--------------------------------------------------------------
ofAlignHorz ofApp::getNextHorzAlign(ofAlignHorz hAlign) {
    if(hAlign == OF_ALIGN_HORZ_LEFT) {
        hAlign = OF_ALIGN_HORZ_CENTER;
    } else if(hAlign == OF_ALIGN_HORZ_CENTER) {
        hAlign = OF_ALIGN_HORZ_RIGHT;
    } else if(hAlign == OF_ALIGN_HORZ_RIGHT) {
        hAlign = OF_ALIGN_HORZ_LEFT;
    }
    return hAlign;
}

//--------------------------------------------------------------
ofAlignVert ofApp::getNextVertAlign(ofAlignVert vAlign) {
    if(vAlign == OF_ALIGN_VERT_TOP) {
        vAlign = OF_ALIGN_VERT_CENTER;
    } else if(vAlign == OF_ALIGN_VERT_CENTER) {
        vAlign = OF_ALIGN_VERT_BOTTOM;
    } else if(vAlign == OF_ALIGN_VERT_BOTTOM) {
        vAlign = OF_ALIGN_VERT_TOP;
    }
    return vAlign;
}

//--------------------------------------------------------------
string ofApp::getHorzAlignString(ofAlignHorz hAlign) {
    switch (hAlign) {
        case OF_ALIGN_HORZ_LEFT:
            return "OF_ALIGN_HORZ_LEFT";
        case OF_ALIGN_HORZ_CENTER:
            return "OF_ALIGN_HORZ_CENTER";
        case OF_ALIGN_HORZ_RIGHT:
            return "OF_ALIGN_HORZ_RIGHT";
        case OF_ALIGN_HORZ_IGNORE:
            return "OF_ALIGN_HORZ_IGNORE";
        default:
        	ofLogError() << "Unknown ofAlignHorz: " << hAlign;
        	return "";
    }
}

//--------------------------------------------------------------
string ofApp::getVertAlignString(ofAlignVert vAlign) {
    switch (vAlign) {
        case OF_ALIGN_VERT_TOP:
            return "OF_ALIGN_VERT_TOP";
        case OF_ALIGN_VERT_CENTER:
            return "OF_ALIGN_VERT_CENTER";
        case OF_ALIGN_VERT_BOTTOM:
            return "OF_ALIGN_VERT_BOTTOM";
        case OF_ALIGN_VERT_IGNORE:
            return "OF_ALIGN_VERT_IGNORE";
        default:
        	ofLogError() << "Unknown ofAlignVert: " << vAlign;
        	return "";
    }
}

//--------------------------------------------------------------
string ofApp::getAspectRatioModeString(ofAspectRatioMode mode) {
    switch (mode) {
        case OF_ASPECT_RATIO_IGNORE:
            return "OF_ASPECT_RATIO_IGNORE";
        case OF_ASPECT_RATIO_KEEP:
            return "OF_ASPECT_RATIO_KEEP";
        case OF_ASPECT_RATIO_KEEP_BY_EXPANDING:
            return "OF_ASPECT_RATIO_KEEP_BY_EXPANDING";
        default:
        	ofLogError() << "Unknown ofAspectRatioMode: " << mode;
        	return "";
    }
}

//--------------------------------------------------------------
string ofApp::getScaleModeString(ofScaleMode mode) {
    switch (mode) {
        case OF_SCALEMODE_FIT:
            return "OF_SCALEMODE_FIT";
        case OF_SCALEMODE_FILL:
            return "OF_SCALEMODE_FILL";
        case OF_SCALEMODE_CENTER:
            return "OF_SCALEMODE_CENTER";
        case OF_SCALEMODE_STRETCH_TO_FILL:
            return "OF_SCALEMODE_STRETCH_TO_FILL";
        default:
        	ofLogError() << "Unknown ofScaleMode: " << mode;
        	return "";
    }
}