#include "SurfaceManager.h"

namespace ofx {
namespace piMapper {

SurfaceManager::SurfaceManager(){
	mediaServer = 0;
	selectedSurface = 0;
	
	// Create one SurfaceStack instance in the beginning as interphase towards full
	// preset functionality.
	
	SurfaceStack * preset = new SurfaceStack();
	_presets.push_back(preset);
	
	ofAddListener(
		_presets[0]->vertexChangedEvent,
		this,
		&SurfaceManager::onVertexChanged);
	ofAddListener(
		_presets[0]->verticesChangedEvent,
		this,
		&SurfaceManager::onVerticesChanged);
	_selectedVertexIndex = -1;
}

void SurfaceManager::draw(){
	_presets[0]->draw();
}

void SurfaceManager::addSurface(BaseSurface * surface){
	_presets[0]->push_back(surface);
}

void SurfaceManager::removeSelectedSurface(){
	if(selectedSurface == 0){
		return;
	}
	
	for(int i = 0; i < _presets[0]->size(); i++){
		if(_presets[0]->at(i) == selectedSurface){
			_presets[0]->erase(i);
			selectedSurface = 0;
			_selectedVertexIndex = -1;
			break;
		}
	}
}

void SurfaceManager::removeSurface(){
	if(_presets[0]->size() <= 0){
		return;
	}
    BaseSurface * s = _presets[0]->back();
	_presets[0]->pop_back();
    delete s;
}

void SurfaceManager::deleteSurface(ofx::piMapper::BaseSurface * surface){
	for(int i = 0; i < _presets[0]->size(); ++i){
		if(_presets[0]->at(i) == surface){
			_presets[0]->erase(i);
			break;
		}
	}
}

void SurfaceManager::clear(){
	while(_presets[0]->size()){
		delete _presets[0]->back();
		_presets[0]->pop_back();
	}
}

void SurfaceManager::saveXmlSettings(string fileName){
	if(mediaServer == 0){
		ofLogFatalError("SurfaceManager") << "Media server not set";
		exit(EXIT_FAILURE);
	}
	
	SettingsLoader::instance()->save(*_presets[0], fileName);
}

bool SurfaceManager::loadXmlSettings(string fileName){
	if(mediaServer == 0){
		ofLogFatalError("SurfaceManager") << "Media server not set";
		exit(EXIT_FAILURE);
	}
	
	return SettingsLoader::instance()->load(*_presets[0], *mediaServer, fileName);
}

void SurfaceManager::setMediaServer(MediaServer * newMediaServer){
	mediaServer = newMediaServer;
}

BaseSurface * SurfaceManager::selectSurface(int index){
	if(index >= _presets[0]->size()){
		throw runtime_error("Surface index out of bounds.");
	}

	selectedSurface = _presets[0]->at(index);
	_selectedVertexIndex = -1;
	ofSendMessage("surfaceSelected");
	return selectedSurface;
}

BaseSurface * SurfaceManager::selectSurface(BaseSurface * surface){
	cout << "SurfaceManager::selectSurface()" << endl;

	for(int i = 0; i < _presets[0]->size(); i++){
		if(_presets[0]->at(i) == surface){
			selectedSurface = surface;
			_selectedVertexIndex = -1;
			ofSendMessage("surfaceSelected");
			ofNotifyEvent(surfaceSelectedEvent, i, this);
			return selectedSurface;
		}
	}
	
	deselectSurface();
	return 0;
}

BaseSurface * SurfaceManager::selectNextSurface(){
	int next;
	_selectedVertexIndex = -1;
	
	if(selectedSurface == 0){
		next = 0;
		selectedSurface = selectSurface(next);
		ofNotifyEvent(surfaceSelectedEvent, next, this);
		return selectedSurface;
	}
	
	for(int i = 0; i < _presets[0]->size(); ++i){
		if(_presets[0]->at(i) == selectedSurface){
			if(i < _presets[0]->size() - 1){
				next = i + 1;
			}else{
				next = 0;
			}
			
			selectedSurface = _presets[0]->at(next);
			ofNotifyEvent(surfaceSelectedEvent, next, this);
			return selectedSurface;
		}
	}
	
	return 0;
}

BaseSurface * SurfaceManager::selectPrevSurface(){
	int prev;
	_selectedVertexIndex = -1;
	
	if(selectedSurface == 0){
		prev = _presets[0]->size() - 1;
		selectedSurface = selectSurface(prev);
		ofNotifyEvent(surfaceSelectedEvent, prev, this);
		return selectedSurface;
	}
	
	for(int i = 0; i < _presets[0]->size(); ++i){
		if(_presets[0]->at(i) == selectedSurface){
			if(i > 0){
				prev = i - 1;
			}else{
				prev = _presets[0]->size() - 1;
			}
			
			selectedSurface = _presets[0]->at(prev);
			ofNotifyEvent(surfaceSelectedEvent, prev, this);
			return selectedSurface;
		}
	}
	
	return 0;
}

BaseSurface * SurfaceManager::getSelectedSurface(){
	return selectedSurface;
}

// TODO: select vertex should be implemented ad BaseSurface level
void SurfaceManager::selectNextVertex(){
	if(selectedSurface == 0){
		return;
	}
	
	int numVertices = selectedSurface->getVertices().size();
	
	if(_selectedVertexIndex == -1){
		_selectedVertexIndex = 0;
	}else if(_selectedVertexIndex < numVertices - 1){
		_selectedVertexIndex += 1;
	}else{
		_selectedVertexIndex = 0;
	}
	
	ofNotifyEvent(vertexSelectedEvent, _selectedVertexIndex, this);
}

// TODO: select vertex should be implemented at BaseSurface level
void SurfaceManager::selectPrevVertex(){
	if(selectedSurface == 0){
		return;
	}
	
	int numVertices = selectedSurface->getVertices().size();
	
	if(_selectedVertexIndex > 0){
		_selectedVertexIndex -= 1;
	}else{
		_selectedVertexIndex = numVertices - 1;
	}
	
	ofNotifyEvent(vertexSelectedEvent, _selectedVertexIndex, this);
}

void SurfaceManager::selectVertex(int i){
	if(selectedSurface == 0){
		return;
	}
	
	if(i > selectedSurface->getVertices().size() - 1){
		ofLogError("SurfaceManager::selectVertex") << "index (" << i << ") out of bounds" << endl;
		int prevVertIndex = _selectedVertexIndex;
		ofNotifyEvent(vertexUnselectedEvent, prevVertIndex, this);
		_selectedVertexIndex = -1;
		return;
	}
	
	_selectedVertexIndex = i;
	ofNotifyEvent(vertexSelectedEvent, _selectedVertexIndex, this);
}

void SurfaceManager::moveSelectionBy(ofVec2f v){
	if(selectedSurface == 0){
		moveAllSurfacesBy(v);
		return;
	}
	
	if(_selectedVertexIndex != -1){
		selectedSurface->getVertices()[_selectedVertexIndex] += v;
		ofNotifyEvent(vertexChangedEvent, _selectedVertexIndex, this);
	}else{
		selectedSurface->moveBy(v);
	}
	
	// TODO: instead of having single selected surface
	// we want to have a selection of one or more.
	// it could be implemented as vector here.
}

void SurfaceManager::moveAllSurfacesBy(ofVec2f v){
	for(int i = 0; i < _presets[0]->size(); ++i){
		_presets[0]->at(i)->moveBy(v);
	}
}

void SurfaceManager::deselectSurface(){
	selectedSurface = 0;
	_selectedVertexIndex = -1;
}

BaseSurface * SurfaceManager::getSurface(int index){
	if(index >= _presets[0]->size()){
		throw runtime_error("Surface index out of bounds.");
		return 0;
	}
	
	return _presets[0]->at(index);
}

int SurfaceManager::size(){
	return _presets[0]->size();
}

int SurfaceManager::getSelectedVertexIndex(){
	return _selectedVertexIndex;
}

void SurfaceManager::onVertexChanged(int & i){
	ofNotifyEvent(vertexChangedEvent, i, this);
}

void SurfaceManager::onVerticesChanged(vector<ofVec3f> & vertices){
	ofNotifyEvent(verticesChangedEvent, vertices, this);
}

SurfaceStack * SurfaceManager::getActivePreset(){
	return _presets[0];
}

} // namespace piMapper
} // namespace ofx